block是一种块语法,类似于函数指针,它可以封装一段代码,被封装的代码在block被调用的时候才会执行。
一、block的定义
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//设置一个没有返回值 没有参数的block
void(^back)(void) = ^{
NSLog(@"Hello");
};//末尾的分号是十分重要的,写这篇博客时我就没有注意到...
//设置一个有参数有返回值的block
int(^sum)(int a,int b) = ^(int a,int b){
return a + b ;
};
}
return 0;
}
一般来说block一般都是写在实现文件中的,不过在这里为了图方便,我将它们都写在了main.m中。首先一个block应该包含 返回值,black名,以及参数。首先来看第一个block,开头的 void 说明这个block(代码块)是有返回值的,后面的括号中的 ^ 是block的标志,表示我们正在定义一个block,后面的 back 是block的名字,后面的一个括号中是这个block的参数。紧接着是是一个等号,这个等号表示等号后面的内容是 back block的内容。
下面的 sum 是有一个整型的返回值,接受两个整型参数的block,我们返回了两个整型参数的和。
二、block的内存管理
创建一个block实际上就是分配一片独立的内存空间(堆上),去运行block里面的代码块。这个代码块的运行空间已经脱离那个位置了,但是block会保存定义block的那个上下文(运行环境),换句话说就是copy了一份上下文,所以在block里面可以访问外部的变量,但是无法改变外部变量的值,因为改变不能影响到原来的那个外部变量,如果非要改变外部变量的值,需要在外部变量的声明前面写一个__block(两个下划线)。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int a = 10;//a对于change block是外部的变量
void (^change)(int c) = ^(int c){
a++;
NSLog(@"%d ",a);
};
change(18);
}
return 0;
}
这里我们定义了一个change block,在它的外面我定义了一个整型变量 a ,当它前面还没有__block这个关键字时,它是不能被block所改变的,因为,block里面只是访问的a的副本,改变无法传回到a本身。但是我们加上一个__block之后,就把这个a的地址传递到了堆上的那个空间中。现在输出的值就是11了。
block容易造成循环引用,当在block中调用某一个类的属性或者方法,由于block一般在实现文件中定义,所以会用到self.name [self test]等方法,一旦使用了这些方法block会对这个对象进行强引用。这篇文章中对这个问题进行了更为详细的讨论 https://www.jianshu.com/p/492be28d63c4。
解决方案:通过 __weak typeof(self) weakself = self 将self声明为弱引用
由于block会拷贝一份上下文,所以对类的一个对象,实际拷贝的是对象实际的在内存中的地址所以在对这个对象中的内容进行修改的时候既不用在对象前前缀 __block了,因为是地址。比如下面的 lsr 对象,实际上他是堆上的一个真实的对象的地址,通过访问地址来改变值是可以实现的。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * lsr = [Person new];
void (^chagne2)(void)=^{
a++;
lsr.name=@"lisiran";
NSLog(@"%@ %d",lsr.name,a);
};
chagne2();
}
return 0;
}