块中的命名约定是否会导致保留周期问题?

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: 的实现中收到一个警告,“在此块中强制捕获 'self' 可能导致保留循环。”然而,如果我重构方法 -addBar:withCompletion:名称-setupBar:withCompletion:,则此警告消失了。这似乎表明我对 Objective-C 命名约定的知识有所缺失,这让我感到惊讶!

1
尝试重新编译。警告不是“消失了”,而是 Xcode 的愚蠢行为,刷新了上次 LLVM 生成的警告。 - CodaFi
1个回答

20

这段代码

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

通常不会创建保留循环。如果someMethod:withCompletion:只是调用了块并返回,那么根本没有保留循环(例如 - [NSArray enumerateObjectsUsingBlock:])。

只有当someMethod:withCompletion:“记住”要稍后执行的块时,可能会出现保留循环。因此,clang使用一种启发式方法来决定它是否是将块存储到Foo属性中以便稍后执行的“setter-like”方法。

-set<Key>-add<Key>是Key-Value Coding中的访问器模式,用于设置属性或将值添加到(一对多的)关系中,这正是clang检查的内容。

可以在Clang源代码中看到这一点:

/// 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());
}
这里称为:

which is called here:

/// 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
   */

}

由于"set"后面没有紧跟一个大写字母,所以您的setupBar方法不会被视为类似setter的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接