2013-03-20 80 views
7

我很驚訝地發現以下行爲......塊保留命名約定的循環?

@interface Foo : NSObject 

- (void)addBar:(id)aBar withCompletion:(void(^)(void))completion; 

@end 

@interface AwesomeClass : NSObject 

@property (strong, nonatomic) Foo *foo; 

- (void)doSomethingWithBar:(id)bar; 

@end 

@implementation AwesomeClass 

- (void)doSomethingWithBar:(id)bar 
{ 
    [self.foo addBar:bar withCompletion:^{ 
     NSLog(@"%@", self.foo); 
    }]; 
} 

在Xcode中4.6.1我得到的-doSomethingWithBar:的實現,「捕獲‘自我’強烈該塊很可能會導致一個警告保留週期「。

但是,如果我重構方法-addBar:withCompletion:-setupBar:withCompletion:此警告消失。這似乎令我驚訝,說明我在Objective-C命名約定方面的知識存在差距!

+1

嘗試重新編譯。警告不會「消失」,就像Xcode是愚蠢的,並刷新LLVM最後一次產生的警告 – CodaFi 2013-03-20 22:42:45

回答

19

代碼

[self.foo someMethod:bar withCompletion:^{ 
    NSLog(@"%@", self.foo); 
}]; 

一般不創建保留週期。如果someMethod:withCompletion:只是調用該塊並返回,則根本沒有保留循環。 (-[NSArray enumerateObjectsUsingBlock:]是一個例子。)

只有在someMethod:withCompletion:「記住」稍後要執行的塊時,纔有可能的保留週期。因此,鏗鏘使用啓發式來決定它是否是一種「類似setter」的方法,將該塊存儲到Foo的屬性中以便稍後執行。

-set<Key>-add<Key>是Key-Value Coding中的存取器模式,用於設置屬性或將值添加到(to-many)關係中,這正是clang檢查的內容。

這可以看出,在Clang source code

/// Check for a keyword selector that starts with the word 'add' or 
/// 'set'. 
static bool isSetterLikeSelector(Selector sel) { 
    if (sel.isUnarySelector()) return false; 

    StringRef str = sel.getNameForSlot(0); 
    while (!str.empty() && str.front() == '_') str = str.substr(1); 
    if (str.startswith("set")) 
    str = str.substr(3); 
    else if (str.startswith("add")) { 
    // Specially whitelist 'addOperationWithBlock:'. 
    if (sel.getNumArgs() == 1 && str.startswith("addOperationWithBlock")) 
     return false; 
    str = str.substr(3); 
    } 
    else 
    return false; 

    if (str.empty()) return true; 
    return !islower(str.front()); 
} 

其在這裏稱爲:

/// Check a message send to see if it's likely to cause a retain cycle. 
void Sema::checkRetainCycles(ObjCMessageExpr *msg) { 
    // Only check instance methods whose selector looks like a setter. 
    if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector())) 
    return; 

    /* 
    * rest omitted 
    */ 

} 

setupBar方法作爲 「二傳手樣」 方法治療,因爲 「套」後面跟着一個大寫字母。