下面的代码输出什么?
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
参考答案:
// 输出
NSStringFromClass([self class]) = Son
NSStringFromClass([super class]) = Son
参考解释:
这个题目主要是考察关于Objective-C中对self和super的理解。我们都知道:self是类的隐藏参数,指向当前调用方法的这个类的实例。那super呢?
很多人会想当然的认为“super和self类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实 super是一个 Magic Keyword,它本质是一个编译器标示符,和self 是指向的同一个消息接受者!他们两个的不同点在于:super会告诉编译器,调用class 这个方法时,要去父类的方法,而不是本类里的。
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。
当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法。
图片很大,需要分3次下载,然后合并才能用,应该怎么做?
参考答案:
通过dispatch_group_t来实现,将每部分图片下载请求放入到Group中,将合并图片的操作放在dispatch_group_notify中实现。
参考代码:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*下载图片Part1 */ });
dispatch_group_async(group, queue, ^{ /*下载图片Part2 */ });
dispatch_group_async(group, queue, ^{ /*下载图片Part3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并图片
});
参考解释:
本题是多线程的一种应用场景,能说出做法就算过关;如果能用代码表达,说明做过,经验丰富。也可以追问,上面的queue是串行队列还是并行队列?dispatch_get_main_queue()是串行队列还是并行队列?两者有什么区别?分别适用什么场景?
下面的代码存在什么问题?原因是什么?怎么解决?
@property (nonatomic, strong) HttpRequestHandler *handler;
@property (nonatomic, strong) NSData *data;
self.handler = [httpRequestHandler sharedManager];
[self.handle downloadData:^(id responseData) {
self.data = responseData;
}];
参考答案:
循环引用
self强引用handler,handler强引用block,block强引用self,形成循环
采用weak self的方法解决
参考代码:
@property (nonatomic, strong) HttpRequestHandler *handler;
@property (nonatomic, strong) NSData *data;
self.handler = [httpRequestHandler sharedManager];
__weak typedof(self) weakSelf = self;
[self.handle downloadData:^(id responseData) {
__strong typeof(weakSelf)strongSelf = weakSelf;
strongSelf.data = responseData;
}];
参考解释:
- block的引用循环问题在实际开发过程中比较常见。能找出这个问题,就算过关。
- 能说出具体原因,并能从代码上消除引用循环,那就更好了。
- 直接用weakSelf也是可以的,有strongSelf配合更好。
- 如果能够回答出weakSelf是为了解决引用循环,strongSelf是为了防止事情没做完提前释放,更好
- 如果知道ReactiveCocoa这个第三方库提供了一种更优雅的做法,那就更好
@property (nonatomic, strong) HttpRequestHandler *handler;
@property (nonatomic, strong) NSData *data;
self.handler = [httpRequestHandler sharedManager];
@weakify(self)
[self.handle downloadData:^(id responseData) {
@strongify(self)
strongSelf.data = responseData;
}];
以下代码在主线程调用,结果是什么?
NSLog(@"开始");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"进行中");
});
NSLog(@"结束");
参考答案:
只会打印第一句:开始,然后主线程就卡死了。
参考解释:
同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。那么这里的步骤就是:打印完第一句后,dispatch_sync
立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue
中,可是 main_queue
中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,dispatch_sync
就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。
有3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。如何实现?
参考答案:
使用NSOperation的addDependency方法
参考代码:
//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片");
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印");
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片");
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
参考解释:
能够设置任务间的依赖,是NSOperation的特色,比较适合这个场景。当然,使用dispatch的串行队列也可以达到同样效果。
能想到这个方法就算过关,能写出代码,说明经验丰富。
SDWebImage原理是什么?或者说缓存的原理是什么?
参考答案:
- 从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;
- 从沙盒中找,找到直接使用,缓存到内存。
- 从网络上获取,使用,缓存到内存,缓存到沙盒。
如何以UIWebView显示www.apple.com网页的内容?
参考答案:
- 新建UIWebView,添加到view中;
- 写好地址为”http://www.apple.com“的NSURLRequest;
- webview 设置好delegate,调用加载方法,加载该NSURLRequest。在delegate方法中检查加载状态
参考代码:
UIWebView *webView = [[UIWebView alloc]initWithFrame:self.view.frame];
[self.view addSubview:webView];
NSString *urlStr = @"http://www.apple.com";
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:urlStr]];
[webView loadRequest:request];
参考解释:
只要能说出做法就算过关,能写出代码更好。UIWebView的基本用法,目前在Hybrid架构下,H5的比重越来越高。
NSString *obj = [[NSData alloc]init] ,obj在编译时和运行时分别是什么类型的对象?
参考答案:
编译时是NSString, 运行时是NSSData的一个实例
参考解释:
Object-C是动态语言,编译时和运行时类型可以不一样。不过平时不要这样用。平时用的多的是id,可以代表任何继承自NSObject的类,也涉及到强制转换。
根据下面代码片段,回答相应问题
NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
- copyArray与array的地址是否一样?
- mCopyArray和array的地址又是否一样?
- 改变mCopyArray里面的元素内容,是否会刻变array的内容?
- copy和mutableCopy的拷贝操作有何不同?
参考答案:
- 一样;
- 不一样;
- 不改变
- copy浅拷贝,只是指针的复制,而内容未复制;
mutableCopy是深拷贝,复制内容,新分配一段内存;
网友Coulson_Wang的说法是正确的,对于不可变对象,copy也是深拷贝。
这一点,平时也用到。一般返回值是不可变对象,但是具体做事的时候是可变对象,返回的时候,用copy函数就好了,这是一种习惯。
比如,连接两个数组的函数:
- (nonnull NSArray *)concatArray1:(nonnull NSArray *)array1 andArray2:(nonnull NSArray *)array2 {
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array1];
for (id object in array2) {
[mutableArray addObject:object];
}
return [mutableArray copy]; // 这是习惯用法,返回一个不可变的深拷贝给外部调用者
}
按照下面这样的例子使用:
NSArray *array = [self concatArray1:@[@8,@7,@8] andArray2:@[@"a", @"c",@"q"]];
NSLog(@"%@", array);
输出的结果,数组成员类型是NSObject(NSNumber和NSString混合),可以看调试截图:
8,7,8,a,c,q
根据下面的代码,回答相应的问题:
IQuery.h
@protocol IQuery
@required
- (void)query:(NSString*) sql;
@optional
- (void)helloWorld;
@end
DBQuery.h
#import <Foundation/Foundation.h>
#import "IQuery.h"
@interface DBQuery : NSObject <IQuery>
- (void)test:(id<IQuery>) obj;
@end
- @required 表示什么意思?
- @optional 表示什么意思?
- “@interface DBQuery : NSObject <IQuery> ”中的<IQuery>表示什么意思?
- “- (void)test:(id<IQuery>) obj;”中的id<IQuery>表示什么意思?
参考答案:
- 必须实现的方法
- 可选实现的方法
- DBQuery遵循IQuery协议,会根据要求实现IQuery协议中的方法。其中- (void)query:(NSString*) sql; 必须实现;- (void)helloWorld; 根据实际情况,可以实现,也可以不实现。
- id表示obj是实现了IQuery协议的任意类型
参考解释:
id在实际中用得还是非常多的,比如代理模式中的delegate一般都是id类型,是Object-C动态特性的一种应用。面向协议的编程也非常普遍。协议还可以表示一种类型,用在函数参数中。
根据下面的代码回答问题
NSTimer *myTimer = [NSTimer timerWithTimeInterval:3.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
- 在滑动时,定时器会怎么样?怎么改进?
- [myTimer invalidate]; 是什么意思?
- [myTimer setFireDate:[NSDate distantFuture]]; 是什么意思?
- [myTimer setFireDate:[NSDate distantPast]]; 是什么意思?
参考答案:
- 滑动时定时器会停止;将定时器的模式改为NSRunLoopCommonModes就可以让定时器在滑动时正常触发。
- 取消定时器 ,是永久的停止,移除定时器对象
- 关闭定时器
- 开启定时器
参考解释:
定时器的使用还是比较常用的,苹果给的API名字不够人性化,容易造成一定的误解。