Obj-C高级编程--Blocks

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。导致循环引用

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

推荐阅读更多精彩内容