iOS基础深入补完计划--Block相关原理探究

前文地址:《iOS基础深入补完计划》

在前文、我们提到了property中的关键字copy可以修饰block。
那么、block究竟有哪些特殊。
block该怎样声明
__block、__weak、__strong是如何工作的呢。
__block又是否可以解决循环引用呢?

首先、关于block的作用域

  • 总的来说。block默认是存档在栈中(作用于被限制在当前上下文(函数, 方法...))、可能被随时回收(比如代码块执行完毕、会将块内变量内存回收)。通过copy操作可以使其在堆中保留一份,、相当于一直强引用着。以此保证在执行block对象时、程序不会因为执行一个空对象而崩溃。
举个例子
   @interface ViewController ()
   
   @property (nonatomic, copy) void(^myblock)();
   
   @end
   
   @implementation ViewController
   
   - (void)viewDidLoad {
       [super viewDidLoad];
       // Do any additional setup after loading the view, typically from a nib.
   
       [self test];//当此代码结束时,test函数中的所有存储在栈区的变量都会被系统释放。
       self.myblock();
   
   }
   
   - (void)test {
       int n = 5;
       [self setMyblock:^{
           NSLog(@"%d",n);
       }];
   }
之后、把copy声明换成assign。

EXC_BAD_ACCESS、使用了一个野指针。


屏幕快照 2017-12-04 下午5.53.09.png
此外、我们还可以进一步确认崩溃的原因。
屏幕快照 2017-12-05 上午10.12.01.png
之后、到底是不是因为局部变量被释放、导致的崩溃?
  • 用成员变量试试。


    WechatIMG210.jpeg

    看来无论是否使用成员变量、内部的变量指针都会被释放


    WechatIMG211.jpeg
结论:
  • copy声明会把变量在block保存一份指针变量、指向原对象地址。保证block内部所引用变量不会被自动释放而造成崩溃。
  • assign声明的block、内部引用的变量(无论是否是局部变量)在出了赋值当前作用域之后、会被释放。导致崩溃。
除此之外:
  • block声明为成员变量、用strong修饰。也是没有问题的、作用和copy一样。
  • block声明为成员变量、不加任何修饰。效果也和copy/strong一样。
  • block声明为局部变量、不加任何修饰。效果也和copy/strong一样。

关于copy之后block的一些特性

会对其内部的使用对象引用计数+1

这也是为什么需要用__weak去修饰self以防止循环引用的原因。
更多的解释可以参阅《__weak与__block区别,深层理解两者区别》《深入分析 Objective-C block、weakself、strongself 实现原理》

原理总结下来就是

__weak会新开辟一个变量指针、指向被修饰的变量地址、但不会对该变量增加引用
当被修饰变量被释放、__weak变量指针依旧会指向原有变量地址、而该地址则会返回空。
所以需要用__strong在block内部修饰、让block内的变量不会在中途被释放掉、保证block内容一致性。

WechatIMG204.jpeg


默认情况下、内外部的变量数据不会互通。

   int a = 1;
   
   int (^backValue)(int) = ^(int num) {
       return num+a;
   };
   
   a += 1;//这次+=1不会被传递到block中a上。
   int b = backValue(1);
   NSLog(@"b-->%d", b);
  //输出结果b-->2
  • 当然、我们可以__block来修饰这个a、使其在block内部的指针也可以被修改


    屏幕快照 2017-12-04 下午6.11.00.png
  • 为什么用__block修饰就可以修改外部的变量、或者将外部变量的修改传递进block内了?

先写结论、不愿意细看可以跳过。

  • 无论是普通变量、还是OC变量。在不使用__block的情况下、无法对其本身进行修改。

因为二者、都只会在block执行代码中copy一份值/指向该对象地址的指针。即使修改了这个值/指针指向、也无法对外部造成影响。所以编译器干脆就禁止这个操作了(虽然你可以通过直接操作指针的方式修改、但是被修改的依旧只是block内部的指针变量而已)

  • OC对象在不使用__block修饰的情况下、可以修改其属性。

自身的指针会被捕获到block代码块中、借此操作该指针指向地址所属对象的属性。(这也是为什么在block里可以修改self.xxx属性的原因)。

  • OC对象以及普通变量在使用__block修饰的情况下、可以对自身进行修改。

会将原变量的指针从栈移动至堆区。由block对象进行捕获保存、并借此进行修改。


以下是相关分析

这里有一篇文章、借助编译后的block阐释了__block的工作原理
《你真的理解__block修饰符的原理么》

  • 简而言之、block捕获普通变量其实将变量的值捕获到了block执行块中。这个已经与原变量无关。(由于无法直接获得原变量、技术上无法实现修改、所以编译器直接禁止了)。
    屏幕快照 2017-12-04 下午6.36.49.png

    block内部的指针指向、与外部完全分离
  • __block声明的变量、在被block捕获时。会被block以指针变量的形式保存在自身处于堆中结构体内。同时、原对象的指针地址也被修改到了堆中。既然变量指针内存地址相同、就代表是同一个指针、自然可以同步修改。

__block之后的效果

具体可见:《iOS中__block 关键字的底层实现原理》《一篇文章看懂iOS代码块Block》

  • 当然、还有个其他情况。就是OC对象的属性。

当block执行代码需要捕获的是OC对象时、block会自动在block代码块中copy一个对象指针(普通对象是copy值)、指向该对象当前地址。
就会出现如下情况


屏幕快照 2017-12-04 下午6.57.07.png
不过、并不允许你为这个存在栈中的变量指针直接重新指定一个新的指向。除非使用__block将这个对象的地址直接移动到堆中。

此外、还做了另外一个实验。


屏幕快照 2017-12-04 下午8.18.27.png
这也证明、即便是OC对象也仅仅是捕获了当前指针的指向。如果新生成了一个对象修改了外部的对象的指针指向地址、里面继续修改也没用了。

关于block的循环引用

  • __block是否可以防止循环引用。

可以、只需要在block内部根据需要将变量置空。缺点是需要手动调用一次block

 __block id tmp = self;
 self.block = ^{
     tmp = nil;
 };
 self.block();
需要注意的是。在tmp被block的捕获的时候、和__weak一样是不会增加tmp的引用计数的。之所以要手动释放、是因为在__block id tmp = self;时、tmp对self进行了一次强引用。需要消除的是这次引用。而且、当tmp被置nil之后、下次block调用将会得到一个空地址。但原对象不受此影响。
WechatIMG207.jpeg

最后

本文主要是自己的学习与总结。如果文内存在纰漏、万望留言斧正。如果不吝赐教小弟更加感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容