以下所有内容属笔者原创, 如有雷同纯属巧合, 未经允许不得转载.
这篇内容主要讲解 定时器
中的循环引用, 常见的循环引用和 block
中的循环引用, 解决起来也相对简单, 这里没有介绍, 笔者会在后续更新中陆续补充完整
CDDisplayLink
和 NSTimer
使用 target
的时候, 容易产生循环引用
-
self
强引用定时器对象
-
定时器对象
通过target
强引用self
self对象
-> 定时器对象
-> target
-> self对象
, 从而形成循环引用
解决循环引用的方法:
1. 使用block执行代码块
使用 blcok
的形式来执行代码块, 这样定时器对象就不会强引用 self对象
, 这样问题就转化为 block
中的循环引用问题, 可以很方便的解决
2. 加入第三方代理
所有类似的问题, 都可以通过引入一个第三方对象来解决
具体做法(下边用一个 viewController
来指代 self对象
):
-
viewController
强引用定时器对象
-
定时器对象
通过target
强引用新增的第三对象
-
第三对象
弱引用viewController
这样虽然三者之间还是 '循环引用'
, 但是因为 第三对象
弱引用了 viewController
, 所以这个循环引用是被打破了的, 不会存在问题
注意
为了保持代码的整体性, 我们同样不会把定时器方法放到 第三对象
中来实现, 依然在 viewController
中实现, 这样我们只需要在 第三对象
中使用 消息转发
就能轻松调用到 viewController
中的定时器方法
更推荐的做法
OC
中并不是所有的类都是继承自 NSObject
, 其中有一个类叫做 NSProxy
, 他就是专门用来进行消息转发的, 他在执行消息转发的过程中, 效率相对普通 NSObject
对象要高一些
原因是:
不同于普通的 NSObject
对象的方法查找, NSProxy
在当前类的缓存和方法列表中查找方法未果以后, 不会再一次向上, 从他的父类中查找, 而是直接开启消息转发, 因为缺省了这个步骤, 所以他做消息转发的效率会高很多
下面是以上内容的部分代码实现:
HLProxy.h文件部分实现
@interface HLProxy : NSProxy
@property (weak, nonatomic) id target;
+ (instancetype)proxyWith:(id)target;
@end
HLProxy.m文件部分实现
+ (instancetype)proxyWith:(id)target {
HLProxy *proxy = [HLProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
viewController.m中部分实现
HLProxy *proxy = [HLProxy proxyWith:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:proxy selector:@selector(__timerTest) userInfo:nil repeats:YES];