ARC 简明参考手册 (Part 2)

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  *delegate;

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

ARC 简明参考手册 (Part 2)_第1张图片
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))