iOS block 捕获外部变量以及注意点

参考文章:深入研究Block捕获外部变量和__block实现原理
做一些简单的总结说明:

(1)对于四种非对象变量:

  • 自动变量(局部变量)
  • 静态变量
  • 静态全局变量
  • 全局变量

首先: Block会捕获哪些变量?如果Block外面还有很多自动变量静态变量,等等,这些变量在Block里面并不会被使用到。那么这些变量并不会被Block捕获进来,也就是说并不会在构造函数里面传入它们的值。Block捕获外部变量仅仅只捕获Block闭包里面会用到的值,其他用不到的值,它并不会去捕获。

其次:全局变量静态全局变量可以在Block内值被修改是为什么呢?全局变量静态全局变量在执行Block语法的时候,它们被Block捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block捕获了它们进去之后,在Block里面进行++操作,Block结束之后,它们的值依旧可以得以保存下来。

然后:对于静态变量Block是如何捕获的呢?静态变量传递给Block是内存地址值,所以能在Block里面直接改变值。在执行Block语法的时候,Block语法表达式所使用的静态变量的地址是被保存进了Block的结构体实例中,也就是Block自身中。所以能够在Block 内部修改静态变量的值。

最后:为什么自动变量无法在Block内部修改值呢?类似静态变量,自动变量也是在执行Block语法的时候,被block捕获成为Block的结构体实例中,但是Block仅仅捕获了val的值,并没有捕获val的内存地址,所以在Block内部是无法修改自动变量的值。OC可能是基于这一点,在编译的层面就防止开发者可能犯的错误,因为自动变量没法在Block中改变外部变量的值,所以编译过程中就报编译错误。错误:Variable is not assignable(missing __block type specifier)
后面会说明。

总结一下在Block中改变变量值有2种方式,一是传递内存地址指针到Block中,二是改变存储区方式(__block)。

1 传递内存地址

对于对象变量,在被Block捕获后,在Block的结构体实例变量会增加一个指针,所以传递的是指针,所以成功改变了变量的值。

2 __block 改变存储方式。

_block修饰自动变量后,_block的变量也被转化成了一个结构体:__Block_byref_i_0,这个结构体有5个成员变量。

struct __Block_byref_i_0 {
  void *__isa;   指针
__Block_byref_i_0 *__forwarding; 指向自身类型的__forwarding指针
 int __flags; 标记flag
 int __size;大小
 int i; 变量值
};

MRC环境下,只有copy,_block才会被复制到堆上,否则,_block一直都在栈上,block也只是 _NSStackBlock,这个时候_forwarding指针就只指向自己了。
ARC环境下,一旦Block赋值就会触发copy,_block就会copy到堆上,Block也是_NSMallocBlock。ARC环境下也是存在_NSStackBlock的时候,这种情况下,_block就在栈上

在ARC环境下,Block也是存在__NSStackBlock的时候的,平时见到最多的是_NSConcreteMallocBlock,是因为我们会对Block有赋值操作,所以ARC下,block 类型通过=进行传递时,会导致调用objc_retainBlock->_Block_copy->_Block_copy_internal方法链。并导致 __NSStackBlock__ 类型的 block 转换为 __NSMallocBlock__ 类型

_forwarding指针初始化传递的是自己的地址,在执行Block的时候,堆上的Block会持有对象。 当我们把Block复制到堆上,堆上的Block也会持有_block.当Block释放的时候,_block没有被任何对象引用,也会被释放销毁。堆上的_forwarding指针也指向自己,只不过一个指针是_NSConcreteStackBlock,一个是_NSConcreteMallocBlock,两份_block ,栈上的_forwarding指针指向堆区的block,堆区的_forwarding指针指向原来的自己所以这样不管_block怎么复制到堆上,还是在栈上,都可以通过(i->__forwarding->i)来访问到变量值。

Block 分类

OC中,一般Block就分为以下3种,_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock。
先来说明一下3者的区别。

  • _NSConcreteStackBlock:
    只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
    StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。

  • _NSConcreteMallocBlock:
    有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制

  • _NSConcreteGlobalBlock:
    没有用到外界变量或只用到全局变量、静态变量的block为
    _NSConcreteGlobalBlock,生命周期从创建到应用程序结束。
    没有用到外部变量肯定是_NSConcreteGlobalBlock,这点很好理解。不过只用到全局变量、静态变量的block也是_NSConcreteGlobalBlock。

weakSelf StrongSelf 的使用

解决循环应用的问题一定要分析清楚哪里出现了循环引用,只需要把其中一环加上weakSelf这类似的宏,就可以解决循环引用。_weak的实现原理,在原对象释放之后,_weak对象就会变成null,防止野指针。所以就输出了null了。

那么我们怎么才能在weakSelf之后,block里面还能继续使用weakSelf之后的对象呢?

究其根本原因就是weakSelf之后,无法控制什么时候会被释放,为了保证在block内不会被释放,需要添加_strong。

在block里面使用的_strong修饰的weakSelf是为了在函数生命周期中防止self提前释放。strongSelf是一个自动变量当block执行完毕就会释放自动变量strongSelf不会对self进行一直进行强引用。

 __weak typeof(student) weakSelf = student;

    student.study = ^{
        __strong typeof(student) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"my name is = %@",strongSelf.name);
        });

    };

_block 与_weak 的区别

1._block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2._weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3._block对象可以在block中被重新赋值,_weak不可以。

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

推荐阅读更多精彩内容