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 · 3687 次点击
    这是一个创建于 3335 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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   ·   实用小工具   ·   2868 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 12:29 · PVG 20:29 · LAX 04:29 · JFK 07:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.