Block

block- 三种block__NSGlobalBlock__ NSStackBlock __NSMallocBlock __

自己测试的数据:
测试时在buildSetting中将ARC关掉,因为在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上。

    /****** __NSGlobalBlock__ (没有访问auto变量)******/

    // 什么参数也不引用
    NSLog(@"%@",^{NSLog(@"*******");} );
    
    // 引用全局变量或者全局静态变量
    NSLog(@"%@", ^{NSLog(@"%d, %d", a, global_i);});
    
    // 引用局部静态变量
    NSLog(@"%@", ^{NSLog(@"%d", static_local_k);});
    
    // 被强指针引用,并且只引用了全局变量或静态变量
    strongBlock = ^{NSLog(@"%d", a);};
    NSLog(@"%@", strongBlock);

    /******__NSStackBlock (访问了auto变量)******/

    // 只引用局部变量
    NSLog(@"%@", ^{NSLog(@"%d%d%d", a, static_local_k, static_global_j);});
    // 引用局部变量和全局变量(或静态变量)
    NSLog(@"%@", ^{NSLog(@"%d, %d", a, global_i);});
    
    
    /****** __NSMallocBlock (__NSStackBlock 执行copy操作)******/

    // 被强指针引用,并且引用了局部非静态变量
    strongBlock = ^{NSLog(@"%d%d", a, global_i);};
    NSLog(@"%@", strongBlock);
    
    // 被强指针引用,并且引用了局部非静态变量,和全局变量(或静态)
    strongBlock = ^{NSLog(@"%d%d", a, global_i);};
    NSLog(@"%@", strongBlock);

总结:

NSGlobalBlock(不持有对象)

以下总结基于MRC条件下,因为在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上:

  1. 只要不引用auto变量,那么这个block就是__NSGlobalBlock

NSStackBlock(不持有对象)

  1. 只要引入auto变量,那么这个block就是__NSStackBlock

NSMallocBlock(持有对象)

  1. __NSStackBlock 执行copy操作。

Block的副本:

Block的类 副本源的配置存储域 复制效果
__NSConcreteStackBlock 从栈复制到堆
__NSConcreteGlobalBlock 程序的数据区(全局区) 什么也不做
__NSConcreteMallocBlock 引用计数增加

不管block配置在何处,用copy方法复制都不会引起任何问题。在不确定时调用copy方法即可。(浪费CPU资源)

block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上(即执行copy操作),比如以下情况:

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • GCD中用到的block参数

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

无论是ARC还是MRC,

  1. NSStackBlock(即block在栈上)中访问对象类型的自动变量时,block不会持有该对象。
  2. NSMallocBlock(即block被copy到堆上)中访问对象类型的自动变量时,block会持有该对象,即该对象的引用计数会加1.

分析:
如果block被拷贝到堆上,
首先会调用block内部的copy函数,
然后copy函数内部会调用_Block_object_assign函数,
_Block_object_assign函数会根据auto变量的修饰符(__strong,__weak,__unsafe_unretained)做出相应的操作,类似retain(形成强引用,弱引用)

Screen Shot 2018-05-27 at 15.36.32.png

如果block从堆中移除:
会调用block内部的dispose函数,
dispose函数内部会调用_Block_object_dispose函数,
_Block_object_dispose函数会自动释放引用的auto变量,类似于release

在block内修改外部变量的方式

  1. 静态变量可以在block内直接修改
  2. 全局变量可以在block内直接修改
  3. 使用__block修饰符修饰auto变量,可以在block内修改

__block修饰符

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量,静态变量(static)
  • __block 会将变量包装成一个对象
Screen Shot 2018-05-27 at 17.01.04.png

__block的内存管理

  • 当block在栈上时,并不会对__block变量产生强引用
  • 当block被copy到堆上时,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数会对__block变量形成强引用
    Screen Shot 2018-05-27 at 18.27.16.png
  • 当block从堆中移除时,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的__block变量(release)
    Screen Shot 2018-05-27 at 18.30.17.png

Block被复制到堆上时,__block变量也被复制到堆上,被Block持有。
__block变量同时被两个Block持有时,会增加__block的引用计数。

总结-对象类型的auto变量和__block变量

Screen Shot 2018-05-27 at 18.47.45.png

被__block修饰的对象类型(对象类型😯)

  • 当__block变量在栈上时,不会对指向的对象产生强引用
  • 当__block变量被copy到堆上时,会调用__block变量内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据所指向对象的修饰符(__strong,__weak,__unsafe_unretained)做出相应的操作,类似retain(形成强引用,弱引用)(!!!:这里仅限于ARC时会retain,MRC时不会retain)

在MRC下:(如下图)block还没释放,person对象就dealloc了

Screen Shot 2018-05-27 at 20.03.30.png

__block变量结构体中__forwarding指针的作用

Screen Shot 2018-05-27 at 18.56.37.png

先给个结论:
__forwarding指向__block变量本身。当__block变量在栈上的时候,__forwarding指向__block在栈上的地址;当__block被复制到堆上后,栈上__block变量的__forwarding指向__block在堆上的地址,同样,堆上__block变量的__forwarding指针也指向它在堆上的地址,这样,当在函数中改变__block变量时,访问的便是同一个__block变量。细节看下面代码。

    typedef void (^blk_t)(void);
    
    __block int val = 0;
    
    
    blk_t blk = [^{++val;} copy];    // 可转化为++(val.__forwarding->val)
    
    ++val;     // 可转化为++(val.__forwarding->val)
    
    blk();
    NSLog(@"val:%d", val);

上述代码执行后val的值变为2


Screen Shot 2018-03-19 at 14.25.35.png

block中引用self时会不会造成循环引用

  • 首先考虑block的类型,__NSStackBlock__不会持有对象,所以不会造成循环引用;如果是__NSMallocBlock__,则会持有对象,所以会导致循环引用。

解决循环引用-ARC

三种方法:

Screen Shot 2018-05-27 at 20.52.32.png

解决循环引用-MRC

Screen Shot 2018-05-27 at 21.03.42.png

参考文章

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

推荐阅读更多精彩内容