IOS解决循环引用

2017-02-13  本文已影响0人  传火的余烬

虽然目前IOS都普遍使用了ARC开发,但是还是有一些情况下是必然存在循环引用的,为了更好的说明循环引用。先以MRC的代码来描述一些内存关系。

@interface ViewController ()

@end

@implementation ViewController

@end

这里我们看到 AViewController *av 的引用计数是1, 以下是 AViewController 的代码

@implementation AViewController

正常逻辑下 当我们 调用 back 的时候 这个时候 AViewController 会因为 dismissViewControllerAnimated 使得引用计数变成0,然后完成正常的内存释放。

这个时候我们入一个 BClass 先看 BClass的定义
@protocol BClassDelegate <NSObject>

注意 delegate 是 retain 关键点来了。这种设计摆明了跟其他 委托模式不一样(参考UITableView 的 delegate 是assign[因为目前是MRC 所以不是weak])。

这个时候 在 AViewController 使用 BClass。
@interface AViewController ()
@property (nonatomic, retain) BClass *animation;
@end

为了降低复杂度 我们先只考虑AViewController 的内存释放,先暂时放下 animation的内存释放问题。
这里重点情况是 _animation.delegate = self; 这里会使得AViewController 引用计数变成2。
看下 BClass 源码

@implementation BClass

关键点是因为 delegate 设计成 retain类型,所以 _animation.delegate = self; 这句代码使得 AViewController 变成了2,
这个时候 dismissViewControllerAnimated 引用计数减1, 但是 AViewController 引用计数没有变成0,所以AViewController内存没有释放,这就造成内存泄漏。到这里给出完整BClass的代码

@interface AViewController ()
@property (nonatomic, retain) BClass *animation;
@end

@implementation AViewController

这里因为 back的时候 我们没有办法使得AViewController引用计数变成0,所以没有调用AViewController的dealloc,所以无法
触发 [_animation release]; 进而使得 BClass也没有被释放内存。这里就是循环引用了。
解决方案可以这样考虑,只要在调用back之前我们能够使得 AViewController的引用计数由2变成1就可以了。
这个时候改写back

@implementation TestProxy

@end

这里可以先忽略 methodSignatureForSelector 和 forwardInvocation 先重点放在内存泄漏上。
引入TestProxy后,改写AViewController里面的userBClass和dealloc,back

重点是 _animation.delegate = _proxy 这里并没有引起AViewController 引用计数的改变,所以 back的时候 AViewController能正常释放内存。所以 back触发的时候 会进入 dealloc 这个时候 [_animation release]; 会使得_animation引用计数变成了0。
到这里我们能够正常释放了 AViewController和BClass了。剩下只有能解决NSProxy正常释放那么就能够解决所有的内存泄漏问题了。 目前 proxy引用计数是2,参考代码可以知道 AViewController 的 dealloc 有一次 [_proxy release], BClass里面有一个
[_delegate release], BClass里面的delegate就是_proxy 所以 TestProxy的引用计数变成0,最终 AViewController, BClass 和 TestProxy 出现的3个类都成功完成内存释放。

对比原来非常粗暴的back函数里面调用release来解决内存问题,另一种通过引入 TestProxy 把释放内存的时机放到了 AViewController 的 dealloc。一般情况建议大家这样引用TestProxy, 因为这样写法不需要考虑内存释放的时机的。同理NSTimer这种也可以引入Proxy,然后在 dealloc 调用 invalidate即可。

总结:循环引用计数的关键点在于 setDelegate方法, aaa.delegate = self; aaa.delegate = nil 。

上一篇下一篇

猜你喜欢

热点阅读