2013-03-20 61 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方法作为 “二传手样” 方法治疗,因为 “套”后面跟着一个大写字母。