iOS复习笔记:内存管理之引用计数

现在我们使用Objective-C编写iOS和Mac OS App的时候都是使用的是ARC来进行内存管理的。用一句话来总结ARC的功能的话,就是ARC使用编译器来代替程序员做内存管理的工作。虽然编译器帮我们做了内存管理工作,但是我们应该弄清楚编译器帮我们做了哪些工作,哪些工作是编译器无法做的,还是需要我们程序员来完成的。

OC中对象的内存管理是基于引用计数的内存管理:(Reference Counted Memory Management)

我们可以想象一下我们在系统中使用对象的情形,在需要的时候向系统去申请内存,不需要的时候释放对象内存,还给系统。如果长期霸占着不需要的内存而不释放,那就是刷流氓,会造成系统的内存泄露。内存泄露的多了,系统有可能会崩溃。

内存管理 == 引用计数,如果我们明白了引用计数的原理和使用的规则,OC的内存管理就变的简单了。

对象使用引用计数方法使得内存管理变的很简单。

我们需要弄清楚两个问题:1. 引用计数的对象是什么?2. 引用计数是如何变化的?

引用计数的对象是什么:这个问题很简单,在这里我们讨论的引用计数的对象就是Objective-C对象。

引用计数是如何变化的:要想回答这个问题,我们需要先回答另一个问题,就是有哪些操作会影响引用计数的变化?

影响引用计数的变化的操作,大致有以下4种:

  1. 创建对象从而获得对象的所有权(have ownership),引用计数+1, 从0变成1
  2. 持有对象从而取得对象的所有权(take ownership),引用计数+1
  3. 释放对象从而放弃对象的所有权(relinguish ownership),引用计数-1;
  4. 如果一个对象的引用计数-1后为0,意味着这个对象不存在所有权关系,没有被其它对象需要的可能,也就没有存在的意义,系统会销毁(destroy)对象回收对象分配的内存。这个销毁操作是系统自动进行的。

我们可以发现一个共同点,上面的这些操作都是针对对象的所有权(ownership)进行的,对象的所有权可以看作是操作对象的凭证或者授权,没有对象的所有权是不能对该对象进行操作的。

所以要想操作一个对象,首先必要要获得对象的所有权,获得所有权的方法有两种:

  1. 如果这个对象已经存在了,我们只需要声明持有这个对象来获得对象的所有权,同时对象的引用计数会+1
  2. 如果对象不存在,我们需要新创建一个对象,同时自然会获得这个新建对象的所有权,新建对象的引用计数从0变成1。

对于一个我们没有所有权的对象,是不能进行操作的,如果不小心操作了一个没有所有权的对象,程序有可能会崩溃。

OC中有相应的方法来进行对象所有权操作的方法:

对象所有权操作描述 OC方法
新建对象获得对象的所有权 alloc/new/copy/mutableCopy
取得已有对象的所有权 retain
放弃对象的所有权 release
销毁对象(系统自动调用) dealloc

内存管理需要遵循的几个规则:

  1. 能够获得任何新创建对象的所有权:使用alloc/new/copy/mutableCopy开头的方法来新建对象,并取得对象的所有权。我用四个字简单总结就是你建你有。
  2. 能够取得其它对象(不是自己创建的)的所有权:可以使用retain方法取得对象的所有权来操作对象。对于不是以alloc/new/copy/mutableCopy开头的方法返回得到的对象,虽然对象存在,但是我们没有该对象的所有权,不能对它进行操作。只有通过retain方法取得对象的所有权后,才能进行相应的操作。简单总结就是他建你有。
  3. 及时放弃不需要的对象的所有权:当一个对象不需要时,不管是通过新建对象还是使用retain方法获得的对象所有权,都必须要调用release方法来放弃对该对象的所有权。简单来说就是没用快扔。
  4. 不能放弃你没有所有权的对象:对于没有所有权的对象,不能调用release方法,一旦调用了release方法,程序就有可能出错奔溃。简单来说就是没有别动。

如果想要进一步弄清楚OC对象的内存管理规则,必须要清楚几个问题和细节:

  1. 引用一个对象和持有一个对象的区别?或者说获得对象和获得对象所有权的区别?
    面向对象的知识告诉我们,一个对象的引用其实就是对象在内存中的地址,通过引用我们可以找到我们需要的对象的位置。OC中的内存管理告诉我们,持有一个对象是指我们不仅能够知道对象在内存中的位置,而且我们还拥有对象的所有权,有了获得了对象的所有权后,我们能够操作这个对象。
    举个不太恰当的例子:比如说你暗恋一个小姐姐,同时你也知道了小姐姐的住址,但是小姐姐并没有同意和你在一起,此时你只是有了小姐姐的地址,然而并不能对小姐姐做什么;如果你不仅知道了小姐姐的住址,小姐姐也喜欢你同意和你在一起了,此时你获得了小姐姐的所有权(不太恰当的说法),然后你就能对小姐姐进行一番操作了。这就是引用一个对象和持有一个对象的区别。

下面使用一些代码例子来说明内存管理和引用计数:

Example 1: 创建对象并获得对象所有权

// 使用alloc方法,并获得对象所有权
id obj1 = [[NSObject alloc] init];
// 使用new方法,并获得对象所有权
id obj2 = [NSObject new];

Example 2: 不是自己创建的对象,获得对象所有权

// 获得一个不是自己创建的对象
id obj1 = [NSMutableArray array];
// 获得对象的引用,但是没有获得对象的所有权
[obj1 retain];
//获得了对象的所有权

Example 3: 不需要的对象,快快放弃对象的所有权

//创建对象并获得对象所有权
id obj = [[NSObject alloc] init];
//获得对象的所有权,可以操作对象
...
  
[obj release];
// 放弃对象的所有权,但是变量obj还是指向对象,即对象的引用。但是不能访问对象和操作对象。

Example 4: 方法返回对象时的所有权关系

//假设allocMyObject和myObject方法存在于MyClass中
- (id)allocMyObject {
  //新建对象并获得所有权
  id obj = [[NSObject alloc] init];
  // 方法取得对象的所有权
  return obj;
  //返回对象,并转把对象的所有权转移到方法调用者
}

- (id)myObject {
  //新建对象并获得所有权
  id obj = [[NSObject alloc] init];
  [obj autorelease];
  //方法放弃对象的所有权
  return obj;
  //返回对象,方法调用者无法取得对象的所有权
}

//myObj是MyClass的一个实例对象
//通过alloc开头的方法创建的对象,获得对象的所有权
id obj1 = [myObj allocMyObject];

//获得对象的引用,但是没有取得对象的所有权
id obj2 = [myObj myObject];

Example 5: 不能操作没有所有权的对象

// 新建一个对象并取得对象的所有权
id obj1 = [[NSObject alloc] init];
//具有对象的所有权
[obj1 release];
//放弃对象的所有权,此时不能操作对象


[obj1 release];
// 操作了一个没有所有权的对象,系统可能崩溃

// 获得一个对象的引用,但没有取得对象的所有权
id obj2 = [myObj myObject];
// 此时没有对象的所有权

[obj2 release];
// 操作了一个没有所有权的对象,系统可能崩溃

总结

可以用一个狗和绳圈的比喻来描述对象引用计数的过程变化:狗脖子要有绳圈才能上街。当需要带狗上街时,你就应该给它套上一个绳圈,表明你取得了这个狗的所有权。当别人看上了你的狗,他也可以给这个狗加上一个绳圈,表明也取得了这个狗的所有权。只要狗的脖子上面还有绳圈,说明这个狗是有人所有的,或者说是有需求的,这时候这个狗是跑不掉的。当别人不需要这个狗时,就会解开他拥有的绳圈,表明放弃了对这个狗的所有权。当所有人都解开了绳圈,放弃对这个狗的所有权时。狗的脖子上是没有绳圈的,这时狗子一看,没有绳圈套着了,赶紧溜吧。这里的🐶就相当于一个对象,脖子上的绳圈就是引用计数。

引用计数的需要遵循的规则:

  1. 你建你有
  2. 他建你有
  3. 没用快扔
  4. 没有别动

我们还需要分清楚对象的引用和对象的所有权的区别,想想小姐姐的故事。

如果觉得有用可以去GitHub给个👍。

参考:Pro Multithreading and Memory Management for iOS and OS X: With ARC, Grand Central Dispatch and Blocks.

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

推荐阅读更多精彩内容