xcode保留周期警告有时只发生

时间:2017-03-11 14:41:09

标签: ios objective-c xcode automatic-ref-counting objective-c-blocks

Xcode似乎只是有时会发出警告"捕捉自我'强烈地在这个块中可能会导致保留周期",如下面的代码片段所示。

enter image description here

第一个块实际上是否可以保留保留周期,如果是,为什么,或者它是否不安全且xcode是否错误地不发出警告?

3 个答案:

答案 0 :(得分:2)

两个块都将导致保留周期。对于第一个检测它有点困难,因此编译器不会报告它。

在第一个块中,我假设datePicker是您对象的属性。因此,您的对象会保留日期选择器,它会保留保留对象的块(通过捕获自身)。它是一个有3个物体的循环,但仍然是一个循环。

在你的第二个区块中,这更加直截了当:你的对象保留了块,并且块保留了你的对象(通过捕获自己)。它是一个循环,只有2个对象易于识别(因此警告)。

在这两种情况下,你应该自我捕捉以避免保留周期。

__weak typeof(self) weakSelf = self;
[self methodThatRetainsABlock: ^{
    typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf == nil) {
        return;
    }
    // Don't ever use self here, as it will capture it strongly.
    // Use only strongSelf
}];

答案 1 :(得分:1)

OnDateChange被发送到另一个对象,因此Xcode不希望有任何保留周期(虽然理论上它仍然可能发生)。

AddOnTap发送给自己,因此它在块周围的可能性很高。因此警告。

答案 2 :(得分:0)

编译器将第二个块识别为可能具有保留循环的原因是,编译器仅检查名称以 addset 开头的函数的保留循环。

来自clang documentation

  1. 检查保留周期可验证“类似于 setter”的选择器
/// 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;

  // Try to find a variable that the receiver is strongly owned by.
  RetainCycleOwner owner;
  if (msg->getReceiverKind() == ObjCMessageExpr::Instance) {
    if (!findRetainCycleOwner(*this, msg->getInstanceReceiver(), owner))
      return;
  } else {
    assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
    owner.Variable = getCurMethodDecl()->getSelfDecl();
    owner.Loc = msg->getSuperLoc();
    owner.Range = msg->getSuperLoc();
  }

  // Check whether the receiver is captured by any of the arguments.
  for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i)
    if (Expr *capturer = findCapturingExpr(*this, msg->getArg(i), owner))
      return diagnoseRetainCycle(*this, capturer, owner);
}
  1. “Setter like”方法以 addset 开头
/// 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());
}

在您的代码中,如果您将 onDateChangedCallback 重命名为 addDateChangedCallbacksetDateChangedCallback,您很可能会收到相同的警告。