Block

作者: 6灰太狼9 | 来源:发表于2017-04-06 18:09 被阅读11次

    block是带有局部变量的匿名函数,差不多跟C语言中的函数指针类似,可以当做参数传来传去,而且可以没有名字。同时也是oc对象类型,所以可以吧Block赋值给一个变量,也可以存储在NSArray NSDictionary这样的容器中。

    一.Block捕获外部变量实质#

    oc中的变量有一下几种

    • 自动变量
    • 函数参数
    • 静态变量
    • 静态全局变量
    • 全局变量
      而Block的外部变量有四种
    • 自动变量
    • 静态变量
    • 静态全局变量
    • 全局变量
      如下图我们看到,自动变量i要添加__Block.
    自动变量在block中改变报错

    下图的执行结果为block外 i = 101,j = 201,m = 301,n = 401 block内 i = 100,j = 202,m = 302,n = 402为什么先打印外在打印内,这个就不用说了吧!

    03975684-C188-44F7-A758-E2F8A75F34DA.png
    我们可以看到自动变量i在block中获取的值还是原始的值。而其他变量的却已经增加了!

    那么这里就有两个问题
    1.为什么block中的自动变量不加__block不容许修改。
    2.为什么自动变量的值没有增加,而其他几个变量的值是增加的?
    自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。 对自定变量来说,Block内部和外部的变量实际上是两种不同的存在:前者是Block内部结构体的一个成员变量,后者是在栈区里的临时变量。 总结一下在Block中改变变量值有2种方式,一是传递内存地址指针到Block中(自动变量nsmutablestring,nsmutablearray 就可以把内存地址指针传到block中),二是改变存储区方式(__block)。

    对于对象来说,
    在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
    而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象。

    二.block的分类#

    oc中block一般分为_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock三种。

    1. _NSStackBlock 存储在栈上
    2. _NSGlobalBlock 存储在全局数据区域(和全局变量一样)
    3. _NSMallocBlock 存储在堆上
      没有引用自动变量或者在全局作用域的Block为_NSGlobalBlock,其他常见的基本上都是_NSMallocBlock ,因为ARC下,block 类型通过=进行传递时,会导致调用objc_retainBlock->_Block_copy->_Block_copy_internal方法链。并导致 NSStackBlock 类型的 block 转换为 NSMallocBlock 类型。

    三.block中循环引用的解决和原理#

    self.myBlock = ^{
            self.name = @"aa";
    }; 存在循环引用   self持有block   在block又对self就行了retain,持有了self
    

    解决方法

    __weak __typeof(self) weakself = self;
    self.myBlock = ^{
            weakself.name = @"aa";
     };这样就可以block对self的引用变成了弱引用。
    

    这样循环问题是解决了,但是又会导致一个新的问题,假如在block有一个耗时操作,在这个过程self被销毁了,而weakself也会随着self的销毁而销毁,block又要对weakself进行某些操作,这是拿到的weakself就是nil了。
    解决方法

    __weak __typeof(self) weakself = self;
    self.myBlock = ^{
            __strong __typeof(self) strongself = weakself;
            strongself = @"aa";
     };执行bock时,即使self被销毁,我们还有self的一份拷贝(我自己的理解)
    

    strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放,就需要加入strongSelf。block执行完后这个strongSelf 会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

    那么__weak ,__strong (所有权修饰)的原理是什么
    在ARC环境下,id类型和对象类型和C语言其他类型不同,类型前必须加上所有权的修饰符。
    所有权修饰符总共有4种:
    1.__strong修饰符
    2.__weak修饰符(如果__weak引用的原对象如果被释放了,那么对应的__weak对象就会被指为nil。)
    3.__unsafe_unretained修饰符
    4.__autoreleasing修饰符
    一般我们如果不写,默认的修饰符是__strong。

    相关文章

      网友评论

          本文标题:Block

          本文链接:https://www.haomeiwen.com/subject/cemqattx.html