块从命名约定中保留循环?

时间:2013-03-20 22:19:50

标签: objective-c automatic-ref-counting clang

我很惊讶地发现以下行为......

@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 个答案:

答案 0 :(得分:19)

代码

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

通常不会创建保留周期。如果someMethod:withCompletion:只调用块并返回,则根本没有保留周期。 (-[NSArray enumerateObjectsUsingBlock:]就是一个例子。)

仅当someMethod:withCompletion:“记住”稍后要执行的块时,才有可能的保留周期。所以clang使用启发式方法来决定它是否是一个“类似setter”的方法,它将块存储到Foo的属性中以便稍后执行。

-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”的方法,因为“set”后面没有大写字母。