一说起循环引用, 首先肯定想到的是 block
和 代理
,解决方法就是使用 weak
来弱引用.
什么是循环引用?
有两个对象 A 和 B, A 强引用 B, 并且 B 强引用 A, 这就是循环引用, 因为两个对象互相强引用, 所以两个对象都不会被释放.
block 的循环引用请看: block 循环引用, __strong、__weak、__block使用规则
无法直接通过 weak
来解决循环引用的情况
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(selector)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- (void)dealloc {
[self.displayLink invalidate];
}
上面这段代码, 我希望在 dealloc 的时候将 CADisplayLink
对象释放掉, 但是 CADisplayLink
在 displayLinkWithTarget
方法中, CADisplayLink
对象会强引用 self
, 如此看来, self
和 self.displayLink 已经出现循环引用, 那么如何在不主动执行 [self.displayLink invalidate];
的前提下将 self
释放掉?
首先我们都会想到使用 __weak
来实现, 但是, 在 CADisplayLink
内部被强引用, 怎么实现这个 weak
?
既然两个对象无法解决, 我们就加一个对象, 或者更多, 只要, 这个闭环中有一个对象是 weak
的, 那么循环引用就不成立了, 那么怎么加这个对象那, 在之前的文章(objective-c 的消息转发)中, 提到过消息转发.
我们现在新加一个类叫做 WeakTarget
, 让它的事例去持有 weak 的 self
, 让 self.displayLink
持有 WeakTarget
的事例, ok, self
本身是持有 self.displayLink
的, 这样就不是一个闭环了, 如下图.
现在, 只要实现 WeakTarget
将消息转发给 self 就可以了,
实现很简单, 代码如下:
// class WeakTarget
@property (nonatomic, weak) id target;
- (instancetype)initWithTarget:(id)target {
self = [super init];
if (self) {
self.target = target;
}
return self;
}
- (id)forwardingTargetForSelector:(SEL)selector {
return self.target;
}
使用
self.displayLink = [CADisplayLink displayLinkWithTarget:[[WeakTarget alloc] initWithTarget:self] selector:@selector(selector)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- (void)dealloc {
[self.displayLink invalidate];
}
消息转发的具体细节请看objective-c 的消息转发