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语法表达式使用fmt和val。Blocks中,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。导致循环引用