面试题:使用CADisplayLink
、NSTimer
有什么注意点(一般指内存管理方面)?
一、CADisplayLink、NSTimer
NSTimer
是可以设置时间间隔的,自动添加到RunLoop中。而CADisplayLink
默认情况下是保证调用频率和屏幕的刷帧频率一致(60FPS),并且CADisplayLink
必须手动添加到RunLoop中,否则不会工作。
问题:CADisplayLink
、NSTimer
会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用。
解决方案:使用block 或 使用代理对象。
使用block:(仅限NSTimer
,因为CADisplayLink
不支持block)
1 | __weak typeof(self) weakSelf = self; |
使用代理对象:
1 | @interface DBProxy : NSObject |
使用代理对象时,也可以使用NSProxy
。
NSProxy和NSObject一样,都是基类(没有继承父类)。
1 | @interface NSProxy <NSObject> { |
使用NSProxy
:
1 | @interface DBProxy : NSProxy |
NSProxy
没有forwardingTargetForSelector:
方法,只有methodSignatureForSelector:
和forwardInvocation:
,所以需要重写这两个方法让消息转发给传入的target。如果没有重写这两个方法,就会报错:-[NSProxy methodSignatureForSelector:] called!
。
假设NSProxy
的target是控制器对象,[proxy isKindOfClass:self]
的执行结果是1,因为NSProxy内部没有isKindOfClass
方法(遵守了NSObject协议,所以可以调用该方法),最终会进入消息转发阶段,所以这句代码的本质就是[target isKindOfClass:self]
。
如果使用NSProxy
,方法找不到时会直接进入到消息转发阶段。而使用自定义代理对象(继承NSObject
),会通过isa指针查找类对象,再到类方法列表/缓存列表查找方法,如果找不到再进入动态解析阶段,最后才进入消息转发阶段。所以从这一层面也可以看出,**NSProxy
的效率更高,它是专门用来做消息转发的**。
二、GCD定时器
NSTimer
依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时。而GCD的定时器会更加准时,因为GCD不依赖RunLoop。
1 | dispatch_queue_t queue = dispatch_queue_create("timer", DISPATCH_QUEUE_SERIAL); |