说明:阅读本文章,请参考之前的block文章加以理解;
一、栈区block分析
//代码
//ARC void test1() { { Person *per = [[Person alloc] init]; per.age = 10; ^{ NSLog(@"age:%d", per.age); }; } NSLog(@"-------1"); }
//打印
2019-01-14 17:24:12.118653+0800 MJ_TEST[6638:285938] Person dealloc 2019-01-14 17:24:12.118934+0800 MJ_TEST[6638:285938] -------1 Program ended with exit code: 0
分析:
<1>block代码内部引用的Person实例对象先于输出语句销毁,因为per仅限于大括号内,但此时block销毁了没有?往下看;
<2>上述block代码块并没有被指针持有,接下来看看指针持有的情况;
//代码
typedef void(^MyBlock)(void); //ARC void test2() { MyBlock block; { Person *per = [[Person alloc] init]; per.age = 10; block = ^{ NSLog(@"age:%d", per.age); }; } NSLog(@"-------1"); }
//打印
2019-01-14 17:34:58.473267+0800 MJ_TEST[6824:293129] -------1 2019-01-14 17:34:58.473705+0800 MJ_TEST[6824:293129] Person dealloc Program ended with exit code: 0
分析:Person实例对象后于输出语句销毁,为什么有指针持有,顺序就变了?
<1>等号左边:是一个auto类型的局部的block指针变量,存放在栈区;等号右边:是一个block代码块(对象),也是一个局部对象,存放在栈区;
<2>在ARC模式下,如果有指针持有(默认是强指针,修饰符为__strong)一个局部的block对象,系统会自动copy该block对象从栈区到堆区;
补充:其他三种情况——block作为函数返回值、含usingBlock方法(如数据的枚举方法)、GCD的应用(自己可以验证,此处不再赘述);
那么,我们再看看MRC的情况
//打印————test1()和test2()
2019-01-14 17:56:46.641171+0800 MJ_TEST[7171:306788] -------1 Program ended with exit code: 0
分析:为什么per对象没有销毁?——因为需要手动释放;
//代码
[per release];
//打印————test1()和test2()
2019-01-14 17:59:39.091313+0800 MJ_TEST[7243:309139] Person dealloc 2019-01-14 17:59:39.091974+0800 MJ_TEST[7243:309139] -------1 2019-01-14 17:59:39.092013+0800 MJ_TEST[7243:309139] Person dealloc 2019-01-14 17:59:39.092086+0800 MJ_TEST[7243:309139] -------1 Program ended with exit code: 0
分析:
<1>此时的block对象的作用域在第一个大括号范围内,超出则被释放;
<2>Person实例对象被捕获到block对象结构体体中,同时其作用域也仅限于第一个大括号内,因此超出同样被释放;
二、堆区block分析
1)类型分析——ARC
//strong类型
执行上述test2()方法,我们知道系统会自动将block对象从栈区copy到堆区;同时,Person实例对象会被捕捉到block对象的结构体中,如下
struct __test2_block_impl_0 { struct __block_impl impl; struct __test2_block_desc_0* Desc; Person *per; __test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, Person *_per, int flags=0) : per(_per) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
分析:可以看到per是一个指针变量,而该指针变量默认修饰符为__strong;修改代码
__strong Person *per = [[Person alloc] init];
clang命令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 main.m
说明:
<1>该命令行,只针对ARC模式下,MRC模式下,如果有release语句会报错;
<2>该命令行,是解决ARC模式下,实例对象为__weak类型,转成C++代码;
struct __test2_block_impl_0 { struct __block_impl impl; struct __test2_block_desc_0* Desc; Person *__strong per; __test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
分析:因此,一般的指针变量,默认修饰符为__strong;
//weak类型
//代码
//ARC void test2() { MyBlock block; { Person *per = [[Person alloc] init]; per.age = 10; __weak Person *weakPer = per; block = ^{ NSLog(@"age:%d", weakPer.age); }; // [per release]; } NSLog(@"----