美文网首页
ARC 简明参考手册 (Part 2)

ARC 简明参考手册 (Part 2)

作者: 快乐的小问酱 | 来源:发表于2014-10-03 20:02 被阅读62次

    ARC 简明参考手册 (Part 2)

    引言

    前面说到, ARC 简单的来说, 就是内存管理的半自动档。 使用 ARC 需要遵守一定的约定, 事实上这些约定在开启 ARC 模式下大部分是编译器强制的, 所以给新手的建议是, 找一本 ARC 后时代的书, 而不要花时间在 retain/release 这些细节上。 在这里, 我们将讨论的是, 有哪些细节是我们需要关心的, 也就是 ARC 自己没有办法处理的。

    weak 属性修饰符

    例如在树的实习中, 子节点如果持有父节点的强引用, 就会造成 strong reference cycle, 所以需要使用 weak 属性修饰符。

    @interface TreeNode : NSObject
      @property NSMutableArray* childs;
      @property (weak) TreeNode* parent;
    @end
    

    同时建议委托使用 weak 属性修饰符。

    @property (nonatomic, weak) NSObject <SomeDelegate> *delegate;
    

    例如, 在 UI 中,一个 ViewController 是一个 View 的委托对象, 如果不使用 weak 修饰符, 就会造成如图所示的 S.R.C.。

    UI S.R.C.UI S.R.C.

    __weak 变量修饰符

    由于 block 捕获变量默认是强引用, 在 block 中捕获 self 有可能导致 strong reference cycle, 所以需要使用 __weak 变量修饰符。

    @interface XYZBlockKeeper : NSObject
      @property (copy) void (^block)(void);
    @end
    @implementation XYZBlockKeeper
    - (void)configureBlock {
        self.block = ^{
            [self doSomething];    // capturing a strong reference to self
                                   // creates a strong reference cycle
        };
    }
    ...
    @end
    

    需要更正为:

    - (void)configureBlock {
        XYZBlockKeeper * __weak weakSelf = self;
        self.block = ^{
            [weakSelf doSomething];   // capture the weak reference
                                      // to avoid the reference cycle
        }
    }
    

    block 中对 ivar 的引用也是一个陷阱:

    // The following block will retain "self"
    SomeBlockType someBlock = ^{
        BOOL isDone = _isDone;  // _isDone is an ivar of self
    };
    

    也要注意可能在 block 中捕获了外部的变量, 而这个变量又反过来持有这个 block。

    SomeObjectClass *someObject = ...
    __weak SomeObjectClass *weakSomeObject = someObject;
    
    someObject.completionHandler = ^{
        SomeObjectClass *strongSomeObject = weakSomeObject;
        if (strongSomeObject == nil)
        {
            // The original someObject doesn't exist anymore.
            // Ignore, notify or otherwise handle this case.
        }
        else
        {
            // okay, NOW we can do something with someObject
            [strongSomeObject someMethod];
        }
    };
    

    __autoreleasing 变量修饰符

    用于这样的情景, 一个方法返回 BOOL 值指示错误, 并有一个 NSError ** 作为传出参数 。

    (BOOL) doSomething:(NSError * __autoreleasing *)myError {
      NSError *error = [[NSError alloc] init];
      myError = &error;
    
      // ...
    
      return NO;
    }
    

    上面的代码片段的问题在于, callee 中的 error 是强引用, 而它不作为返回值, 那么从 callee 的堆栈到 caller 的堆栈的时候, 它就会被销毁。 所以需要更正如下:

    NSError __autoreleasing *error = [[NSError alloc] init];
    myError = &error;
    

    或者

    *myError = [[NSError alloc] init];
    

    总结

    这里的内容已经覆盖了 80% 左右的, 当你在 ARC 模式下 需要关心的问题, 还有一些高级的主题, 主要涉及非 ARC 代码(例如 Core Foundation Framework) 和 ARC 代码之间的转换, 容后再叙。

    相关文章

      网友评论

          本文标题:ARC 简明参考手册 (Part 2)

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