iOS-ARC

本文的内容包括

一、所有权修饰符
二、ARC的基本规则
三、ARC的实现

ARC中仍然是通过引用计数来管理内存,这个本质没有变。只是,不需要我们手动的写代码去管理内存了,编译器自动帮助我们管理“引用计数”相关的部分。那么ARC是通过什么方式来帮我们管理内存的呢?

ARC是通过编译期运行期两部分来处理的:

  • 编译期,编译器不是通过添加retain/release/autorelease这些方法,而是会直接调用更底层的C语言函数(如objc_retain)。
  • 运行期,ARC也包含运行期组件。比如,某些类方法返回对象前,为其执行了autorelease操作,而大多数情况,我们会对返回的对象保留,比如:_myPerson = [Person personWithName:@"Kobe"];
    那么其实就相当于先执行了一个autorelease,然后又retain了一下。ARC可以在运行期检测到这一多余的操作,也就是autorelease后紧跟retain。那么会它们两个会被改为调用objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValueobjc_autoreleaseReturnValue会检查后边是否紧接着调用objc_retainAutoreleasedReturnValue,如果是,就不将返回的对象注册到autoreleasepool中而直接传递,省略了autorelasepool注册,实现了最优化。

一、所有权修饰符

1. __strong修饰符

如它的名字一样,__strong表示对对象的强引用。持有强引用的变量在超出其作用域的时候被废弃,随着强引用的失效,引用的对象会随之释放。

知识点:

  1. __strong修饰符是id类型和对象类型默认的所有权修饰符,所以一般不写__strong。
  2. __strong和__weak和 __autoreleasing修饰符一样,可以将附有这些修饰符的自动变量初始化为nil。

2. __weak(iOS5以上才有,之前用__unsafe_unretained)

如它的名字一样,__weak修饰符表示对对象的弱引用。不持有对象。

知识点:

  1. 可以避免循环引用。
  2. 变量指向的对象被销毁了,变量也会自动置空为nil。

3. __unsafe_unretained

如它的名字一样,它是不安全的所有权修饰符。

知识点:

  1. 和__weak一样表示弱引用,不持有对象,但不会置nil,这也正是不安全的原因。
id __unsafe_unretained obj1 = nil;
    {
        id obj0 = [[NSObject alloc] init];
        obj1 = obj0;
        NSLog(@"A:%@",obj1);
    }
    NSLog(@"B:%@",obj1);

输出A和B虽然是一样的地址,但是此时B处obj1已经是野指针了。

4. __autoreleasing

通过给对象附加__autoreleasing修饰符 来替代调用autorelease方法,把对象注册到autoreleasepool中。

具体使用情况,在这里

二、规则

1. 不能使用retain/release/retainCount/autorelease

2. 用@autoreleasepool{}代替NSAutoreleasePool对象

3. 不要显式的调用dealloc

多数情况下在dealloc中删除已注册的代理或观察者。不用书写[super dealloc],因为ARC已经自动处理了。

4. 必须遵守内存管理的方法命名规则

  1. alloc/new/copy/mutableCopy,以上述名称开始的方法在返回对象时,必须返回给调用方所应当持有的对象。这在ARC下,依然没有变。
  2. ARC下追加了一条命名规则:
  • init,以init开头的方法,必须是实例方法,并且返回对象,类型应为id或该类的的对象类型,抑或是超类或子类型。该方法基本上只是对alloc方法返回的对象进行初始化操作并返回。
    注:initialize方法并不包含在上述命名规则里。

5. 对象类型不能作为结构体的成员

因为ARC把内存管理的工作分配给了编译器,所以编译器必须能够知道并管理对象的生命周期。例如C语言的自动变量(局部变量)可以使用该变量的作用域来管理。但是对于结构体成员来说,是无法实现的。

6. 显式转换id和void*

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;

但是,__bridge安全性与__unsafe_unretained类似,甚至更低。很容易造成野指针导致崩溃。
__bridge还有另外两种转换,__bridge_retained和__bridge_transfer

  1. Core Foundation 对象类型不在 ARC 管理范畴内,需要自己管理.
  2. __bridge只做类型转换,但是不修改对象(内存)管理权,原来是ARC管理的还用ARC,原来MRC管理的继续用MRC
  3. __bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
  4. __bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。

5. 不能使用NSAllocateObject/NSDeallocObject

6. 不能使用区域(NSZone)

三、ARC的实现

1. __strong实现

赋值给附有__strong修饰符的变量在实际的程序中是怎样运行的呢?

{
       id __strong obj = [[NSObject alloc] init];
   }

编译器在超出作用域时自动插入了release。

{
        id __strong obj = [NSMutableArray array];
    }

执行,alloc/new/copy/mutableCopy之外的方法,如array类方法:
像代码这样,返回注册到autoreleasepool中对象的方法使用了objc_autoreleaseReturnValue,如果其后紧接着调用objc_retainAutoreleasedReturnValue(),那么就不将返回的对象注册的autoreleasepool中而直接传递。通过这两个方法,优化了程序运行。

2.__weak的实现

id __strong obj = [[NSObject alloc]init];
id __weak obj1 = obj;

objc_initWeak函数中的weak_register_no_lock()把赋值对象obj的地址作为键值,通过哈希查找找到weak弱引用表中对应的数组,将附有__weak修饰符变量的指针添加到数组中。如果没有找到数组,表示是第一个weak指针,则新建一个数组。

对象在被废弃时dealloc方法中会调用object_dispose函数,该方法内部会通过调用weak_clear_no_lock()

  1. 通过哈希查找从weak表中获取废弃对象地址作为键值的记录是一个数组
  2. 将包含在数组中的所有附有__weak修饰符的变量地址,遍历数组赋值为nil。
  3. 从weak表中删除该记录。
  4. 从weak表中删除该键值。

所以,如果大量使用__weak修饰符的变量,则会消耗相应的cpu资源。良策是只在需要避免循环引用的时候使用__weak。

3. __autoreleasing修饰符的实现

objc_autorelease

4. 引用计数

可以通过_objc_rootRetainCount(obj)来获取对象的引用计数值。但实际上并不能完全信任该函数取得的值。对于已经释放的对象以及不正确的对象地址,有时也返回1。另外,在多线程中使用它,因为存在竞态条件的问题,所以取得的数值也不一定完全可信。当然它在调试中还是比较有用的。

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

推荐阅读更多精彩内容