block原理

1. block的定义

block是Objective-C对于闭包的实现(闭包是一个函数<或者指向函数的指针>加上函数有关的自由变量).

block的数据结构

block也是对象, 以下对block结构体的成员作简单解释

isa : 指向Class对象的指针, 所有对象都有该指针

flags : 用于按 bit 位表示一些 block 的附加信息

reserved : 保留变量

invoke : 指向函数的指针, 指向block的实现代码

descriptor : 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。

捕捉到的变量 : 捕捉过来的变量,block 之所以能够访问它外部的局部变量,就是因为将这些变量(或对象的地址)拷贝到了结构体中。

copy : 用于保留捕获的对象

dispose : 用于释放捕获的对象

2. block的类型

全局块(_NSConcreteGlobalBlock)

栈块(_NSConcreteStackBlock)

堆块(_NSConcreteMallocBlock)

这三种block各自的存储域如下表

                    类                                                             设置对象的存储域

_NSConcreteStackBlock                                                        栈

_NSConcreteGlobalBlock                                         程序的数据区域(.data区)

_NSConcreteMallocBlock                                                        堆

说明 :

全局块存在于全局内存中, 相当于单例.

栈块存在于栈内存中, 超出其作用域则马上被销毁

堆块存在于堆内存中, 是一个带引用计数的对象, 需要自行管理其内存

全局块(_NSConcreteGlobalBlock)

block定义在全局变量的地方

block没有截获任何自动变量

以上两个情况满足任意一个则该block为全局块, 全局块的生命周期贯穿整个程序, 相当于单例.

栈块(_NSConcreteStackBlock)

只要不是全局块, 且block没有被copy, 就是栈块.栈块的生命周期很短, 当前作用域结束, 该block就被废弃. 要想在当前作用域以外的地方使用该block, 应该把该block从栈copy到堆上


从栈复制到堆上的Block与__block变量

堆块(_NSConcreteMallocBlock)

简单来说, 栈块copy之后就变成堆块, 这简单吧~

ARC下的block类型

因为ARC下默认变量修饰符为__strong, 所以我们接触到的block几乎全是堆block和全局block.

ARC下,  blk = block;  相当于  blk = [block copy];

3. block特性

1. 截获自动变量值

1> 对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的. 也就是说block的自动变量截获只针对block内部使用的自动变量, 不使用则不截获, 因为截获的自动变量会存储于block的结构体内部, 会导致block体积变大.


int age = 10;

myBlock block = ^{

    NSLog(@"age = %d", age);

};

age = 18;

block();

输出为

age = 10

2> 对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的.

引用地址

__block int age = 10;

myBlock block = ^{

    NSLog(@"age = %d", age);

};

age = 18;

block();

输出为

age = 18

意味着对于第一种情况, 在block外部修改变量的值并不会应该block内部变量的值.而第二种情况则反之.

并且第一种情况block内部不允许修改变量的值, 第二种情况下可以. (有例外, 静态变量, 静态全局变量, 全局变量即使不使用__block修饰符也可以在block内部修改其值)

2. 截获对象

堆块作用域不同于栈块, 堆块可以超出其作用域地方使用, 所以堆块结构体内部会保留对象的强指针, 保证堆块在生命周期结束之前都能访问对象. 而对于__block对象为什么不会持有呢? 原因很简单, 因为__block对象会跟随block被复制到堆中, block再去引用堆中的__对象(后面会讲这个过程)..

栈块只能在当前作用域下使用, 所以其内部不会持有对象. 因为不存在在作用域之外访问对象的可能(栈离开当前作用域立马被销毁)

3 __block修饰符

__block int val = 10;

转换成

__Block_byref_val_0 val = {

    0,

    &amp;val,

    0,

    sizeof(__Block_byref_val_0),

    10

};

一个局部变量加上__block修饰符后竟然跟block一样变成了一个__Block_byref_val_0结构体类型的自动变量实例.

此时我们在block内部访问val变量则需要通过一个叫__forwarding的成员变量来间接访问val变量(下面会对__forwarding进行详解)

4. copy

block的copy操作究竟做了什么呢?

这里不得不提及__block变量的存储域

__block变量的配置存储域                     block从栈复制到堆时的影响

              栈                                             从栈复制到堆并被Block持有

              堆                                                        被Block持有

Block中使用__block变量

由上图可知, 对一个栈块进行copy操作会连同block与__block变量(不管有没有使用)在内一同copy到堆上, 并且block会持有__block变量(使用).

ps : 堆上的block及__block变量均为对象, 都有各自的引用计数

当然, 当block被销毁时, block持有的__block也会被释放

Block废弃和__block变量的释放

到这里我们能知道, 此思考方式与Objective-C的引用计数内存管理完全相同.

那么有人就会问了, 既然__block变量也被复制到堆上去了, 那么访问该变量是访问栈上的还是堆上的呢?? __forwarding 终于要闪亮登场了


复制__block变量

通过__forwarding, 无论实在block中, block外访问__block变量, 也不管该变量在栈上或堆上, 都能顺利地访问同一个__block变量.

什么时候我们需要手动对block调用copy方法

前面我们说到 : 要想在当前作用域以外的地方使用该block, 应该把该block从栈copy到堆上. 实际上, 在ARC下, 以下几种情况下, 编译器会帮我们把栈上的block复制到堆中

block作为函数返回值返回时

将block赋值给__strong修饰符id类型或block类型成员变量时

在方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递block时

理论上我们只有把block作为函数/方法的参数传入时才需要对block进行copy操作.

我们对不同地方的block调用copy会产生什么效果呢?

Block的类                                 副本源的配置存储域                      拷贝效果

_NSConcreteStackBlock                     栈                                      从栈拷贝到堆

_NSConcreteGlobalBlock           程序的数据区域                         什么也不做

_NSConcreteMallocBlock                    堆                                      引用计数增加

所以, 不管block是什么类型, 在什么地方, 用copy方法都不会引起任何问题.如下表格所示. 就算是反复多次调用copy方法, 如

Objective-C

1 blk = [[[[blk copy] copy] copy] copy];

该源码可解释如下 :

{

    block tmp = [blk copy]; // block被tmp持有

    blk = tmp; // block被tmp和blk持有

}

// tmp超出作用域, 其指向的block也被释放, block被blk持有

{

    block tmp = [blk copy]; // block被tmp和blk持有

    blk = tmp; // blk指向的旧block释放, 并强引用新block, 最终block被tmp和blk持有

}

// tmp超出作用域, 其指向的block也被释放, block被blk持有

...下面不断重复该过程

我们知道, 这只是一个循环的过程, block被tmp持有 -> block被tmp和blk持有 -> block被blk持有 -> block被tmp和blk持有 -> ……

由此可得知, 在ARC下该代码也没有任何问题.

总结 : 如果block需要给作用域外的地方使用, 但是你不知道需不需要copy, 那就copy吧. 反正不会错

5. block的循环引用

这部分相信大家都清楚怎样做能破环, 所以我在这就只简单说两句

MRC下用__block可以避免循环引用(原因见上面block特性之截获自动变量值)

ARC下用__weak来避免循环引用

这里需要提醒大家的是, 只有堆块(_NSConcreteMallocBlock)才可能会造成循环引用, 其他两种block不会

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