您的位置:首页 > 移动开发 > IOS开发

iOS内存管理---block机制详解

2015-10-20 19:59 337 查看
初学者对于block机制一时很难理解。我刚开始学习的时候也是跟着敲代码,并不能说立马理解了其中的道理。今天,我想从2个层次、5个点去相对详细的讲解一下OC中的block机制。


参考:http://www.2cto.com/kf/201504/388349.html

第一层:理解block机制

①定义(相关说明很多,了解的人可以跳过)

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中的内存管理:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 内存管理