美文网首页
29.理解引用计数

29.理解引用计数

作者: z_zero | 来源:发表于2016-08-04 15:50 被阅读0次

    《编写高质量iOS与OS X代码的52个有效方法》--第五章 第29条
    (ps:此乃读书笔记,加深记忆,仅供大家参考)


    第5章 内存管理

    ARC几乎把所有内存管理事宜都交给编译器决定,开发者只需专注于业务逻辑。

    第29条:理解引用计数

    Objective-C语言使用引用计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数器。如果想使某个对象继续存活,那就递增其引用计数;用完了之后,就递减其引用计数。计数变为0,就表示没人关注此对象了,于是,就可以把它销毁。

    引用计数工作原理

    在引用计数架构下,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。这在Objective-C中叫做“保留计数”(retain count),不过也可以叫“引用计数”(reference count)。NSObject协议声明了下面三个方法用于操作计数器,以递增或递减其值:

    • Retain 递增保留计数。
    • release 递减保留计数。
    • autorelease 待稍后清理“自动释放池”(autorelease pool)时,再递减保留计数。

    查看保留计数的方法叫做retainCount, 此方法不太有用,即便在调试时也如此,所以笔者(苹果公司)并不推荐大家使用这个方法。

    对象创建出来时,其保留计数至少为1。 最终当保留计数归零时,对象就回收了(deallocated),也就是说,系统会将其占用的内存标记为“可重用”(reuse)。此时,所有指向该对象的引用也都变得无效了。

    对象如果持有指向其他对象的强引用(strong reference),那么前者就“拥有”(own)后者。也就是说,对象想令其所引用的那些对象继续存活,就可将其“保留”。等用完了之后,再释放。

    如果按“引用树”回溯,那么最终会发现一个“跟对象”(root object)。在Mac OS X应用程序中,此对象就是NSApplication对象;而在iOS应用程序中,则是UIApplication对象。两者都是应用程序启动时创建的单例

    NSMutableArray * array = [[NSMutableArray alloc] init]; 
    NSNumber * number = [[NSNumber alloc] initWithInt:2333];
    [array addObject:number];    
    [number release];    
    //do something with 'array'    
    [array release];
    

    在Objective-C中,调用alloc方法所返回的对象由调用者所拥有。也就是说,调用者已通过alloc方法表达了想令该对象继续存活下去的意思。不过请注意,这并不是说此对象此时的保留计数必定是1。在alloc或“initWitInt:”方法的实现代码中,也许还有其他对象也保留了此对象,所以,其保留计数至少为1。不能说保留计数一定是某个值,只能说你所执行的操作是递增了该计数还是递减了该计数。

    number对象调用release释放之后,仍然存活.因为数组还在引用着它。然而绝不应该假设此对象一定存活,也就是说,不要像下面这样编写代码:

    NSNumber * number = [[NSNumber alloc] initWithInt:2333];
    [array addObject:number];
    [number release];
    NSLog(@"number = %@", number);
    

    如果调用release由于某些原因,其保留计数降至0,那么number对象所占内存也许会回收,这样的话,在调用NSLog可能就将是程序崩溃了。这里说“可能”,而没说“一定”,因为对象所占的内存在“解除分配”(deallocated)之后,只是放回“可用内存池”(avaiable pool)。如果执行NSLog时尚未复写对象内存,那么该对象仍然有效,这时程序不会崩溃。

    为避免在不经意间使用了无效对象,一般调用完release之后都会清空指针。这就能保证不会出现可能指向无效对象的指针,这种指针通常称为“悬挂指针”(dangling pointer)。

    NSNumber * number = [[NSNumber alloc] initWithInt:2333];
    [array addObject:number];
    [number release];
    number = nil;
    

    属性存取方法中的内存管理

    若属性为“strong关系”(strong relationship),则设置的属性值会保留。

    - (void)setFoo:(id)foo
    {
        [foo retain];
        [_foo release];
        _foo = foo;
    }   
    

    此方法将保留新值并释放旧值,然后更新实例变量,令其指向新值。顺序很重要

    自动释放池

    调用release会立刻递减对象的保留计数(而且还有可能令系统回收此对象),然而有时候可以不调用它,改为调用autorelease,此方法会在稍后递减计数,通常是在下一次“时间循环”(event loop)时递减,不过也可能执行的更早些。

    此特性很有用,尤其是在方法中返回对象时更应该使用它。在这种情况下,我们并不总是想令方法调用者手工保留其值。

    - (NSString *)stringValue
    {
        NSString * str = [[NSString alloc] initWithFormat:@"I am this: %@", self];
        return self;    //return [str autorelease];
    }
    

    此时返回的str对象其保留计数比期望值要多1(+1 retain count),因为调用alloc会令保留计数加1,而又没有与之对应的释放操作。

    这里应该用autorelease,它会在稍后释放对象,从而给调用者留下了足够长的时间,使其可以在需要时先保留返回值。换句话说,此方法可以保证对象在跨越“方法调用边界”(method call boundary)后一定存活。

    保留环

    通常采用“弱引用”(weak reference)来解决此问题,或是从外界命令循环中的某个对象不再保留另一个对象。这两种方法都能打破保留环,从而避免内存泄露。

    要点

    • 引用计数机制通过可以递增递减的计数器来管理内存。对象创建好之后,其保留计数至少为1。若保留计数为正,则对象继续存活。当保留计数降为0时,对象就被销毁了。
    • 在对象生命期中,其余对象通过引用来保留或释放此对象。保留与释放操作分别会递增或递减保留计数。

    理解 iOS 的内存管理----唐巧大神

    相关文章

      网友评论

          本文标题:29.理解引用计数

          本文链接:https://www.haomeiwen.com/subject/jigcsttx.html