一说起循环引用, 首先肯定想到的是 block代理,解决方法就是使用 weak 来弱引用.

#####什么是循环引用?
有两个对象 A 和 B, A 强引用 B, 并且 B 强引用 A, 这就是循环引用, 因为两个对象互相强引用, 所以两个对象都不会被释放.

block 的循环引用请看: block 循环引用, __strong、__weak、__block使用规则

#####无法直接通过 weak 来解决循环引用的情况

1
2
3
4
5
6
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(selector)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- (void)dealloc {
[self.displayLink invalidate];
}

上面这段代码, 我希望在 dealloc 的时候将 CADisplayLink 对象释放掉, 但是 CADisplayLinkdisplayLinkWithTarget 方法中, 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 就可以了,
实现很简单, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 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;
}

使用

1
2
3
4
5
6
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 的消息转发