代码改变世界

ios(3)-Block

2015-11-10 10:10  jinruo.HW  阅读(497)  评论(0)    收藏  举报

Block

block语法

^(函数返回类型)(函数参数列表,参数,...){表达式}

一个栗子:

  ^void (int a,int b){
  		printf(@"%d",a+b);
  };

block概念

block包含的两个方面的内容 :

  • Block执行的代码,这是在编译的时候已经生成好的;
  • 一个包含Block执行时需要的所有外部变量值的数据结构。 Block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。

Quote from OneV's Den 引用自喵神

 block就 是一个代码块,但是它的神奇之处在于在内联(inline)执行的时候(这和C++很像)
 还可以传递参数。同时block本身也可以被作为参数在方法和函数间传递
 这就给予了block无限的可能。

与函数的区别

  • block不仅实现函数的功能,还可以携带函数的执行环境
  • 对闭包内的函数变量做一种保护措施,让其他函数无法访问该变量

为什么要有block

  • 让函数作为参数调用,而且当它作为参数的同时还可以传递参数 (如何理解可以传递参数:是不是说block作文参数传递的时候自身带的参数还是说携带的执行环境?)

  • iOS下用block + GCD来在程序中实现非阻塞式执行耗时任务

  • 让内部函数访问函数外的在同一范围下的变量;

如何使用

 typedef long (^Sum)(int,int);
 
 Sum sum = ^void (int a,int b){
 		printf(@"%d",a+b);
 }    
            
 //调用block 
 sum(1,2);

可能遇到的问题以及解决方法

  • 在ARC当中,block会对传进来的参数进行retain

    retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj相互持有,谁也释放不了谁。比如:

   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
   [request setCompletionBlock:^{
		 NSString* string = [request responseString]; 
	}];   
  • 解决办法:ARC中用__weak属性修饰
    MRC中用__block属性修饰

注意:

  • MRC中__block是不会引起retain;
  • 但在ARC中__block则会引起retain。ARC中block会自动retain传进来的参数,所以不用担心在block执行之前该变量被释放的问题。
  • ARC中应该使用__weak或__unsafe_unretained弱引用。
  • __weak只能在iOS5以后使用。

block只能访问外部变量,不能对外部变量进行改变,如果要修改,有两种方法:

  • 需在该变量上 添加属性__block 当我们给block访问外部变量的时候,实际上是在栈上copy了该参数,在编译的时候已经确定该外部变量的值,所以对其更改无效。对引用的外部变量添加__block属性,是在定义(注意是定义,不是运行)时,局部变量base当前值被copy到栈上,作为常量供Block使用。
  • 使用实例变量

block在内存中的三种位置 llvm&clang

BlkSum blk1 = ^ long (int a, int b) { return a + b; };
NSLog(@"blk1 = %@", blk1);// blk1 = <__NSGlobalBlock__: 0x47d0> 

int base = 100; BlkSum blk2 = ^ long (int a, int b) { return base + a + b; };
NSLog(@"blk2 = %@", blk2); // blk2 = <__NSStackBlock__: 0xbfffddf8> 

BlkSum blk3 = [[blk2 copy] autorelease]; 
NSLog(@"blk3 = %@", blk3); // blk3 = <__NSMallocBlock__: 0x902fda0>

  • NSStackBlock 位于栈空间,在函数返回后block无效
  • NSGlobalBlock 没有访问block之外的变量,位于text代码段
  • NSMallocBlock 当block作为对象加入到NSArray 或者 NSDictionary中时,会在堆上开辟一段空间复制block

[?]为什么blk1类型是NSGlobalBlock,而blk2类型是NSStackBlock?

blk1和blk2的区别在于,blk1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使blk1与函数没有任何区别,从blk1所在内存地址0x47d0猜测编译器把blk1放到了text代码段。blk2与blk1唯一不同是的使用了局部变量base,在定义(注意是定义,不是运行)blk2时,局部变量base当前值被copy到栈上,作为常量供Block使用。执行下面代码,结果是203,而不是204。

int base = 100; 
base += 100; 
BlkSum sum = ^ long (int a, int b) { return base + a + b; };
base++; 
printf("%ld",sum(1,2));

Block中使用__block修饰的变量时,将取变量此刻运行时的值,而不是定义时的快照。执行下面代码,结果是204

__block int base = 100; 
base += 100; 
BlkSum sum = ^ long (int a, int b) { return base + a + b; };
base++; 
printf("%ld",sum(1,2));

Apple所推荐的block使用范围包括以下几个方面:

  • 枚举——通过block获取枚举对象或控制枚举进程
  • View动画——简单明了的方式规定动画
  • 排序——在block内写排序算法
  • 通知——当某事件发生后执行block内的代码
  • 错误处理——当错误发生时执行block代码
  • 完成处理——当方法执行完毕后执行block代码
  • GCD多线程——多线程控制

block就是一个结构体

参考:

[1] http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/

[2] http://onevcat.com/2011/11/objc-block/