美文网首页
深入 Block 的内存问题

深入 Block 的内存问题

作者: cbd0bfba9942 | 来源:发表于2016-05-19 17:47 被阅读180次

    一些关键点:

    ```

    block 是在栈上创建的

    block 可以复制到堆上

    Block会捕获栈上的变量(或指针),将其复制为自己私有的const(变量)。

    (如果在Block中修改Block块外的)栈上的变量和指针,那么这些变量和指针必须用__block关键字申明(译者注:否则就会跟上面的情况一样只是捕获他们的瞬时值)。

    ```

    如果 block 没有在其他地方被保持,那么它会随着栈生存并且当栈帧(stack frame)返回的时候消失。仅存在于栈上时,block对对象访问的内存管理和生命周期没有任何影响。

    如果 block 需要在栈帧返回的时候存在,它们需要明确地被复制到堆上,这样,block 会像其他 Cocoa 对象一样增加引用计数。当它们被复制的时候,它会带着它们的捕获作用域一起,retain 他们所有引用的对象。

    如果一个 block引用了一个栈变量或指针,那么这个block初始化的时候会拥有这个变量或指针的const副本,所以(被捕获之后再在栈中改变这个变量或指针的值)是不起作用的。(译者注:所以这时候我们在block中对这种变量进行赋值会编译报错:Variable is not assignable(missing __block type specifier),因为他们是副本而且是const的.具体见下面的例程)。

    当一个 block 被复制后,__block 声明的栈变量的引用被复制到了堆里,复制完成之后,无论是栈上的block还是刚刚产生在堆上的block(栈上block的副本)都会引用该变量在堆上的副本。

    (下面代码是译者加的)

    ```

    CGFloat blockInt = 10;

    void (^playblock)(void) = ^{

    NSLog(@"blockInt = %zd", blockInt);

    };

    blockInt ++;

    playblock();

    ...

    //结果为:blockInt = 10

    ```

    ```

    用 LLDB 来展示 block 是这样子的:

    ![这里写图片描述](http://img.blog.csdn.net/20160519181047277)

    最重要的事情是 __block 声明的变量和指针在 block 里面是作为显示操作真实值/对象的结构来对待的。

    block 在 Objective-C 的 runtime(运行时) 里面被当作一等公民对待:他们有一个 isa 指针,一个类也是用 isa 指针在Objective-C 运行时来访问方法和存储数据的。在非 ARC 环境肯定会把它搞得很糟糕,并且悬挂指针会导致 crash。__block 仅仅对 block 内的变量起作用,它只是简单地告诉 block:

    嗨,这个指针或者原始的类型依赖它们在的栈。请用一个栈上的新变量来引用它。我是说,请对它进行双重解引用,不要 retain 它。 谢谢,哥们。

    如果在定义之后但是 block 没有被调用前,对象被释放了,那么 block 的执行会导致 crash。 __block 变量不会在 block 中被持有,最后... 指针、引用、解引用以及引用计数变得一团糟。

    self 的循环引用

    当使用代码块和异步分发的时候,要注意避免引用循环。 总是使用 weak 来引用对象,避免引用循环。(译者注:这里更为优雅的方式是采用影子变量@weakify/@strongify 这里有更为详细的说明) 此外,把持有 block 的属性设置为 nil (比如 self.completionBlock = nil) 是一个好的实践。它会打破 block 捕获的作用域带来的引用循环。

    例子:

    ```

    __weak __typeof(self) weakSelf = self;

    [self executeBlock:^(NSData *data, NSError *error) {

    [weakSelf doSomethingWithData:data];

    }];

    ```

    不要这样:

    ```

    [self executeBlock:^(NSData *data, NSError *error) {

    [self doSomethingWithData:data];

    }];

    ```

    多个语句的例子:

    ```

    __weak __typeof(self)weakSelf = self;

    [self executeBlock:^(NSData *data, NSError *error) {

    __strong __typeof(weakSelf) strongSelf = weakSelf;

    if (strongSelf) {

    [strongSelf doSomethingWithData:data];

    [strongSelf doSomethingWithData:data];

    }

    }];

    ```

    不要这样:

    ```

    __weak __typeof(self)weakSelf = self;

    [self executeBlock:^(NSData *data, NSError *error) {

    [weakSelf doSomethingWithData:data];

    [weakSelf doSomethingWithData:data];

    }];

    ```

    你应该把这两行代码作为 snippet 加到 Xcode 里面并且总是这样使用它们。

    ```

    __weak __typeof(self)weakSelf = self;

    __strong __typeof(weakSelf)strongSelf = weakSelf;

    ```

    相关文章

      网友评论

          本文标题:深入 Block 的内存问题

          本文链接:https://www.haomeiwen.com/subject/azegrttx.html