iOS 中控制器释放问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
tunnyios
V2EX    iDev

iOS 中控制器释放问题

  •  
  •   tunnyios
    chenzao1024 2015-10-15 09:15:47 +08:00 4029 次点击
    这是一个创建于 3652 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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/

    第 1 条附言    2015-10-15 15:39:01 +08:00

    其实总结下来也就是:控制器强引用着 block 。凡是控制器的对象,或者控制器的成员变量,只要在 block 中就会被强引用,(间接导致 block 强引用了控制器)。引发循环引用,最终导致内存泄漏!

    系统自带的动画的 block 方法、 dispathafter()等方法,可以不用_weak typeof (self)weakSelf = self;

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

    内存管理是一道不高不低的坎,如果刚开始就有意去躲避这些坎,而不是啃下这块骨头,虽然当时轻松,后面会浪费更多的精力去补。
    loveuqian
        6
    loveuqian  
       2015-10-15 15:01:31 +08:00 via iPhone
    手机 mark
    感谢总结
    tunnyios
        7
    tunnyios  
    OP
       2015-10-15 15:37:02 +08:00
    其实总结下来也就是:控制器强引用着 block 。凡是控制器的对象,或者控制器的成员变量,只要在 block 中就会被强引用,(间接导致 block 强引用了控制器)。引发循环引用,最终导致内存泄漏
    MeiganFang
        8
    MeiganFang  
       2015-10-15 17:10:24 +08:00   1
    将 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
        9
    aaronlam  
       2015-10-16 00:53:22 +08:00 via iPad
    总结的很赞 ⊙⊙
    WildCat
        10
    WildCat  
       2015-10-16 19:33:11 +08:00
    @MeiganFang

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

    我 Swift 里习惯用 [weak self] ,目前还没注意到 ViewController 提前释放的问题(初学者)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1031 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 22:57 PVG 06:57 LAX 15:57 JFK 18:57
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86