定时器
有NSTimer、CADisplayLink、GCD、NSThread(performSelector:afterDelay:)等。
NSTimer:
一般情况下准确,但是当runloop中的耗时操作过多,或者runloopmode切换,就会出现误差,mode切换导致的暂停可以通过把加入的mode改为NSCommonMode来消除,
CADisplayLink:
基于屏幕刷新的周期,一般很准时,每秒刷新60次。本质也是通过runloop 所以runloop选择其他模式或者耗时操作过多时,仍会造成延迟。因为只在屏幕刷新时调用,所以对于需要实时更新的UI (比如进度条)来说,使用CADisplayLink能优化性能,可以减少无谓的计算。
GCD
GCD定时器使用了dispatch source,监听系统内核对象并处理。因为直接调用系统方法,所以更加精准,但是缺点时会内存消耗更大
定时器内存泄漏
问题1: NSTimer无法释放
问题2: 控制器无法释放
解决方案:再把timer设置为nil之前,使用invalidate 。因为invalidate方法会将定时器从RunLoop中移除,同时解除对target等对象的强引用。在dealloc中调用无效,在viewcontroller生命周期中调用更合适。CADisplayLink同理,而GCD定时器则使用dispatch_suspend()。
ps :生命周期调用:这种情况是可以解决循环引用的问题,内存可以释放,但是又会引来新的问题,当导航控制器push到下一个页面时,当前VC并没有被释放,这时候我们可能并不想销毁NSTimer,我们通常希望VC该销毁的时候,同时销毁NSTimer,所以调用invalidate方法的时机很难找。所以最好的解决方式是使用nsproxy。
消息转发机制
objc_msgSend 有三个阶段 消息发送(当前类,父类中查找)、动态方法解析、消息转发
消息发送:
在当前类的方法缓存中查找方法,如果没找到,在类的方法列表中找,还没找到就在父类的缓存,父类的方法列表中查找,如果以上任何一步找到了,都加入当前类的缓存中。没找到就进入动态方法解析阶段。
动态方法解析:
查看是否有过动态解析 如果有直接消息转发,否则调用+resolveInstanceMethod:/+resolveClassMethod:动态添加方法实现,并标记为已动态解析,重走消息发送流程。
消息转发
1.先调用 forwardingTargetForSelector 方法获取新的 target 作为 receiver 重新执行 selector,如果返回值不用空,走消息发送流程。如果返回的内容不合法(为 nil 或者跟旧 receiver 一样),那就进入第二步。
2.调用 methodSignatureForSelector 获取方法签名后,判断返回类型信息是否正确,再调用 forwardInvocation 执行 NSInvocation 对象,并将结果返回。如果对象没实现 methodSignatureForSelector 方法,进入第三步。
3.调用 doesNotRecognizeSelector 方法
NSMethodSignature
一个NSMethodSignature对象记录着某个方法的返回值类型信息以及参数类型信息
网友评论