我很惊讶地发现以下行为......
@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:
的实现中收到警告,“在此 block 中强烈捕获‘self’可能会导致保留周期。”
但是,如果我将方法 -addBar:withCompletion:
的 name 重构为 -setupBar:withCompletion:
,此警告就会消失。看来我对此感到惊讶说明我在 Objective-C 命名约定方面的知识存在差距!
最佳答案
代码
[self.foo someMethod:bar withCompletion:^{
NSLog(@"%@", self.foo);
}];
通常不会创建保留循环。如果someMethod:withCompletion:
只是调用 block 并返回,根本没有保留周期。 (-[NSArray enumerateObjectsUsingBlock:]
是一个例子。)
仅当 someMethod:withCompletion:
“记住”稍后要执行的 block ,可能存在保留循环。因此 clang 使用启发式方法来确定它是否是将 block 存储到 Foo
的属性中的“setter-like”方法。稍后执行。
-set<Key>
和 -add<Key>
是键值编码中的访问器模式,用于设置属性或向(对多)关系添加值,而这正是 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
方法不被视为“setter-like”方法,因为“set”后面没有跟大写字母。
关于objective-c - block 保留命名约定的循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15535899/