是否可以消除 "performSelector" 上的警告并接收 return 值或对象?

Is it possible to silence the warning on "performSelector" and receive the return value or object?

我熟悉此 ARC 警告的解决方案 (performSelector may cause a leak because its selector is unknown) 并且在大多数情况下都已实施,但我似乎无法找到正确获取 return 值的方法对于选择器而不仅仅是抑制警告。

好像不能或者不应该做,但是重写代码逻辑(别人开发的)太费时间了。

代码示例:

NSString *message = [callback performSelector:validatorSel withObject:textCell.textField.text];

如果已知 validatorSel 不以 allocnew 开头,或者名称中包含 copy(或 Copy),并且您知道不涉及内存管理覆盖(这种情况很少见),那么这里的默认内存管理将是正确的,您可以使用适当的 #pragma 抑制警告。如果你不能证明这些事情,那么这可能会崩溃,这就是为什么会有警告。

如果你不能证明上面的要求,那么这个在ARC下是没有办法保证安全的。您将不得不在没有 ARC 的情况下构建它或重写它。

从您的代码示例来看,您似乎期待一个采用 NSText * 和 return 和 NSString * 的方法的选择器。因此,根据您的链接答案,您可以确定此方法的实现具有函数类型:

NSString *(*)(ID, SEL, NSText *)

这里的ID可以替换成callback的类型,而NSText *可以替换成实际的textCell.textField.text类型,如果我们猜对的话。

再次从您的链接答案中,您可以获得实现并使用以下方式调用它:

NSString *(*implementation)(ID, SEL, NSText *)
   = (void *)[callback methodForSelector: performSelector:validatorSel];
NSString *message = implementation(callback, validatorSel, textCell.textField.text);

正如@RobNapier 正确指出的那样,如果选择器没有 return 保留值,这仅在 ARC 下是安全的,即对于普通 [*] 选择器,如果它是 的成员initcopynew 方法系列。现在你不太可能被传递给 validatorSelinit 系列方法,因为这需要 callback 成为对 alloc 的引用但不是 init 的对象,所以我们现在可以忽略它 [#]。要测试其他两个系列,您可以使用以下代码:

NSString *message; // for the return value of the selector
NSString *selName = NSStringFromSelector(validatorSel); // get string name of selector
if ([selName hasPrefix:@"new"]     // starts with new,
    || [selName hasPrefix:@"copy"] // or copy,
    || [selName rangeOfString:@"Copy"].location != NSNotFound) // or contains "Copy"
{
   // need to handle returning a retained object
   ...
}
else
{
   // normal case
   NSString *(*implementation)(ID, SEL, NSText *)
      = (void *)[callback methodForSelector: performSelector:validatorSel];
   message = implementation(callback, validatorSel, textCell.textField.text);
}

这只是留下如何在 ARC 下正确处理 copynew 系列方法的 return 值...

处理复制系列方法

ARC 通过 属性 被放置在 method/function 类型上,知道方法或函数 return 是一个保留对象。 命名约定 只是语言推断属性的方式,如果它不存在,可以在 method/function 声明中使用 NS_RETURNS_RETAINED 宏手动指定。所以上面缺少的代码只是:

{
   // need to handle returning a retained object
   NSString *(*implementation)(ID, SEL, NSText *) NS_RETURNS_RETAINED
      = (void *)[callback methodForSelector: performSelector:validatorSel];
   message = implementation(callback, validatorSel, textCell.textField.text);
}

implementation 的修改类型告诉 ARC 它将 return 一个保留的对象,ARC 将以与已知 copy[=75= 相同的方式处理调用] 或 系列方法。

HTH


注:处理init族方法

我们跳过了 init 系列,不仅因为它极不可能,而且因为它的行为不同 - init 系列方法 consume 调用它们的对象引用,也就是说,它们希望传递一个它们拥有所有权的拥有对象,并在需要时释放它。不出所料,使用参数也由属性指示,就像 returning 保留对象一样。好奇的 reader 可能希望确定所需的代码,即使需要它的可能性很小。


[*] "normal" 选择器是一种方法,它遵循 Objective-C 的标准命名约定,并且不使用属性以与标准约定相反的方式改变内存所有权行为.仅支持标准约定并不是一个很大的限制,约定的全部意义在于代码依赖于它们!

[#] 你当然也不太可能通过 new 系列选择器,callback 通常必须是对 class 对象,但处理它与 copy 系列相同,因此我们将其包括在内。