Xcode似乎只是有时会发出警告"捕捉自我'强烈地在这个块中可能会导致保留周期",如下面的代码片段所示。
第一个块实际上是否可以保留保留周期,如果是,为什么,或者它是否不安全且xcode是否错误地不发出警告?
答案 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)
编译器将第二个块识别为可能具有保留循环的原因是,编译器仅检查名称以 add
或 set
开头的函数的保留循环。
/// 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);
}
add
或 set
开头/// 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
重命名为 addDateChangedCallback
或 setDateChangedCallback
,您很可能会收到相同的警告。