Mac OS X系统10.4及其iOS 4.0后引入了闭包的概念,这项语言特性是做为扩展而加入GCC编译器的。在Foundation框架中大量使用了Block。 块就是一个实现某个功能的函数闭包,这个函数闭包能够带有参数,也能够没有参数,能够有返回值也能够没有返回值者,用符号'^'来表示。块在声明的范围内,能够调用块外部的全局变量和局部变量。安全
void (^someBlock) () = ^{
//A simple block
//Implementation: some code
//无返回值,无参数
}
void (^block) (int a, bool b) = ^(int a, bool b) {
//some code
//无返回值,带参数
}
int (^block2) (int a, bool b) = ^(int a, bool b) {
//some code
//带参数带返回值
return integerValue;
}
复制代码
须要主意的是,block内部不能改变外部变量,想要在Block中改变变量的值,那么咱们只须要在变量声明的时候加上__Block修饰符。数据结构
__block int a = 0;
void (^block)() = ^{
a = 33;
};
复制代码
每一个Object-c变量都占据着某个内存区域,block自己也是一个对象,在存放block对象的内存区域中,首个变量是指向class的指针isa,其他内存里包含着对象的其余全部信息。闭包
isa 指针,全部对象都有该指针,用于实现对象相关的功能。框架
flags,用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码能够看到对该变量的使用。ide
reserved,保留block函数代码内的变量。函数
invoke,函数指针,指向具体的 block 实现的函数调用地址。在内存布局中最重要的就是invoke函数指针,指向block的实现代码布局
descriptor,是指向结构体的指针,每一个块里都包含此结构体。block将所捕获的变量指针拷贝到descriptor变量后。表示 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。this
variables,capture 过来的变量,block 可以访问它外部的局部变量,就是由于将这些变量(或变 量的地址)复制到告终构体中。spa
对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。对于用 __block 修饰的外部变量引用,block是复制其引用地址来实现访问指针
定义block的时候,其所占的内存区域是分配在栈中的。 在 Objective-C 语言中,一共有 3 种类型的 block:
下面的这段代码在执行的时候就很危险:
void (^block)();
int a = 3;
if (a > 0) {
block = ^ { NSLog(@"Block A") };
} else {
block = ^ { NSLog(@"Block B") };
}
复制代码
在定义if else 语句中的两个block都分配在栈内存区域,编译器会给每一个block分配好内存,然而等离开相应的范围后,编译器有可能会把分配给块的内存覆盖掉。因而这两个块只能保障在对应的if else语句范围内有效,这样的代码运行起来就会出现问题。 为解决此问题,能够给block对象发送copy消息以拷贝到堆空间里。一旦复制到堆上,block就成了带引用计数器的对象了。后续的复制操做都不会真的执行复制,只是递增块对象的应用计数器。 如下代码就是安全的:
void (^block)();
int a = 3;
if (a > 0) {
block = [^{ NSLog(@"Block A") } copy];
} else {
block = [^{ NSLog(@"Block B") } copy];
}
复制代码
与全局变量相似,全局块所使用的内存区域,在编译期就已经彻底肯定了,全局块能够声明在全局内存里。下面就是一个简单的全局块:
void (^block) () = ^ {
NSLog(@"this is a global block");
}
复制代码
typedef int (^BLOCKSOME) (bool flag, int value);
BLOCKSOME block = ^(bool flag, int value) {
//some code
};
复制代码
用块引用及其所属对象时,不要保留闭环,防止出现return cycle。如使用weakself来防止return cycle:
__weak ViewController *wself = self;
复制代码
定义一个wself变量并加上__weak修饰符,在Block代码块中,全部须要self的地方都用wself来替代。这样就不会增长引用计数,因此Block持有self对象也就不会形成循环引用,从而形成内存泄漏。