objective-c Block 相关内容
2015-11-09 18:49
288 查看
1、什么是Blocks
块可以实现闭包。这项语言特性是作为“扩展”(extension)而加入GCC编译器中的,在近期版本的Clang中都可以使用。10.4版及其后的Mac OS X系统,与4.0版及其后的iOS系统中,都含有正常执行块所需要的组件。从技术上讲,这是个位于C语言层面的特性,因此,只要有支持此特性的编译器,以及能执行块的运行期组件,就可以在C,C++,Objective-C、Objective-C++代码中使用它。Blocks是C语言的扩展功能。可以用一句话来表示Blocks的扩展功能:带有自动变量(局部变量)的匿名函数。顾名思义,所谓匿名函数就是不带有名称的函数。C语言的标准不允许存在这样的函数。Blocks提供了类似由C++和Objective-C类生成实例或对象来保持变量的方法,其代码量与编写C语言函数差不多。如“带有自动变量”,Blocks保持自变量的值。
2、Block语法
完整形式的Block语法与一般的C语言函数定义相比,仅有两点不同。1)没有函数名;
2)带有“^”。
第一点不同是没有函数名,因为它是匿名函数;第二点不同是返回值类型前带有“^”(插入记号,caret)记号。Block语法如下:
如:^int (int count) { return count + 1; }
Block语法可省略好几个项目,首先是返回值类型;
省略返回类型时,如果表达式中有return语句就使用该返回值的类型,
4000
如果表达式中没有return语句就使用void类型。表达式中憨厚多个return语句时,所有return的返回值类型都必须相同。
如:^int (int count) { return count + 1; }
返回值类型以及参数列表均被省略的Block语法是大家最为熟悉的记述方式:
如:^ { printf("Blocks\n"); }
3、Block类型变量
Block语法单在其记述上来看,除了没有名称以及带有“^”以外,其他都与C语言函数定义相同。声明Block类型变量的示例如:int (^blk) (int);
声明Block类型变量仅仅是将声明函数指针类型变量的“*”变为"^“,该类型变量与一般的C语言变量完全相同,可作为以下用途使用:
自动变量
函数参数
静态变量
静态全局变量
全局变量
4、截获自动变量值
通过Block语法和Block类型的变量说明,我们已经理解了”带有自动变量值的匿名函数“中”匿名函数“。而”带有自动变量值“究竟是什么呢?”带有自动变量值“在Blocks中表现为”截获自动变量“。int main() { int dmy = 256; int val = 10; const char *fmt = "val = %d\n"; void (^blk) (void) = ^{ printf(fmt, val); }; val = 2; fmt = "These values were changed. val = %d\n"; blk(); // val = 10 return 0; }
该源码中,Block语法的表达方式使用的是它之前声明的自动变量fmt和val。Block中,Block表达式截获所有的自动变量的值,即保存该自动变量的瞬间值。因为Block表达式保存了自动变量的值,所以在执行Block语法后,即使改写Block中使用的自动变量的值也不会影响Block执行时自动变量的值。该源码的执行结果是:val = 10.
5、__block说明符
实际上,自动变量值截获只能保存执行Block语法瞬间的值,保存后就不能改写该值。使用附有__block说明符的自动变量可以在Block中赋值,该变量称为__block变量。int val = 0; void (^blk) (void) = ^{ val = 1; }; // error __block int val = 0; void (^blk) (void) = ^{ val = 1; }; // ok
6、截获的自动变量
如果将值赋给Block中截获的自动变量,就会产生编译错误。那么截获Objective-C对象,调用改变该对象的方法也会产生编译错误么?这是没问题的,而向截获的变量array赋值则会产生编译错误。该源代码中截获的变量值为NSMutableArray类的对象,如果用C语言来描述,即是截获NSMutableArray类对象用的结构体实例指针。虽然赋值给截获的自动变量array的操作会产生编译错误,但使用截获的值却不会有任何问题。id array = [[NSMutableArray alloc] init]; void (^blk) (void) = ^ { id = obj = [[NSObject alloc] init]; [array addObject: obj]; // ok array = [[NSMutableArray alloc] init]; // error };
另外,在使用C语言数组时必须小心使用其指针。
const char text[] = "hello"; void (^blk) (void) = ^ { printf("%c\n", text[2]); }
只是使用C语言的字符串字面量数组,而并没有向截获的自变量赋值,因此看似没有问题,但实际上回产生编译错误。这是因为在现在的Blocks中,截获自动变量的方法并没有实现对C语言数组的截获。此时,使用指针可以解决该问题。
const char* text = "hello"; void (^blk) (void) = ^ { printf("%c\n", text[2]); }
7、块的实质
如果块所捕获的变量是对象类型,那么就会自动保留它。系统在释放这个块的时候,也就将其一并释放。这就引出了一个与块有关的问题,块本身可视为对象。实际上,在其他Objective-C对象所能响应的选择子中,有很多是块也能响应的。而最重要之处则在于,块本身也和其他对象一样,有引用计数。当最后一个指向块的引用移走之后,块就回收了。如果将块定义在Objective-C类的实例方法中,那么除了可以访问类的所有实例变量之外,还可以使用self变量。块总能修改实例变量,所以在声明时无须加__block。不过,如果通过读写操作捕获了实例变量,那么也会自动把self变量一并捕获,因为实例变量是与self所指代的实例变量关联在一起的。例如:
@interface EOCClass - (void) anInstanceMethod { //... void (^blk) () = ^ { _anInstanceVariable = @"Something"; NSLog(@"_anInstanceVariable = %@", _anInstanceVariable); }; //.. } @end
如果某个EOCClass实例正在执行anInstanceMethod方法,那么self变量就指向此实例。由于块里没有明确使用self变量,所以很容易就会忘记self变量其实也为块所捕获了。直接访问实例变量和通过self来访问时等效的:
<span style="white-space:pre"> </span>self->_anInstanceVariable = @"_Something";之所以要捕获self变量,原因在于此。然而,一定要记住:self也是 ,因而块在捕获它时也会将其保留。如果self所指代的那个对象同时也保留了块,那么这种情况通常就会导致”保留环”。
8、块的内部结构
每个Objective-C对象都占据某个内存区域。块本身也是对象,在存放块对象的内存区域中,首个变量是指向Class对象的指针,该指针叫做isa。其余内存里含有块对象正常运转所需的各种信息。块对象的内存布局如下:在内存布局中,最重要的就是invoke变量,这是个函数指针,指向块的实现代码。函数原型至少要接受一个void*型的参数,此参数代表块。
descriptor变量是指向结构体的指针,每个块里都包含此结构体,其中声明了块对象的总体大小,还声明了copy与dispose这两个辅助函数所对应的函数指针。辅助函数在拷贝及丢弃块对象时运行,其中会执行一些操作,比方说,前者保留捕获的对象,后者将之释放。
块还会把它所捕获的所有变量都拷贝一份。这些拷贝放在descriptor变量后面,捕获了多少个变量,就要占据多少内存空间。请注意,拷贝的并不是对象本身,而是指向这些对象的指针变量。invoke函数为何需要将块对象作为参数传进去呢?原因在于,执行块时,要从内存中把这些捕获到的变量读出来。
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- Ruby中Block和迭代器的使用讲解
- Ruby中使用Block、Proc、lambda实现闭包
- Ruby中的block、proc、lambda区别总结
- 讲解iOS开发中基本的定位功能实现
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解
- IOS 改变键盘颜色代码
- 举例详解iOS开发过程中的沙盒机制与文件
- Android和IOS的浏览器中检测是否安装某个客户端的方法