标签:
block和函数有很多相同点:
没有返回值,没有形参的block:
// 定义block
void (^block)() = ^{
NSLog(@"------block----");
};
// 直接调用
block();
有形参,有返回值的block:
// 求和block
int (^sumBlock)(int ,int) = ^(int a, int b){
return a + b;
};
其实由此可以看出,block的定义和指向函数的指针非常像:
// sum函数
int sum(int a, int b)
{
return a + b;
}
- (void)test
{
// 指针p指向sum函数
int (*p)(int, int) = sum;
}
block在访问局部变量的时候,首先会拷贝它,然后内部会增加一个该类型的成员变量,用来存储这个拷贝进来局部变量,但是这次拷贝只是一次“值传递”,block 默认是将其复制到其数据结构中来实现访问的,这就意味着我们不能修改该局部变量!
因此我们可以通过指针来进行地址传递:
- (void)test
{
int a;
int *p = &a;
void (^block)() = ^(){
*p = 10;
};
}
但需要注意的是:变量a的生命周期是和方法test相关联的,当test运行结束,栈随之销毁,变量a也会销毁,这个时候p就变成了野指针。如果block是作为参数或者返回值,这些类型就会跨栈,再次调用就会引发野指针错误!
因此可以在声明变量的时候加上__block修饰符,这样就可以在块内修改了!当加上__block修饰符后,block内部就会拷贝其引用地址来实现访问的。
因为全局变量都是在“静态数据存储区”,在程序结束前不会销毁,所以block直接访问了对应的变量(可以直接修改变量值),并没有给它预留位置。
其实它和全局变量是一样的,唯一的不同就是作用域!
block本身也可视为对象,在存放block的内存区域中,首个变量是指向Class对象的指针,该指针叫做isa。其余内存里含有块对象正常运行所需的各种信息,其内存布局如下(该图在Effective Objective-C 2.0中的第37条):
该结构在apple的开源代码中也有写。
/* Revised new layout. */
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size; // 块内存大小
void (*copy)(void *dst, void *src); // 复制函数
void (*dispose)(void *); // 销毁函数
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
isa:指向该block的类Class,同时isa也是OC对象的标识,这就说明block也是对象
flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等
reserved:保留变量,留给以后升级使用
invoke:函数指针,指向block的实现代码
descriptor:指向结构体的指针,每一个block都包含此结构体,其中声明了block的总体大小,copy和dispose两个辅助函数所对应的函数指针,copy函数会retain住已经拷贝的对象,而dispose函数则是release对象。
其次,block还会把它所访问的所有变量都拷贝一份,这个变量在descriptor变量后面,捕获了多少个变量,就要占据多少内存空间。
需要注意的是:invoke函数为何要把block对象当做参数传进来呢?原因就在于,执行block时,要从内存中把这些访问到的变量读出来。
在OC中,一共有三种类型的block:
_NSConcreteGlobalBlock:全局的静态block,这种block不会访问任何状态(比如外部的变量等),运行时也无需有任何状态来参与。block所使用的整个内存区域,在编译期就已经完全确定了,因此全局block是声明在全局内存中。同时,全局block的copy操作是一个空的操作,因为全局块不可能被系统所回收,相当于一个单例。
_NSConcreteStackBlock:栈上的block,在函数返回时会销毁,但是给栈block发送copy消息,就可以把block从栈上拷贝到堆上去了,一旦拷贝到堆上,block就成了带引用计数的对象了,而后续的拷贝操作都不会真的执行,只是递增block的引用计数罢了。
_NSConcreteMallocBlock:堆上的block,引用计数为0时销毁。
apple官方文档中有说明,在ARC模式下,在栈间传递block时,不需要手动copy栈中的block,即可让NSConcreteStackBlock 的 block 被 NSConcreteMallocBlock 类型的 block 替代,因为ARC会自动执行copy操作。
标签:
原文地址:http://blog.csdn.net/ldszw/article/details/51337258