ARC 簡明參考手冊 (Part 2)
引言
前面說到滥崩, ARC 簡單的來說, 就是內(nèi)存管理的半自動檔钧椰。 使用 ARC 需要遵守一定的約定怨愤, 事實上這些約定在開啟 ARC 模式下大部分是編譯器強制的, 所以給新手的建議是癌压, 找一本 ARC 后時代的書仰泻, 而不要花時間在 retain/release 這些細(xì)節(jié)上。 在這里滩届, 我們將討論的是集侯, 有哪些細(xì)節(jié)是我們需要關(guān)心的, 也就是 ARC 自己沒有辦法處理的帜消。
weak 屬性修飾符
例如在樹的實習(xí)中棠枉, 子節(jié)點如果持有父節(jié)點的強引用, 就會造成 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.媳溺。

__weak 變量修飾符
由于 block 捕獲變量默認(rèn)是強引用月幌, 在 block 中捕獲 self 有可能導(dǎo)致 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 ** 作為傳出參數(shù) 缅帘。
(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];
總結(jié)
這里的內(nèi)容已經(jīng)覆蓋了 80% 左右的失暂, 當(dāng)你在 ARC 模式下 需要關(guān)心的問題彼宠, 還有一些高級的主題, 主要涉及非 ARC 代碼(例如 Core Foundation Framework) 和 ARC 代碼之間的轉(zhuǎn)換弟塞, 容后再敘凭峡。