Block

一.block的本质

block本质是一个OC对象。封装了函数调用地址和函数调用环境(参数)


block本质

二.block的变量捕获(capture)

  • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

  • 自动变量:局部变量默认会在前面加上auto修饰词,叫自动变量。

  • 捕获原则如下:


    block的变量捕获.png
  • 捕获到block内部:在block内部会新增一个局部变量来接收传进来的值


    block内部接收
  • 为什么局部变量需要捕获呢,全局变量就不需要?
    因为block的调用和block的声明不在一个函数里面话。局部变量就获取不到了。为了在另一个函数里面可以调用到局部变量,所以需要捕获。
    而全局变量在哪个函数都可以调用到,所以就不需要捕获

  • 为什么auto修饰的是值传递呢?
    auto修饰的是局部变量,出了方法会被释放掉,进行值传递就不会因为释放掉而出错。静态变量出了方法不会被释放。


    捕获局部变量的原因

三.block的类型

1. block类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )

2. block内存分配

block内存分配

3. 不同类型的block

image.png
  • 每一种类型的block调用copy后的结果如下所示


    block 调用copy之后
  • 函数调用完毕,栈上面的block会销毁。栈内存的数据就是垃圾数据。调用这个block,里面打印出来的内容就不确定

  • 堆上面的block不会销毁,除非调用release

4.block的copy(block怎么从栈复制到堆上面)

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时(如下图)
  • 将block赋值给__strong指针时(赋值给强指针的时候)(如下图)
  • block作为Cocoa API中方法名含有usingBlock的方法参数时(如下)
  • block作为GCD API的方法参数时(如下)

注意,是对栈上的block进行以上操作才可以

block作为返回值.png
堆上的block进行强引用.png

堆上的block进行强引用.png

上面使用@property进行修饰,block中用了atuo变量,block在栈上,所以让栈上的block复制到堆上。
如果是未使用atuo变量,block是global类型的,无法复制到堆上。

复制到堆上的block
  • MRC下block属性的建议写法
    @property (copy, nonatomic) void (^block)(void);

  • ARC下block属性的建议写法
    @property (strong, nonatomic) void (^block)(void);//因为在arc下强引用栈上面的block,会对block进行copy操作,从栈上复制到堆上
    @property (copy, nonatomic) void (^block)(void);

为什么mrc下建议用copy、arc下建议用copy、strong呢?
因为对于堆上的block。
mrc下把block进行copy会放到堆上。arc下把blcok进行强引用,会复制到堆上。
放到栈上的block出了方法会被释放掉,堆上的不会。所以这么做。

5.block与对象类型的auto变量

上面讲到,block使用auto变量就是栈类型的。那么block内部访问的auto变量是对象类型,block内部对这个对象是强引用还是弱引用呢?

当block内部访问了对象类型的auto变量时:

如果block是在栈上,将不会对auto变量产生强引用
(参考图【栈上面block实例】)

如果block被拷贝到堆上

  • 会调用block内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会根据auto变量的修饰符(__strong、?>__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
    (参考【堆上的blcok强引用】、【堆上的blcok弱引用】)

如果block从堆上移除

  • 会调用block内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数
  • _Block_object_dispose函数会自动释放引用的auto变量(release)

弱引用的对象,出了对象所在的自动释放池就释放了。
强引用的对象,和block一起销毁。block出了所在的block释放。具体看下面:

函数调用时机
栈上面block实例
堆上的blcok强引用

堆上的blcok弱引用

四.__block

  • __block的作用:用于解决block内部无法修改auto变量值的问题
  __block int age = 10;
    MyBlock block = ^(){
    
        age = 20;
        NSLog(@"%d",age);
    };
    
    block();
    __block Person * person = [[Person alloc] init];    
    
    MyBlock block1 = ^(){
        
        person = [[Person alloc] init];
    };  
  • 使用注意:__block不能修饰全局变量、静态变量(static)

  • __block本质/内部原理:
    __block修饰的变量会被包装成一个对象。Block对象内部会有
    包装好的对象的指针。 所以我们要修改__block修饰的变量时,
    Block内部直接去找这个指针,找到要修改的值。


    __block内部原理

__block内存管理

  • 当block在栈上时,并不会对__block变量产生强引用

  • block被copy到堆时
    1.会调用block内部的copy函数
    2.copy函数内部会调用_Block_object_assign函数
    3._Block_object_assign函数会对__block变量形成强引用(retain)

如下:
block0、block1在栈上的时候同时持有__block修饰的变量。
block0复制到堆上的时候,__block修饰的变量也复制到堆上、被强引用了。


image.png

block1复制到堆上的时候,因为持有的__block已经在堆上了,就继续指向原本的__blcok修饰的变量。


image.png
  • 当block从堆中移除时
    1.会调用block内部的dispose函数
    2.dispose函数内部会调用_Block_object_dispose函数
    3._Block_object_dispose函数会自动释放引用的__block变量(release)
image.png
image.png

内存管理总结

变量类型 是否捕获到内部 访问方式
局部 auto修饰 值传递
局部 static修饰 指针传递
全局变量 直接访问
栈类型block 堆类型block
auto修饰的对象 弱引用 根据所指向对象的修饰符形成强引用(retain)或者弱引用
__block修饰的对象 弱引用 根据所指向对象的修饰符形成强引用(retain)或者弱引用
__block修饰的变量 弱引用 强引用

上面三者:auto修饰的对象、__block变量、__block修饰的对象:

当block拷贝到堆上时,都会通过copy函数来处理它们
当block从堆上移除时,都会通过dispose函数来释放它们

__block的__forwarding指针

上面有提到,__block修饰的变量会被包装成一个对象。
这个对象里面有__forwarding指针,是指向自身的_isa指针。


__block的_forwarding指针

当block从栈上复制到堆上,__block生成的对象也会复制到堆上。栈上的__block内部_forwarding指针会指向堆上的__block生成的对象。如下图:


image.png

五.循环引用

解决循环引用问题 - ARC

  • 用__weak、__unsafe_unretained解决


    循环引用
解决方法
  • 用__block解决(必须要调用block;)
    必须:Block内部把引用的对象设置为nil,并且调用block。
    因为__block生成的对象内部强引用对象,需要打破这个强引用。
    __block解决方法1
解决原理

因为上面强引用了,所以直接采用弱引用,发现也解决循环引用的问题了:


__block解决方法2

解决循环引用问题 - MRC

  • 用__unsafe_unretained解决(mrc里面没有_weak)


    __unsafe_unretained解决
  • 用__block解决 (mrc下不会对__block的对象进行强引用,所以mrc下不需要置位nil)

    image.png

六.面试问题

  • block的原理是怎样的?本质是什么?

封装了函数调用以及调用环境的OC对象

  • __block的作用是什么?有什么使用注意点?

解决block内部无法修改auto变量的问题。把__block修饰的变量封装成一个对象,放在block内部。

注意点:
__block不能修饰全局变量、静态变量(static)
__block会进行相应的内存管理;(比如堆上的block,会根据__block对象的修饰符进行强引用或者弱引用)

mrc环境下,不会对__block修饰的变量进行强引用;

  • __block本质/内部原理:

__block修饰的变量会被包装成一个对象。
Block对象内部会有包装好的对象的指针。
当我们要修改__block修饰的变量时,Block内部直接去找这个指针,找到要修改的值。

__block内部原理
  • block的属性修饰词为什么是copy?使用block有哪些使用注意?
    block一旦没有进行copy操作,就不会在堆上;放在堆上是为了控制block的生命周期。
    使用注意:循环引用问题

  • block在修改NSMutableArray,需不需要添加__block?
    为数组增删改的时候不需要。
    修改指针的对象的时候需要。

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

推荐阅读更多精彩内容