iOS 中控制器释放问题

2015-10-15 09:15:47 +08:00
 tunnyios
![]( http://7xke07.com1.z0.glb.clouddn.com/2015-10-14-iOS-ViewControllerDealloc_blockCrash.JPG)

##**iOS 中控制器的释放问题**

ARC 工程是可以重写 dealloc 方法并被系统调用的,但不需要手动调用父类的 dealloc ,手写[super dealloc]方法会报错,事实上系统会自动帮你调用父类的 dealloc 方法,不需要你实现。可以通过在 dealloc 方法中打印 log 查看控制器是否被释放。

控制器在被 pop 后移出栈后会被释放,但有些时候会发现控制器出栈的时候不会调用 dealloc 方法,归根结底,是因为当前控制器被某个对象强引用了,控制器的引用计数不为 0 ,系统无法帮你释放这部分内存。

<!--more-->

###**控制器中 NSTimer 没有被销毁**

当控制器中存在 NSTimer 时,就需要注意,因为当**[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];**时,这个 /*target:self*/ 就增加了 VC 的 RetarnCountr, 如果你不将这个 timer invalidate ,就别想调用 dealloc 。需要在 viewWillDisappear 之前需要把控制器用到的 NSTimer 销毁。

- [timer invalidate]; // 销毁 timer
- timer = nil; // 置 nil

###**控制器中的代理不是 weak 属性**

例如**@property (nonatomic, weak) id<HCAppViewDelegate> delegate;**代理要使用弱引用,因为自定义控件是加载在视图控制器中的,视图控制器 view 对自定义控件是强引用,
如果代理属性设置为 strong ,则意味着 delegate 对视图控制器也进行了强引用,会造成循环引用。导致控制器无法被释放,最终导致内存泄漏。

###**控制器中 block 的循环引用**

block 会把它里面的所有对象强引用(在 ARC 下)/*PS:MRC 下会 retain 加 1*/,包括当前控制器 self ,因此有可能会出现循环引用的问题。
即一个对象有一个 Block 属性,然而这个 Block 属性中又引用了对象的其他成员变量,那么就会对这个变量本身产生强应用,那么这个对象本身和他自己的 Block 属性就形成了循环引用。在 ARC 下需要修改成这样:(/*也就是生成一个对自身对象的弱引用*/)

- __weak typeof(self) weakSelf = self;

**即:保险起见 block 中所有的涉及到 self 的全给替换成 weakSelf**

http://tunnyios.github.io/
3531 次点击
所在节点    iDev
10 条回复
chisj
2015-10-15 09:59:13 +08:00
关于 循环引用:
1 :不是所有 block 的 self 都要替换成 weakSelf ,被 controller 本身 retain 的 block (或者 block 的 copy )才需要引用 weakSelf 。
2 :不仅仅是 self , A 对象 retain 了 block , block 又 retain 了 A ,则 A 也会被循环引用,同理,可用 weakA 解决。

所以:保险起见 block 中所有的涉及到 self 的全给替换成 weakSelf ->是不必要的做法,也是不明就里的做法,关注的点不应该是所有 block ,也不是 self 本身,而是两个相互 retain 的对象。

祝玩得愉快^_^
stormxx
2015-10-15 10:23:13 +08:00
不错!顶一下~
zhanghaitao
2015-10-15 11:20:59 +08:00
真心不错的
finab
2015-10-15 11:26:29 +08:00
@chisj good~
不过对于新手来说,很难分辨需不需要 weak 。
所以只要是 block 里都用 weak ,事实上是有帮助的,
而新手做的功能,需要在 block 里 weak 的,基本都是 就算在用之前被释放了,也没关系的那种对象
比如 controller
chisj
2015-10-15 12:16:03 +08:00
@finab 对于新手来说,也是要区分是否需要 weak :一个是某些情况下用 weak 确实会有问题,虽然新手不容易碰到;另一个是,如果养成了还没理清原理就敢写下代码的习惯,其实对新手的成长之路不太好。

内存管理是一道不高不低的坎,如果刚开始就有意去躲避这些坎,而不是啃下这块骨头,虽然当时轻松,后面会浪费更多的精力去补。
loveuqian
2015-10-15 15:01:31 +08:00
手机 mark
感谢总结
tunnyios
2015-10-15 15:37:02 +08:00
其实总结下来也就是:控制器强引用着 block 。凡是控制器的对象,或者控制器的成员变量,只要在 block 中就会被强引用,(间接导致 block 强引用了控制器)。引发循环引用,最终导致内存泄漏
MeiganFang
2015-10-15 17:10:24 +08:00
将 block 中所有的涉及到 self 的全给替换成 weakSelf ,并不是完全之策。比如多线程情况下, block 涉及到的 self 会多次 retain -1 ,这种情况会导致 block 未执行完 self 就已经释放。典型按理 AFNetworking


__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};

解决引用循环问题,是初级工程师到中级工程师必经的技术点。
aaronlam
2015-10-16 00:53:22 +08:00
总结的很赞 ⊙▽⊙
WildCat
2015-10-16 19:33:11 +08:00
@MeiganFang

AFN Block 的行为,在 Swift 中是否还是自动 -1 ?
另外再 Swift 的 Alamofire 中是否还是自动 -1 ?

我 Swift 里习惯用 [weak self] ,目前还没注意到 ViewController 提前释放的问题(初学者)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/228158

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX