美文网首页
Obj-C高级编程--Blocks

Obj-C高级编程--Blocks

作者: 南京小伙 | 来源:发表于2017-08-30 17:04 被阅读0次

    Blocks

    Blocks

    Blocks 是带有局部变量的匿名函数

    截取自动变量值

    int main()

    {

                  int dmy = 256;

                  int val = 10;

                  const char *fmt = "val = %d\n";

                  void (^blk)(void) = ^void(void){printf(fmt, val);};

                  val = 2;

                  fmt = "There value were changed. val = %d\n";

                  blk(); // 打印:val = 10

                   return 0;

    }

    Block语法表达式使用fmtvalBlocks中,Block表达式截获所使用的自动变量的值。即保存该自动变量的瞬间值。

    因为Block表达式保存了自动变量的值,所以在执行Block语法后,即使改写Block中使用的自动变量的值也不会影响Block执行时自动变量的值。

    __block 说明符

    局部变量附加了 __block 说明符,就能在 Block 内赋值。

    C 语言数组加了 __block 仍是报错。因为 Block 中获取变量是赋值,C 数组不能直接赋值。

    char test1[10];

    char test2[10] = test1; // 报错

    Block 的实质

    使用clang -rewrite-objc +源代码文件名 看 C 语言源代码。

    int main()

    {

               void (^blk)(void) = ^void(void){printf("Block\n");};

               blk();

                return 0;

    }

    通过 clang 转为:

    struct __block_impl {

               void *isa;

               int Flags;

               int Reserved;

               void *FuncPtr;

    };

    struct _main_block_impl_0 {

                struct __block_impl impl;

                struct _main_block_desc_0 *Des;  //结构体声明

    // 构造函数

                __main_block_imp_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { 

                            impl.isa = &_NSConcreteStackBlock; //栈区

                            impl.Flags = flags;

                            impl.FuncPtr = fp; 

                            Desc = desc;

                }

    }

    static void __main_block_func_0(struct __main_block_impl_0 * __cself) {

                printf("Block\n");

    }

    static struct __main_block_desc_0 {

                 unsigned long reserved;

                 unsigned long Block_size;

    } __main_block_desc_0_DATA = {

                  0,

                  sizeof(struct __main_block_impl_0);

    };

    int main()

    {

                 void (*blk)(void) = (void (*)(void))&__main_block_impl0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

                 (void (*)(struct __block_impl *))(stuct __block_impl *)blk)->FuncPtr)((struct __block_impl *) blk);

    }

    逐步分解,看源代码中的 Block 语法。

    ^void(void){  printf("Block\n");  };

    变化后的源代码中的表达式。

    static void __main_block_func_0(struct __main_block_impl_0 * __cself)

    {

              printf("Block\n");

    }

    通过Blocks使用的匿名函数实际上被作为简单的 C 语言函数来处理。另外,根据Block 语法所属的函数名(此处为mian)和该Block 语法在函数出现的顺序值来给变换的函数命名。

    参数__cself为指向 Block 值得变量。 即 __cself 是__main_block_imp_0结构体的指针。

    struct __main_block_impl_0 {

             struct __block_impl impl;

             struct __main_block_desc_0 *desc;

    }

    第一个成员变量 impl,_block_impl 结构声明

    struct __block_impl {

              void *isa;

              int Flags;

              int Reserved;

              void *FuncPtr;

    }

    第二个成员变量 Desc 指针,_main_block_impl_0 结构声明

    struct __main_block_desc_0 {

                 unsigned long reserved;

                 unsigned long Block_size;

    }

    _main_block_impl_0 结构体的构造函数。

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {

              impl.isa = &_NSConcreteStackBlock;

              impl.Flags = flags;

              impl.FuncPtr = fp;

              Desc = desc;

    }

    构造函数的调用:

    void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

    去掉转化部分

    struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);

    struct __main_block_impl_0 *blk = &tmp;

    源代码将__main_block_impl_0结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例的指针,赋值给__main_block_impl_0的结构体指针类型的变量 blk。

    void (^blk)(void) = ^{printf("Block\b");};

    它等同于将 __main_block_impl_0 结构体实例的指针赋给变量 blk

    源代码的中的Block就是__main_block_impl_0结构体类型的局部变量,即栈上生成的__mainblockimpl_0结构体实例。

    结构体实例的构造参数

    __main_block_impl_0(__main_block_func_0, &__main_block_Desc_0_DATA);

    第一个参数是由 Block 语法转换的 C 语言函数指针

    第二个参数是作为静态全局变量的初始化的 __main_block_desc_0 结构体实例指针

    __main_block_desc_0 结构体实例的初始化部分

    static struct __main_block_desc_0 __main_block_desc_0_DATA = {

            sizeof(struct __main_block_impl_0)

    }

    __main_block_impl_0 结构体实例的大小,进行初始化。

    该结构体初始化如下:

    isa = &_NSConcreteStackBlock; // 该类 Block 放置于 Stack

    Flags = 0;

    Reserved = 0;

    FuncPtr = __main_block_func_0;

    Desc = &__main_block_desc_0_DATA;

    使用改 Block 的部分

    block()

    转化源代码

    ((void (*)(struct __block_impl *))(struct __block_impl *)blk->FuncPtr)((struct __block_impl *)blk);

    去掉转换部分

    (*blk->impl.FuncPtr)(blk); // 使用函数指针调用函数

    截取自动变量值

    int main()

    {

             int dmy = 256;

             int val = 10;

             const char *fmt = "val = %d\n";

             void (^blk)(void) = ^void(void){printf(fmt, val);};

              val = 2;

             fmt = "There value were changed. val = %d\n";

             blk(); // 打印:val = 10

             return 0;

    }

    clang 转换

    struct __main_block_impl_0 {

                struct __block_impl impl;

                struct __main_block_desc_0 *Desc;

                const char *fmt;//

                 int val; // 将 Block 内部使用的变量追加到了结构体中

                 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {

                        impl.isa = &_NSConcreteStackBlock;

                        impl.Flags = flags;

                        impl.FuncPtr = fp;

                        Desc = desc;

                  }

    };

    static void __main_block_func_0(struct __main_block_impl_0 *__cself)

    {

               const char *fmt = __cself->fmt;

                int val = __cself->val;

                printf(fmt,val);

    }

    static struct __main_block_desc_0 {

               unsigned long reserved;

               unsigned long Block_size;

    } __main_block_desc_0_DATA = {

               0,

              sizeof(struct __main_block_impl_0)

    };

    int main()

    {

               int dmy = 256;

               int val = 10;

               const char *fmt = "val = %d\n";

               void (*blk)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);

               return 0;

    }

    Block 语法表达式中使用的自动变量作为成员变量追加到了 __main_block_impl_0,Block 语法表达式中没有使用的自动变量没有被追加。

    执行 Block 语法时的自动变量 fmt 和 val 来初始化 __mainblockimpl_0 结构体实例。

    初始化如下:

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = 0;

    impl.FuncPtr = __main_block_func_0;

    Desc = &__main_block_desc_0_DATA;

    fmt = "val = %d\n";

    val = 0;

    所谓“截获自动变量值”意味着在执行 Block 语法时,Block 语法表达式所使用的自动变量被保存到 Block 的结构体实例(即 Block 自身)中,在截获自动变量时,将值传递给结构体的构造函数进行保存。

    __block 说明符

    Block 中所使用的被截获的自动变量仅截获自动变量的值。Block 中使用自动变量后,在 Block的结构体实例中重写该自动变量也不会改变原来截获的自动变量

    改变自动变量的值有两种方法:第一种:C 语言中有一个变量,允许 Block 改写值。(静态变量,静态全局变量,全局变量)

    虽然 Block 语法的匿名函数部分简单地变换为了 C 语言函数,但从这个变化的函数中访问静态全局变量/全局变量并没有任何改变,可直接使用。 但是静态变量的情况下,转换后的函数原本就设置在含有 Block 语法的函数外,所以无法从变量作用域访问。

    int global_val = 1;

    static int static_global_val = 2;

    int main()

    {

            static int static_val = 3;

            void (^blk)(void) = ^ {

                    global_val *= 1; 

                     static_global_val *= 2;

                     static_val *= 3; 

             };

              return 0;

    }

    该源代码转换后:

    int global_val = 1;

    static int _global_val = 2;

    struct __main_block_impl_0 {

             struct __block_impl impl;

             struct __main_block_desc_0* Desc;

             int *static_val; // Block 中使用的自动变量

             __main_block_impl_0(void *fp, struct __main_block_desc_0 * desc, int *_static_Val, int flags=0) : static_val(_static_val) {

                      impl.isa = &_NSConcreteStackBlock;

                      impl.Flags = flags;

                      impl.FuncPtr = fp;

                      Desc = desc;

               }

    };

    static void __main_block_func_0(struct __main_block_impl_0 * __cself) {

                 int *static_val = __cself->static_val; // 使用静态变量 static_val 的指针对其进行访问,将静态变量 static_val 的指针传递给 __main_block_impl_0 结构体的构造函数并保存。这是超出作用域使用变量的最简单的方法。

               global_val *= 1;

               static_global val *= 2;

               (*static_val) *= 3;

    }

    static struct __main_block_desc_0 {

                unsigned long reserved;

               unsigned long Block_size;

    } __main_block_desc_0_DATA = {

               0,

               sizeof(struct __main_block_impl_0)

    }

    int main()

    {

              static int static_val = 3;

              blk = &__main_block_impl_0(__main_block_func_0, &__main_Block_desc_0_DATA, &static_val);

              return 0;

    }

    使用静态变量static_val 的指针对其进行访问,将静态变量static_val 的指针传递给 _mainblockimpl0 结构体的构造函数并保存。这是超出作用域使用变量的最简单的方法。

    第二种方法使用“__block 说明符”。

    __block int val = 10;

    void (^blk)(void) = ^void(void){val = 1;};

    clang 编译后

    //__block修饰的变量结构体

    struct __Block_byref_val_0 {

            void *_isa;

            __Block_byref_val_0 *__forwarding; // 指向结构体自己的 __Block_byref_val_0 结构体指针

            int __flags;

            int __size;

            int val; // 相当于自动变量的成员变量。值为10

    };

    struct __main_block_impl_0 {

           struct __block_impl impl;

           struct __main_block_desc_0 *Desc;

           __Block_byref_val_0 *val;// __block修饰的变量结构体指针      

           __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,         __Block_byref_val_0 *val, int flags=0) : val(_val->__forwarding) {

                  impl.isa = &_NSConcreteStackBlock;

                  impl.Flags = flags;

                  impl.FuncPtr = fp;

                 Desc = desc;

    }

    };

    // Block 的 __main_block_impl_0 结构体实例持有指向 __block变量的

    __Block_byref_val_0 结构体实例变量的指针。

    static void __main_block_func_0(struct __main_block_impl_0 *__cself)

    {

             __Block_byref_val_0 *val = __cself->val;

             (val->__forwarding->val) = 1;

    }

    static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src)

    {

             _Block_object_assign(&dst->val, src-val, BLOCK_FIELD_IS_BYREF);

    }

    static void __main_block_dispose_0(struct __main_block_impl_0 *src)

    {

            _Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);

    }

    static struct __main_block_desc_0 {

           unsigned long reserved; 

           unsigned long Block_size;

           void (*copy)(struct __main_block_impl_0 *,struct __main_block_impl_0 *);

           void (*dispose)(struct __main_block_impl_0*);

    } __main_block_desc_0_DATA = {

            0,

            sizeof(struct __main_block_impl_0),

            __main_block_copy_0,

            __main_block_dispose_0

    };

    int main()

    {

             __Block_byref_val_0 val = {

                      0,

                     &val,

                     0,

                     sizeof(__Block_byref_val_0),

                     10

    };

            blk = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000);

            return 0;

    }

    加上__block的自动变量变为了结构体实例,_block变量也同 Block 一样变成__Block_byref_val_0结构体类型的自动变量,即栈上生成的__Block_byref_val_0结构体实例。

    __Block_byrefval0结构体实例的成员变量__forwarding持有指向该实例自身的指针。通过成员变量__forwwarding访问成员变量 val。

    Block 存储域

    Block 转换为 Block 的结构体类型的自动变量,__block 变量转换为 __block 变量的结构体类型的自动变量。

    impl.isa有三种类型,即 Block 存储位置有三种。

    _NSConcreteStackBlock // 栈区

    _NSConcreteGlobalBlock // 数据区

    _NSConcreteMallocBlock // 堆区

    记述全局变量的地方使用 Block 语法时,生成的 Block 为 NSConcreteGlobalBlock 类对象。

    void (^blk)(void) = ^{printf("Global Block\n");};

    int main()

    {

    impl.isa = &_NSConreteGlobalBlock;此 Block 用结构体实例设置在程序的数据区域中。因为在使用全局变量的地方不能使用自动变量,所以不存在对自动变量进行截获。由此 Block 用结构体实例的内容不依赖于执行时的状态,所以整个程序中只需一个实例。因此将 Block 用结构体实例设置在与全局变量相同的数据区域中即可。

    只要 Block 不截获自动变量,就可以将 Block 用结构体实例设置在程序的数据区域。

    存储在 _NSConreteStackBlock 类对象:

    记述全局变量的地方有 Block 语法时。

    Block 语法的表达式中不适用应截获的自动变量时。

    配置在全局变量上的 Block,从变量作用域外也可以通过指针安全的使用。

    但设置在栈上的 Block,如果其所属的变量作用域结束,该 Block 就被废弃。由于 __block 也配置在栈上,同样的,如果其他所属的变量作用域结束,则该 __变量也会被废弃。

    Blocks 提供了将 Block 和 __block 变量从栈上赋值到堆上的方法来解决这个问题。将配置在栈上的 Block 复制到堆上,这样即使 Block 语法记述的变量作用域结束,堆上的 Block 还可以继续存在。

    复制到堆上的 Block 将 _NSConreteMallocBlock 类对象写入 Block 用结构实例的成员变量 isa。impl.isa = _NSConreteMallocBlock

    而 __block 变量用结构体成员变量 __forwarding 可以实现无论 __block 变量配置在栈上还是堆上时都能正确的访问 __block 变量。有时在 __block 变量配置在堆上的状态下,也可以访问栈上的 __block 变量。在此情形下,只要栈上的结构体实例成员变量 __forwarding 指向堆上的结构体实例。不管堆上还是栈上都能访问。

    将 Block 作为函数返回值返回时,编译器会自动生成复制到堆上的代码。(ARC)

    typedef int (^blk_t)(int);

    blk_t func(int rate)

    {

           return ^(int count){return rate * count;}; // 返回的 block 配置在栈上,即程序执行中从该函数返回函数调用时变量作用域结束,栈上的 block 也被废弃。(ARC无效时)

    }

    //ARC 有效时

    blkt func(int rate)

    {

             blk_t tmp = &__func_block_impl_0(__func_block_func_0, &__func_block_desc_0_DATA, rate);

            tmp = objc_retainBlock(tmp); // 实际上就是 Block_copy函数,将栈上的 Block 复制到堆上

            return objc_autoreleaseReturnValue(tmp); // 注册到 autoreleasepool 中。

    }

    做题传送门(关于 ACR和 MRC 中使用 Block 会不会崩溃)~传送门

    __block 变量存储域

    使用 __block 变量的 Block 从栈复制到堆上时,__block 变量也会受到影响。

    若一个 Block 中使用 __block 变量,则当该 Block 从栈复制到堆时,使用的所有 __block 变量也必定配置在栈上。这些 __block 变量也全部被从栈复制到堆。此时,Block 持有 __block 变量。

    若配置在堆上的 Block 被废弃,那么它所使用的 __block 变量也就被释放(它所持有的 __block 变量会被释放)。

    通过 Block 的复制,__block 变量也从栈复制到堆。此时,可以同时访问栈上的 __block 变量和堆上 __block 变量。

    __block int val = 0;

            void (^blk)(void) = [^{

            ++val;// 使用了堆上的 __block 变量

    } copy]; // 利用 copy 将 Block 和 __block 变量复制到堆上

    ++val; // 使用了栈上的 __block 变量

    blk();

    NSLog(@"%d", val);

    使用 val 都可以转换为

    ++(val.__forwarding->val);

    栈上的 __block 变量用结构体实例在 __block 变量从栈复制到堆上时,会将成员变量 __forwarding 的值替换为复制目标堆上的 __block 变量用结构体实例的地址。

    截获对象

    附有 __strong 修饰符的赋值目标变量作用域立即结束,因此对象被立即释放并废弃。

    {

            id array = [[NSMutableArray alloc] init];

    } // 出了作用域对象被立即释放

    typedef int (^blk_t)(int);

    blk_t blk;

    {

           id array = [[NSMutableArray alloc] init];

           blk = [^(id obj) {

                  [array addObject:obj];

                  NSLog(@"array count = %ld", [array count]);

            } copy]; // 使用 copy 才持有使用的__block 变量和 对象自动变量

    }

    blk([[NSObject alloc] init]);

    blk([[NSObject alloc] init]);

    blk([[NSObject alloc] init]);

    其执行结果:

    array count = 1

    array count = 2

    array count = 3

    /* Block 用结构体 / 函数部分 */

    struct __main_block_impl_0 {

         struct __block_impl impl;

          struct __main_block_desc_0 *Desc;

           id __strong array;

           __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id __strong _array, int flags=0) : array(_array) {

                 impl.isa = &_NSConcreteStackBlock;

                impl.Flags = flags;

                impl.FuncPtr = fp;

              Desc = desc;

          }

    }

    static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {

             id __strong array = __cself->array;

             [array addObject:obj];

             NSLog(@"array count = %ld", [array count]);

    }

    static void __main_block_copy_0(struct __main_block_impl_0 dst, struct __main_block_impl_0 *src)

    {

         _Block_object_assign(&dst->array, src->array, BLOCK_FIELD_IS_OBJECT);

    }

    static void __main_block_dispose_0(struct __mian_block_impl_0 *src)

    {

             _Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT);

    }

    static struct __main_block_desc_0 {

           unsigned long reserved;

           unsigned long Block_size; 

           void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0 *);

           void (*dispose)(struct __main_block_impl_0 *);

    } __main_block_desc_0_DATA = {

            0,

            sizeof(struct __main_block_impl_0),

            __main_block_copy_0;    \用来管理结构体

           __main_block_dispose_0; /生命周期,初始化和废弃。

    }

    /* Block 语法,使用 Block 部分 */

    blk_t blk;

    {

            id __strong array = [[NSMutableArray alloc] init];

             blk = &_main_block_impl_0(__main_block_func_0, &_main_block_desc_0_DATA, array, 0x22000000);

            blk = [blk copy];

    }

    (*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);

    (*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);

    (*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);

    发现 Block 用的结构体中附有 __strong 修饰符的成员变量。

    C 语言结构体不能含有附有 __strong 修饰符的变量。因为编译器不知道应核实进行 C 语言结构体的初始化和废弃操作,不能很好的管理内存。

    但是 Objective-C 的运行时库能够准确把握 Block 从栈复制到堆以及堆上 Block 被废弃的时机,因此 Block 用结构体中即使含有附有 __strong 修饰符或 __weak 修饰符的变量,也可以恰当地进行初始化和废弃。

    为此需要使用在 __main_blockdesc0 结构体中增加的成员变量 copy 和 dispose,以及作为指针赋值给成员变量的 __main_block_cop\y0 函数和 __main_block_dispose0 函数

    __main_block_copy_0 函数使用 _Block_object_assign 函数将对象类型对象赋值给 Block 用结构体的成员变量array 中并持有该对象。

    static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src)

    {

             _Block_object_assign(&dst->array, src->array, BLOCK_FIELD_IS_OBJECT);

    }

    _Block_object_assign 函数调用相当于 retain 实例方法的函数,将对象赋值在对象类型的结构体成员中。

    __main_blockdispose0 函数使用 _Block_object_dispose 函数,释放赋值在 Block 用结构体成员变量 array 中的对象。

    static void __main_block_dispose_0(struct main_block_impl_0 *src)

    {

           _Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT)

    }

    _Block_object_dispose 函数调用相当于 release 实例方法的函数,释放赋值在对象类型的结构体体成员变量中的对象。

    在 Block 从栈复制到堆时(copy),以及堆上的 Block 被废弃时(dispose)会调用这些函数。

    将栈上的 Block 复制到堆上:

    1.调用 Block 的 copy 实例方法时

    2.Block 作为函数返回值返回时(ARC)

    3.将 Block 赋值给附有 __strong 修饰符 id 类型的类或 Block 类型成员变量时

    4.在方法命中含有 usingBlock 的 Cocoa 框架方法或 GCD 的 API 中传递 Block 时

    在调用 Block 的 copy 实例方法时,如果 Block 配置在栈上,那么该 Block 会从栈复制到堆。

    Block 作为函数返回值返回时,将 Block 赋值给附有 __strong 修饰符 id 类型的类或 Block 类型成员变量时,编译器自动地将对象 Block 作为参数并调动 __Block_copy 函数。

    在方法名中含有 usingBlock 的 Cocoa 框架方法或 GCD 的 API 中传递 Block时,在改方法或函数内部对传递过来的 Block 调用 Block 的 copy 实例方法或者 __Block_copy 函数。

    static void __main_block_dispose_0(struct __main_block_impl_0 *src)

    {

            _Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);

    }

    通过BLOCKFIELDIS_OBJECT和BLOCKFIELDIS_BYREF参数,区分 copy 函数和 dispose 函数的对象类型是对象还是 __block 变量(copy 持有截获的对象和所使用的 __block 变量,dispose 函数释放所有使用的 __block 变量和截获的对象)

    ARC 无效

    blk_t blk;

    {

           id array = [[NSMutableArray alloc] init];

           blk = ^(id obj) {

                  [arrray addObject:obj];

                 NSLog(@"array count = %ld", [array count]);

            }; // 没有调用 _Block_copy 函数,虽然截获了对象,但并没有持有对象,对象会随着变量的作用域的结束而废弃

            [array release];

    }

    blk([[NSObject alloc] init]);

    blk([[NSObject alloc] init]);

    blk([[NSObject alloc] init]);

    代码崩溃

    因为只有调用 _Block_copy 函数才能持有截获的附有 __strong 修饰符的对象类型的自动变量值。

    因此,Block 中使用对象类型自动变量时,除以下情形外,推荐调用 Block 的 copy 实例方法。

    1.Block 作为函数返回值返回时(ARC)

    2.将 Block 赋值给附有 __strong 修饰符 id 类型的类或 Block 类型成员变量时

    3.在方法命中含有 usingBlock 的 Cocoa 框架方法或 GCD 的 API 中传递 Block 时

    __block 变量和对象

    __block 说明符可指定任何类型的自动变量。

    __block id obj = [[NSObject alloc] init];

    等同于:

    __block id __strong obj = [[NSObject alloc] init];

    clang 转换后:

    /*__block 变量用结构体部分 */

    struct __Block_byref_obj_0 {

            void *__isa;

           __Block_byref_obj_0 *__forwarding;

            int __flags;

            int __size;

           void (*__Block_byref_id_object_copy)(void*, void*);

           void (*__Block_byref_id_object_dispose)(void *);

           __strong id obj;

    }

    static void __Block_byref_id_object_copy_131(void *dst, void *src) {

          _Block_object_assign((char *)dest + 40,*(void * *) ((char*)src + 40), 131);

    }

    static void __Block_byref_id_object_dispose_131(void *src) {

           _Block_object_dispose(*(void * *) ((char *)src + 40), 131);

    }

    /*__block 变量声明部分*/

    __Block_byref_obj_0 obj = {

           0,

          &obj,

          0x2000000,

         sizeof(__Block_byref_obj_0),

        __Block_byref_id_object_copy_131,

        __Block_byref_id_object_dispose_131,  

         [[NSObject alloc] init];

    };

    在 Block 中使用附有 __strong 修饰符的 id 类型或对象类型自动变量的情况下,当 Block 从栈复制到堆时,使用 _Block_object_assign 函数,持有 Block 截获的对象。当堆上的 Block 被废弃时,使用 _Block_object_dispose 函数,释放 Block 截获的对象。

    在 __block 变量为附有 __strong 修饰符的 id 类型或对象类型自动变量的情形下会发生同样的过程。当 __block 变量从栈复制到堆时,使用 _Block_object_assign 函数,持有赋值给 __block 变量的对象。

    Block 循环引用

    typedef void (^blk_t)(void);

    @interface MyObject : NSObject

    {

           blk_t blk_;

    }

    @end

    @implementation MyObject

    -(id)init

    {

          self = [super init];

         blk_ = ^{NSLog(@"self = %@", self);};

         return self;

    }

    - (void)dealloc

    {

          NSLog(@"dealloc");

    }

    @end

    int main()

    {

          id o = [[MyObject alloc] init];

          NSLog(@"%@", o);

          return 0;

    }

    MyObject 类对象的 Block 类型成员变量 blk_ 持有赋值为 Block 的强引用。即 MyObject 类对象持有 Block。init 实例方法中执行的 Block 语法使用附有 __strong 修饰符的 id 类型变量 self。并且由于 Block 语法赋值在了成员变量 blk_ 中,因此通过 Block 语法生成在栈上的 Block 此时由栈复制到堆,并持有所使用的 self。self持有 Block,Block 持有 self。导致循环引用

    相关文章

      网友评论

          本文标题:Obj-C高级编程--Blocks

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