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

引用计数

引用计数(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容