初学者对于block机制一时很难理解。我刚开始学习的时候也是跟着敲代码,并不能说立马理解了其中的道理。今天,我想从2个层次、5个点去相对详细的讲解一下OC中的block机制。
参考:http://www.2cto.com/kf/201504/388349.html
①定义(相关说明很多,了解的人可以跳过)
int a=1, b=2;
int block1 = a+b;
//定义方式1
int (^block2)(int, int) = ^(int a, int b) {
return a+b;
};
//定义方式2
int (^block3)(int, int);
block3 = ^(int a, int b) {
return a+b;
};
NSLog(@"%d %d %d", block1, block2(a, b), block3(a, b));
讲解:block可以看做一个函数。只是通常我们的函数是先声明、定义,然后再传值、调用;而block可以将定义的部分直接放在赋值表达式的一边,很方便
②OC中block如何使用?
以上的情况我们肯定不会使用block,多此一举。
首先,我们要明确,block是OC中的一种通信机制,相似的还有代理机制和KVO机制。
代理机制中:假如我们有A页面要pushViewController到B页面,把A页面的的属性通过单例或者传值方法,B页面就可以使用了。但是B页面的属性要通过Pop方式传回给A页面就要通过代理或者单例方式了。
实现一个代理要定义协议,创建代理等。有时候也许我们只是想要一个值,但通过这个方式很麻烦。如:
我在b页面有一个输入框,我在里面输入一个字符串,点击按钮跳回a页面。并希望a页面的label.text显示的内容就是b页面中输入的字符串。此时最好的方法就是用block!
①block中的内存管理:
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。例如:
- (void)viewDidLoad
{
[super viewDidLoad];
int number = 1;
_block = ^(){
NSLog(@number %d, number);
};
}
在一个按钮点击事件中调用:如下调用会导致程序崩溃
- (IBAction)testDidClick:(id)sender {
_block();
}
解决这个问题的方法:在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了
_block = ^(){
NSLog(@number %d, number);
};
_block = [_block copy];
②循环引用:
_block = ^(){
NSLog(@string %@, _string);
};
这里的_string相当于是self->_string;那么block是会对内部的对象进行一次retain。也就是说,self会被retain一次。当self释放的时候,需要block释放后才会对self进行释放,但是block的释放又需要等self的dealloc中才会释放。如此一来变形成了循环引用,导致内存泄露。
修改方案:
①在非ARC环境中:
新建一个__block的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值。因为__block标记的变量是不会被自动retain的。
__block ViewController *controller = self;
_block = ^(){
NSLog(@string %@, controller->_string);
};
②在ARC环境下:
__block要换成__weak,因为ARC环境下自动释放池会自动做引用计数的增减。此处我们需要一个弱引用对象controller指向self对象,这样即便在block中使用了controller,由于它是一个弱引用,可以使用self的地址空间但是并不会造成引用计数加1