Objective-C高级编程读书笔记

自动引用计数

ARC

自动引用计数 ARC :是指内存管理中对引用计数采取自动计数的计数。

苹果文档

ARC 是让编译器来进行内存管理。设置了 ARC 为有效状态,就无需再次键入 retain 或者 release 代码。降低了程序崩溃,内存泄漏等风险的风险的同时,很大程度减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再使用的对象。

内存管理

  • 自己生成的对象,自己所持有
  • 非自己生成的对象,自己也可以持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放
  • autorelease 对象
对象操作 Objective-C 方法
生成并持有对象 alloc/new/copy/mutableCopy等方法
持有对象 retain
释放对象 release
废弃对象 dealloc

自己生成的对象,自己所持有

下列名称开头的方法名意味着自己生成的对象自己所持有。

  • alloc
  • new
  • copy
  • mutableCopy

实例代码

id obj = [NSObject new];

非自己生成的对象,自己也可以持有

用 alloc/new/copy/mutableCopy 以外的方法取得的对象,因为非自己生成并持有,所以不是该对象的持有者。但是我们可以持有它

实例代码:

// 取得非自己生成的对象
id obj = [NSMutableArray array];

// 持有非自己生成的对象
[obj retain];

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

自己持有的对象,一旦不在需要,持有者有义务释放该对象,释放使用 release。
实例代码:

// 自己生成并持有对象
id obj = [[NSObject alloc] init];

// 释放对象
[obj release];

同理,用 alloc 方法由自己生成并持有的对象通过 release 方法就释放了。非自己生成而持有的对象,若用 retain 方法变为自己持有,也可以使用 release 方法释放。

// 取得非自己生成的对象
id obj = [NSMutableArray array];

// 持有对象
[obj retain];

// 释放对象
[obj release];

非自己持有的对象无法释放

释放非自己持有的对象会造成崩溃
实例代码:

// 自己生成并持有
id obj = [NSObject new];
// 释放持有对象
[obj release];
// 释放非自己持有的对象,因为之前已经释放了。crash !!!
[obj release];

autorelease 对象

autorelease 对象可以达到对象存在但是自己又不持有的效果。如 [NSMutableArray array]

实现方案:

- (NSMutableArray *) array
{
      // 创建并持有对象
      id obj = [NSObject new];
      // 取得对象存在,但是自己不持有
     [obj autorelease];
     return obj;
}

autorelease 对象会被 autoreleasepool 所持有,在 runloop 休眠的时候会释放所持有的对象。

GUNstep 的计数实现

将引用计数存在对象占用的内存块头部的变量中。

  • 在 Objective-C 的对象中存在引用计数这一个整数值。
  • 调用 alloc 或是 retain 方法后,引用计数值加 1。
  • 调用 release 后,引用计数减 1。
  • 引用计数值为 0 时,调用 dealloc 方法废弃对象。

苹果的实现

苹果使用了散列表来管理引用计数。
对比:

GUNstep

  • 少量代码即可完成。
  • 能够统一管理引用计数用内存块与对象用内存快。

Apple

  • 对象用内存快的分配无需考虑内存块头部。
  • 引用计数表各记录中存在内存块地址,可以从各个记录追溯到各对象内存块。利于调试。

autorelease

autorelease 对象可以达到对象存在但是自己又不持有的效果。autorelease 对象离开作用域都将调用 release 实例方法。
具体使用(MRC):

  1. 生成并持有 NSAutoreleasePool 对象。
  2. 调用已分配对象的 autorelease 实例方法。
  3. 废弃 NSAutoreleasePool 对象。

实例代码:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSObject new];
[obj autorelease];
[pool drain];

ARC 规则

所有权修饰符

  • __strong 修饰符
  • __weak 修饰符
  • __unsafe_unretained 修饰符
  • __autorelease 修饰符

__strong 修饰符

__strong 是默认修饰符
__strong 修饰符表示对对象的强引用。持有强引用的变量在超出其作用域时被废弃,随着强引用失效,引用的对象会随之释放。

  • 如果是自己生成的对象正常持有。
  • 如果不是自己生成的对象,会自动加 retain 来持有。

__weak 修饰符

__strong 修饰符会造成循环引用问题,__weak 修饰符可以避免循环引用。__weak 不持有对象,在对象被释放的时候会自动设置为 nil。
__weak 修饰符只能用于 iOS 5 以上以及 OS X Lion 以上版本,其他版本需要使用 __unsafe_unretained 来替代。

__unsafe_unretained 修饰符

__unsafe_unretained 修饰符是不安全的所有权修饰符。尽管 ARC 是由编译器管理的,但是 __unsafe_unretained 修饰符修饰的变量不属于编译器的内存管理对象。
和 __weak 不持有对象一样。释放了不会被设置为 nil,会出现野指针的情况。

__autorelease 修饰符

在 ARC 下不能显示的调用 autorelease 方法,那么需要使用 __autorelease 修饰。
实例代码:

// MRC 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSObject new];
[obj autorelease];
[pool drain];

// ARC
@autoreleasepool{
  id __autorelease obj = [[NSObject alloc] init];
}

知识点:

  1. 访问 weak 变量是必须访问注册到 autorelease 的对象,因为 weak 修饰符只持有对象的弱引用,而在访问过程中,该对象可能被废弃。如果把要访问的对象注册到 autoreleasepool 中,那么在 runloop 周期内都不会给释放。
  2. id 的指针和对象的指针(如 NSError **)没有显示的修饰符那么默认是 __autorelease.

ARC 下编写代码规则

  • 不能使用 retain / release / retainCount /autorelease。
  • 不能使用 NSAllocateObject / NSDeallocateObject。
  • 必须遵守内存管理的方法命名规则。
  • 不要显式的调用 delloc。
  • 使用 @autoreleasepool 块来替代 NSAutoreleasePool。
  • 不能使用区域(NSZone)
  • 对象型变量不能作为 C 语言结构体的成员。
  • 显示转换 id 和 void *。

Toll-Free Bridge

Core Foundation 对象和 Objective-C 对象没有区别,不同之处只是由哪个框架生成对象, Core Foundation 对象可以转换成 Objective-C 对象来使用,转换过程称之为 Toll-Free Bridge。有以下几个关键字

  • __bridge :只做类型转换,但是不修改对象(内存)管理权
  • __bridge_retained:将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象。
  • __bridge_transfer:将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。

ARC 实现

strong 修饰符

编译器来管理,2 种情况

  1. alloc/new/copy/mutableCopy等方法生成的对象,会自动插入 release方法。
  2. 非情况 1 方法生成的对象,如 [NSMutableArray array] ,会分别插入objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue方法。

objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue是成对出现的。在对象返回处调用objc_autoreleaseReturnValue,并且强引用的时候紧接着会调用objc_retainAutoreleasedReturnValue
系统会对这个其中赋值过程进行优化。

过程图解.png

weak 修饰符

weak 对象流程

  1. 初始化 weak:objc_initWeak
  2. 改变 weak 值:objc_storeWeak
  3. 释放 weak 值:objc_destroyWeak

Blocks

什么是 Blocks

Blocks 是 C 语言的扩充功能:带有局部变量的匿名函数。
其他语言中的 Blocks 的名称

语言 名称
C Blocks
Smalltalk Blocks
Ruby Blocks
LISP Lambda
Python Lambda
C++ Lambda

语法

^ 返回值类型 参数列表{
  表达式
}

Blocks 变量

语法(用在属性,变量处)

返回值类型 (^ 变量名)参数列表

截获自动变量

  • 普通变量获取值得内容
  • 指针变量强引用指针对象

__block

截获的自动变量不能子啊 Blocks 块内被修改,需要加 __block 修饰符。

Blocks 的实现

一个 Blocks 被编译后会生成这么几个结构体

  • __block_impx:包含了 isa,标志位,保留字段,执行的函数指针。
  • __xxx_block_desc_x:包含了保留字段,block 的大小。
  • xxx_block_impx_xxx:包含了上面 2 个成员。

x 表示由编译器决定的名字。

截获自动变量的原理

截获的变量,会在__block_impx 中生成相应的成员变量。普通变量值是拷贝值,指针则是强引用指针。

__block 修饰符

当用 __block 修饰符的时候,会多生成__Block_byref_val_x,包含了被捕获的值,isa,forwarding 指针等字段。
之所以可以在 block 内部修改 __block 变量因为生成这个结构体来包装了一层,通过这个结构体达到修改外部变量的目的。

forwarding 指针

当一个栈 block 执行出了作用域的时候,其捕获的 __block 变量也会出作用域,会出现野指针,如果把 __block 拷贝到堆上,这个时候这个 __block 变量也应该一同拷贝,需要有一个机制在被拷贝之后也会引用到系统的堆变量上。forwarding 指针就是做这个的,forwarding 在未被拷贝的时候始终指向自己,在拷贝之后 forwarding 指针会指向堆变量。防止了野指针的问题,也解决了拷贝之后的引用问题。

循环引用

在使用 block 由于捕获变量都是强引用的,所以需要注意循环引用。可以使用 weak 变量来避免循环引用。

更详细的内容可以看我另外一篇Block 博客

未完待续...

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

推荐阅读更多精彩内容