这是一个项目,展示了如何使用 Objective-C 的消息传递功能来防止与 NSTimer
、NSThread
或 CADisplayLink
实例交互时产生的保留循环。
如您已知,NSTimer
、NSThread
和 CADisplayLink
实例都保留其目标。如果在目标中保留了这些类的一个实例,我们就有一个保留循环:目标和实例将永远无法释放。
为什么你想要保留这些类之一的实例呢?想象一下你有一个特定的视图动画,需要使用 CADisplayLink
实例。一旦动画完成,就不需要 CADisplayLink
实例了,应该暂停以避免您的应用浪费资源。为了暂停它,您将不得不保留一个对它的引用。
这时,您可能会 wonder:为什么不保留一个弱引用而不是一个强引用?这难道不能解决我们所有的问题吗?答案是 no,因为它没有改变目标被保留的事实。例如,一个具有对重复的 NSTimer
对象弱引用的 UIViewController
实例将永远不会被释放,因为它是被计时器保留的。
它使您能够创建 NSTimer
、NSThread
和 CADisplayLink
对象,而不必担心保留循环。
内部,一个 RDRIntermediateTarget
对象保留了对实际目标(例如您的 UIViewController
)的弱引用。它将所有由 NSTimer
、NSThread
或 CADisplayLink
发起的调用转发到实际目标。
请注意,RDRIntermediateTarget
不会被保留!查看示例项目以获取更多信息。
RDRIntermediateTarget *target =
[RDRIntermediateTarget intermediateTargetWithTarget:self];
self.timer = [NSTimer timerWithTimeInterval:1.0f
target:target
selector:@selector(_timerFired:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
该示例项目包含一个名为ViewController
的单个UIViewController
子类,其中包含一个计时器、一个开关、一个标签和一个按钮。计时器是重复的,充当计数器 - 每次滴答都会增加一个整数,并将其随后显示在标签上。开关允许您在默认实现和使用RDRIntermediateTarget
的实现之间切换。当开关处于开启状态时,后一种情况成立。单击按钮会导致应用程序重置应用程序窗口的rootViewController
,这是一个ViewController
的实例。如果rootViewController
成功释放,您将在控制台注意到一条“DEALLOC”消息。如果有引用循环,则不会释放,因此不会有任何日志记录。
在ViewController
中,您可以更改timer
属性的strong
关键字为weak
,以便亲自查看此更改并没有造成差异。
代码采用MIT许可证。请参阅LICENSE
文件获取更多详情。