Objective-C高级编程:iOS与OS X多线程和内存管理(二)

1 Blocks 概要

1.1 什么是 Blocks

Blocks是 C 语言的扩充功能,Blocks 是带有自动变量(局部变量)的匿名函数。

带有自动变量值的匿名函数  分为 “ 匿名函数” 和 “带有自动变量值”

什么是带有自动变量值 

带有自动变量i值的匿名函数。Blocks保持自动变量的值。

2 Blocks 模式

2.1 Block 语法

C 语言函数定义相比,有两点不同 1:没有函数名。 2:带有 ^

第一种:

^ int (int count) { return count + 1;};

第二种: 

省略返回值类型时:Block 语法将按照 return 语句的类型,返回返回值。

第三种: 


2.2 Block 类型变量

“Block” 既指源代码中的 Block 语法,也指由 Block 语法所生成的值。

Block变量声明:

int (^ blk) (int);

Block 类型变量的用途:

(1)自动变量(局部变量)

(2)函数参数

(3)静态变量

(4)静态全局变量

(5)全局变量

使用 Block 语法将 Block 赋值为 Block 类型变量

int (^blk) (int)  = ^(int count) {return count +1 };

在函数参数中使用 Block 类型变量向函数传递 Block:

一种:

void func (int (^blk) (int)) {…}

另一种:

typedef  int (^blk_t) (int);

void func (blk_t blk) {…}

在函数返回值中指定 Block 类型,可以将 Block 作为函数的返回值返回

一种:

- (int (^) (int))func             //注:函数返回值为 Block时,返回类型没有 block 变量名

{

        return ^(int count) {return count + 1;};

}

另一种:

typedef  int (^blk_t) (int);

- (blk_t) func

{

                return ^(int count) {return count + 1;};

}

用 typedef 给 block 重命名 

 用 Block 类型变量调用 Block,与通常的 C 语言变量一样使用:

- (int) methodUsingBlock: (blk_t) blk  rate:(int) rate

{

        return blk(rate);

}

2.3 截获自动变量值

带有自动变量值的匿名函数带有自动变量值 Block 中表现为截获自动亦是值 

Block 截获自动变量

Blocks 中,Block 表达式截获所使用的自动变量的值,即保存该自动变量的瞬间值。因为 Block 表达式保存了自动变量的值,所以在执行 Block 语法后,即使改写 Block 中使用的自动变量的值也不会影响 Block 执行时自动变量的值。

2.4 __block 说明符

自动变量值截获只能保存执行 Block 语法瞬间的值。保存后就不能改写该值。尝试改写截获的自动变量值:会生产编译错误 

若想在 Block 语法的表达式中将值赋给在 Block 语法外声明的自动变量,需要在该自动变量上附加 __block 说明符。

使用附有 __block 说明符的自动变量可在 Block 中赋值,该变量称为 __block 变量。

2.5 截获的自动变量

截获 OC 对象,调用变更该对象的方法:

结论:赋值给截获的自动变量 arrayM 的操作会产生编译错误,但使用截获的值却不会有问题。

Blokc 截获 C 语言数组: 

结论:在 Blocks 中,截获自动变量的方法并没有实现对 C 语言数组的截获。这时,使用指针可以解决该问题。 

3 Blocks 的实现

3.1 Block 的实质

Block的实质即为 Objective-C 的对象(结构体)。

结构体包括:

    isa类结构指针 (三大类型:_NSConcrete[Stack | Malloc | Global ]Block)

    FuncPtr  函数指针

    flags , reserved和 Block 截获的自动变量

clang -rewrite-objc源代码文件名

3.2 截获自动变量值 (只对 Block 中使用的自动变量)

截获自动变量值:在执行 Block 语法时,Block 语法表达式所使用的自动变量值被保存到 Block 的结构体实例中。

3.3 __block 说明符

Block 中修改自动变量的两种方法:

第一种:用 静态局部变量、静态全局变量、全局变量。

注:静态局部变量:Block 结构体中存放静态局部变量的指针。

第二种:用 __block 说明符 (__block 存储域类型说明符) 类似于 static 、auto 、register 说明符,用于指定变量值的存储到哪个存储域中

__block变量clang后转换为结构体, Block 的结构体实例持有指向 __block 变量的结构体实例的指针。

2.3.4 Block 存储域

Block 转换为 Block 的结构体类型的自动变量,__block 变量转换为 __block 变量的结构体类型的自动变量。所谓结构体类型的自动变量,即栈上生成的该结构体的实例。

Block 的类:

_NSConcreteGlobalBlock -存储域:程序的数据区域(.data )

1)记述全局变量的地方使用 Block 语法时

(2)Block 语法的表达式中不使用截获的自动变量时

_NSConcreteStackBlock -存储域:栈 ;复制效果:到堆

除 Global 之外的 Block 生成的 Block 都是栈Block

_NSConcreteMallocBlock -存储域:堆;复制效果:引用计数增加

Blocks提供了将 Block 和 _block 变量从栈上复制到堆上的方法。

这样即使 Block 语法记述的变量作用域结束,堆上的 Block 还可以继续存在。

实际上当 ARC 有效时,大多数情形下编译器会恰当地进行判断,自动生成将 Block 从栈上复制到堆上的代码。

什么时候栈上的 Block 会被复制到堆上:

(1)调用 Block 的 copy 实例方法时

(2)Block 作为函数的返回值时

(2)将 Block 赋值给附有 __strong 修饰符 id 类型的类或Block 类型成员变量时

(3)方法名中含有 usingBlock 的 Cocoa 框架的方法 和 GCD 的 API

需要手动复制的情况:

(1)NSArray 类的initWithObjects

(2)向方法或函数的参数中传递 Block 时(可以不用手动复制) 

不管 Block 配置在何处,用 copy 方法复制都不会引起任何问题。在不确定时调用 copy 方法即可。

3.5 __block 变量存储域

Block中使用 __block 变量,当 Block 从栈复制到堆时,使用的所有 __block 变量也全部被从栈复制到堆。

在多个 Block 中使用 __block 变量   

Block 超出变量作用域可存在的原因:将 Block 和 __block 变量从栈上复制到堆上

__block 变量用结构体成员变量 __forwarding 存在的原因:实现无论 __block变量配置在栈上还是堆上都能正确地进行访问 

3.6 截获对象

__main_block_copy_0 函数使用 _Block_object_assign 函数将对象类型对象赋值给 Block 结构体的成员变量 array 中并持有该对象。

_Block_object_assign 函数调用相当于 retain 实例方法的函数,将对象赋值在对象类型的结构体成员变量中。

__main_block_dispose_0 函数使用 _Block_object_dispose 函数,释放赋值在 Block 用结构体成员变量 array 中的对象。

_Block_object_dispose 函数相当于 release 实例方法的函数,释放赋值在对象类型的结构体成员变量中的对象。

__main_block_copy_0 __main_block_dispose_0 函数在 Block 从栈复制到堆时以及堆上的 Block 被废弃时调用

3.7 __block 变量和对象

__block 说明符可指定任何类型的自动变量。

3.8 Block 循环引用

Block 中使用附有 __strong 修饰符的对象类型自动变量,那么当 Block 从栈复制到堆时,该对象为 Block 所持有。这样容易引起循环引用。

解决方法:

1. ARC :通过 __weak 或 __unsafe_unretained 修饰符(iOS4) 来替代__strong 类型的被截获的自动变量。

2. MRC:通过 __block 说明符指定变量不被 Block 所 retain ; ARC下 __block说明符的作用仅限于使其能在 Block 中被赋值。

原理:

如果对 Block 做一次 copy 操作,Block 的内存就会在堆中

它会将所引用的对象做一次 retain 操作

非 ARC : 如果所引用的对象用了 __block 修饰,就不会做 retain 操作

ARC :如果所引用的对象用了 __unsafe_unretained 或 __weak 修饰,就不会做 retain 操作。

3.9 copy/release

ARC无效时,一般需要手动将 Block 从栈复制到堆。也要释放复制的 Block

copy 方法复制 release 方法释放。



注:静态局部变量,静态全局变量,全局变量 的相同与不同点:

相同点:存储区都在全局区

不同点: 作用域不同

全局变量: 其它文件需要用extern关键字再次声明这个全局变量。

    静态全局变量:仅所在的文件才可以访问

    静态局部变量:仅对其所在的函数体作用域可见。

截获 OC 对象,调用变更该对象的方法:

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

推荐阅读更多精彩内容