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 の命名規則に関する私の知識にギャップがあることを示しているようです!

4

1 に答える 1

20

コード

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

通常、保持サイクルは作成されません。ブロックを呼び出して戻るだけならsomeMethod:withCompletion:、保持サイクルはまったくありません。(-[NSArray enumerateObjectsUsingBlock:]は一例です。)

someMethod:withCompletion:後で実行されるブロックを「記憶」している場合にのみ、保持サイクルが可能です。そのため、clang はヒューリスティックを使用して、ブロックを のプロパティに格納してFoo後で実行する「setter のような」メソッドであるかどうかを判断します。

-set<Key>および-add<Key>は、プロパティを設定したり、(対多) 関係に値を追加したりするための Key-Value コーディングのアクセサ パターンであり、これはまさに 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());
}

ここで呼び出されます:

/// 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ません

于 2013-03-20T23:06:25.470 に答える