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

来源:互联网 发布:宝马工程师编程步骤 编辑:程序博客网 时间:2024/05/16 02:51
一,明确两点
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复制内容到剪贴板
  1. //Test1  
  2. void exampleB_addBlockToArray(NSMutableArray *array) {  
  3.   char b = 'B';  
  4.   [array addObject:^{  
  5.     printf("%c\n", b);  
  6.   }];  
  7. }  
  8. void exampleB() {  
  9.   NSMutableArray *array = [NSMutableArray array];  
  10.   exampleB_addBlockToArray(array);  
  11.   void (^block)() = [array objectAtIndex:0];  
  12.   block();  
  13. }  
  14.   
  15. //Test2  
  16. void exampleC_addBlockToArray(NSMutableArray *array) {  
  17.   [array addObject:^{  
  18.     printf("C\n");  
  19.   }];  
  20. }  
  21. void exampleC() {  
  22.   NSMutableArray *array = [NSMutableArray array];  
  23.   exampleC_addBlockToArray(array);  
  24.   void (^block)() = [array objectAtIndex:0];  
  25.   block();  
  26. }  
  27.   
  28. //Test3  
  29. typedef void (^dBlock)();  
  30. dBlock exampleD_getBlock() {  
  31.   char d = 'D';  
  32.   return ^{  
  33.     printf("%c\n", d);  
  34.   };  
  35. }  
  36. void exampleD() {  
  37.   exampleD_getBlock()();  
  38. }  


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

0 0