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

初学者对于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内存管理)