美文网首页
OC基础-内存管理-MRC(五)

OC基础-内存管理-MRC(五)

作者: 浮华_du | 来源:发表于2021-08-30 10:18 被阅读0次

    引用计数

    引用计数(Reference counting)是一个简单有效管理对象生命周期的方式。
    当我们新建一个新对象时候,它的引用计数+1,当一个新指针指向该对象,将引用计数+1。当指针不再指向这个对象时候,引用计数-1,当引用计数为0时,说明该对象不再被任何指针引用,将对象销毁,进而回收内存。


    image.png

    四条规则

    • 自己生成的对象自己持有 - alloc/new/copy/mutableCopy等方法
    • 非自己生成的对象,自己也能持有 - retain方法
    • 不再需要自己持有的对象时释放 - release方法(autorelease可以取得对象存在,但自己不持有对象,超出指定范围时能够自动并正确释放)
    • 不要释放非自己持有的对象,释放了会造成崩溃

    1.自己生成的对象自己持有 -- alloc/new/copy/mutableCopy等方法

        id obj = [[NSObject alloc] init]; // 创建并持有对象,RC = 1
        NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(obj)));
        //或    NSLog(@"retainCount = %ld",[obj retainCount]);
        // retainCount = 1
    
     id obj = [[NSObject alloc] init]; // 创建并持有对象,RC = 1
           /*
            * 使用该对象,RC = 1
            */
           [obj release]; // 在不需要使用的时候调用 release,RC = 0,对象被销毁
        NSLog(@"retainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(obj)));
        //或    NSLog(@"retainCount = %ld",[obj retainCount]);
       // retainCount = 0
    

    2.非自己生成的对象,自己也能持有 -- retain方法

    使用上述方法以外的方法创建的对象,我们并不持有,其RC初始值也为 1。但是需要注意的是,如果要使用(持有)该对象,需要先进行retain,否则可能会导致程序Crash。原因是这些方法内部是给对象调用了autorelease方法,所以这些对象会被加入到自动释放池中。

    (1).情况一:iOS 程序中不手动指定@autoreleasepool

    当RunLoop迭代结束时,会自动给自动释放池中的对象调用release方法。所以如果我们在使用前不进行retain,那么当RunLoop迭代结束,对象就会收到release消息,如果此时该对象RC值降为 0 就会被销毁。而我们这时候再去访问已经被销毁的对象,程序就会Crash。

        /* 正确的用法 */
    
        id obj = [NSMutableArray array]; // 创建对象但并不持有,对象加入自动释放池,RC = 1
    
        [obj retain]; // 使用之前进行 retain,对对象进行持有,RC = 2
        /*
         * 使用该对象,RC = 2
         */
        [obj release]; // 在不需要使用的时候调用 release,RC = 1
        /*
         * RunLoop 可能在某一时刻迭代结束,给自动释放池中的对象调用 release,RC = 0,对象被销毁
         * 如果这时候 RunLoop 还未迭代结束,该对象还可以被访问,不过这是非常危险的,容易导致 Crash
         */
    
    (2).手动指定@autoreleasepool

    如果@autoreleasepool作用域结束,就会自动给autorelease对象调用release方法。如果这时候我们再访问该对象,程序就会Crash。

        /* 错误的用法 */
        id obj;
        @autoreleasepool {
            obj = [NSMutableArray array]; // 创建对象但并不持有,对象加入自动释放池,RC = 1
        } // @autoreleasepool 作用域结束,对象 release,RC = 0,对象被销毁
        NSLog(@"%@",obj); // EXC_BAD_ACCESS
    
        /* 正确的用法 */
    
        id obj;
        @autoreleasepool {
            obj = [NSMutableArray array]; // 创建对象但并不持有,对象加入自动释放池,RC = 1
            [obj retain]; // RC = 2
        } // @autoreleasepool 作用域结束,对象 release,RC = 1
        NSLog(@"%@",obj); // 正常访问
        /*
         * 使用该对象,RC = 1
         */
        [obj release]; // 在不需要使用的时候调用 release,RC = 0,对象被销毁
    
    (3).自定义方法 创建但并不持有对象

    方法名就不应该以 alloc/new/copy/mutableCopy 开头,且返回对象前应该要先通过autorelease方法将该对象加入自动释放池。

    - (id)object
    {
        id obj = [NSObject alloc] init];
        [obj autorelease];
        retain obj;
    }
    

    这样调用方在使用该方法创建对象的时候,通过方法名他就会知道他不持有该对象,于是他会在使用该对象前进行retain,并在不需要该对象时进行release。

    备注:release和autorelease的区别:
    • 调用release,对象的RC会立即 -1;
    • 调用autorelease,对象的RC不会立即 -1,而是将对象添加进自动释放池,它会在一个恰当的时刻自动给对象调用release,所以autorelease相当于延迟了对象的释放。

    3.不再需要自己持有的对象时释放

    在不需要使用(持有)对象的时候,需要调用一下release或者autorelease方法进行释放(或者称为 “放弃对象使用权”),使其RC-1,防止内存泄漏。当对象的RC为 0 时,就会调用dealloc方法销毁对象。

    4.不要释放非自己持有的对象

    如果自己不是持有者,就不能对对象进行release,否则会导致程序Crash。另外,向已回收的对象发送消息也是不安全的.
    持有对象的两种方式

    • 通过 alloc/new/copy/mutableCopy 等方法创建对象
    • 创建但并不持有的对象,通过retain方法持有
        id obj = [[NSObject alloc] init]; // 创建并持有对象,RC = 1
        [obj release]; // 如果自己是持有者,在不需要使用的时候调用 release,RC = 0
        /*
         * 此时对象已被销毁,不应该再对其进行访问
         */
        [obj release]; // EXC_BAD_ACCESS,这时候自己已经不是持有者,再 release 就会 Crash
        /*
         * 再次 release 已经销毁的对象(过度释放),或是访问已经销毁的对象都会导致崩溃
         */
    
    
        id obj = [NSMutableArray array]; // 创建对象,但并不持有对象,RC = 1
        [obj release]; // EXC_BAD_ACCESS 虽然对象的 RC = 1,但是这里并不持有对象,所以导致 Crash
    
    

    执行如下代码,可能会有问题,也可能没有问题。对象所占内存在 “解除分配(deallocated)” 之后,只是放回可用内存池。如果对象所占内存还没有分配给别人,这时候访问没有问题,如果已经分配给了别人,再次访问就会崩溃。

        Person *person = [[Person alloc] init]; // 创建并持有对象,RC = 1
        [person release]; // 如果自己是持有者,在不需要使用的时候调用 release,RC = 0
        [person release]; // 向已回收的对象发送消息是不安全的
    

    https://juejin.cn/post/6844904129676984334

    相关文章

      网友评论

          本文标题:OC基础-内存管理-MRC(五)

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