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/
浙公网安备 33010602011771号