iOS Block在ARC/非ARC下的使用总结

时间:2021-07-27 18:20:18

今天做了CocoaChina上的小测试"你真的知道blocks在Objective-C中是怎么工作的吗",发现对Block的使用还存在盲区,在这里接着上一篇《iOS ARC内存管理总结》总结下Block的正确使用方式。
一,明确两点

iOS BlockARC/ARC下的使用总结-特酷吧

1,Block可以访问Block函数以及语法作用域以内的外部变量。也就是说:一个函数里定义了个block,这个block可以访问该函数的内部变量(当然还包括静态,全局变量)-即block可以使用和本身定义范围相同的变量。2,Block其实是特殊的Objective-C对象,可以使用copy,release等来管理内存,但和一般的NSObject的管理方式有些不同,稍后会说明。

二,Block语法Block很像函数指针,这从Block的语法上就可以看出。Block的原型:返回值 (^名称)(参数列表)
Block的定义
^ 返回值类型 (参数列表) { 表达式 }其中返回值类型和参数列表都可以省略,最简单的Block就是:
^{ ; };
一般的定义就是:
返回值 (^名称)(参数列表) = ^(参数列表){代码段};为了方便通常使用typedef定义:
typedef void (^blk) (void);

三,Block存储域Block能够截获自动变量,自动变量的当前值会被拷贝到栈上作为常量,此时不能在Block内对自动变量进行赋值操作,如果有这种需求,则需要该变量是:
1,静态变量
2,全局变量
3,或者使用__block修饰符
根据Block中是否引用了自动变量,可以将Block存储区域分类:
1,
_NSConcreteStackBlock-存储在栈上
2,
_NSConcreteGlobalBlock-存储在全局数据区域(和全局变量一样)
3,
_NSConcreteMallocBlock-存储在堆上没有引用自动变量或者在全局作用域的Block为_NSConcreteGlobalBlock,其他的基本上都是_NSConcreteStackBlock。对_NSConcreteStackBlock执行copy操作会生成_NSConcreteMallocBlock。一般来说出问题的Block大部分都是_NSConcreteStackBlock,超过了_NSConcreteStackBlock的作用域_NSConcreteStackBlock就会销毁。

四,对Block执行retain,copy方法的效果Block是C语言的扩展,C语法也可以使用Block的语法,对应的C语言使用Block_copy,Block_release.无论是_NSConcreteStackBlock,还是_NSConcreteGlobalBlock,执行retain都不起作用。而_NSConcreteMallocBlock执行retain引用计数+1。对于copy操作,_NSConcreteStackBlock会被复制到堆上得到新的_NSConcreteMallocBlock,而_NSConcreteGlobalBlock执行copy操作不起作用。而对_NSConcreteMallocBlock执行copy操作会引起引用计数加1。那么就引出一个问题,对_NSConcreteMallocBlock多次copy会不会引起问题呢?参考Objective-C高级管理115页。

五,什么时候要对NSConcreteStackBlock执行copy操作?配置在栈上的Block也就是NSConcreteStackBlock类型的Block,如果其所属的变量作用域结束该Block就会废弃。这个时候如果继续使用该Block,就应该使用copy方法,将NSConcreteStackBlock拷贝为_NSConcreteMallocBlock。当_NSConcreteMallocBlock的引用计数变为0,该_NSConcreteMallocBlock就会被释放。

如果是非ARC环境,需要显式的执行copy或者antorelease方法。而当ARC有效的时候,实际上大部分情况下编译器已经为我们做好了,自动的将Block从栈上复制到堆上。包括以下几个情况:
1,
Block作为返回值时
类似在非ARC的时候,对返回值Block执行[[returnedBlock copy] autorelease];
2,
方法的参数中传递Block时
3,Cocoa框架中方法名中还有useringBlock等时
4,GCD相关的一系列API传递Block时。
比如:[mutableAarry addObject:stackBlock];这段代码在非ARC环境下肯定有问题,而在ARC环境下方法参数中传递NSConcreteStackBlock会自动执行copy。

六,Block的循环引用
对于非ARC下, 为了防止循环引用, 我们使用__block来修饰在Block中使用的对象:
对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中使用的对象。原理就是:ARC中,
Block中如果引用了__strong修饰符的自动变量,则相当于Block对该变量的引用计数+1。

七,演示代码主要为了说明在ARC和非ARC环境下使用Block的一些区别。这里还是给出CococChina上的两个典型例子说明:

折叠 C/C++ Code复制内容到剪贴板

01.
02.
03.
04.
05.

//Test1

voidexampleB_addBlockToArray(NSMutableArray *array) {charb = 'B';
[array addObject:^{

printf("%c\n", b);

http://www.tekuba.net/program/349/ 2/8

2015/4/22

iOS BlockARC/ARC下的使用总结-特酷吧

06.
07.
08.
09.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

}];}

voidexampleB() {
NSMutableArray *array = [NSMutableArray array];exampleB_addBlockToArray(array);
void(^block)() = [array objectAtIndex:0];block();

}

//Test2

voidexampleC_addBlockToArray(NSMutableArray *array) {[array addObject:^{

    printf("C\n");
}];

}
voidexampleC() {

NSMutableArray *array = [NSMutableArray array];exampleC_addBlockToArray(array);
void(^block)() = [array objectAtIndex:0];block();

}

//Test3

typedef void(^dBlock)();dBlock exampleD_getBlock() {

chard = 'D';return^{

    printf("%c\n", d);
};

}
voidexampleD() {

  exampleD_getBlock()();
}

Test1:exampleB_addBlockToArray添加的Block为NSConcreteStackBlock,在非ARC环境下,执行该Block时,栈上的数据已经被清除了,错误。而在ARC环境下,作为参数的Block会自动copy,不会出现问题;对应是五(2)情况。
Test2:exampleC_addBlockToArray添加的Block为_NSConcreteGlobalBlock,存储在程序的数据区,相当于一个不使用外部环境的函数,没有用到栈上的数据,所有无论是ARC还是非ARC都能正常执行。
Test3ARC中作为返回值的NSConcreteStackBlock会被执行copy操作。所有ARC运行正常,非ARC错误。对应是五(1)情况。
转载请注明来自
特酷吧,本文地址:www.tekuba.net/program/349/
推荐阅读:
iOS ARC内存管理总结
Objective-C Runtime分析(二)-Class,Method,SEL,IMP