ARC 假定名称以 new 开头的方法返回 +1 保留计数。
您的Client *client 变量默认为__strong。
在正常的调度代码中,当ARC将newClientForServerSocket:返回的引用分配给client时,它不会调整Client对象的保留计数,因为它转移了@987654328返回的+1的所有权@ 到 client 变量。
在直接调度代码中,ARC 不知道IMP_newClientForServerSocket 函数返回+1 保留计数。它假定函数返回 +0 保留计数。因此,当 ARC 将引用分配给 client 变量时,它会保留该对象。因此该对象被泄露,因为IMP_newClientForServerSocket 返回的+1 从未被释放。
您可以通过告诉 ARC IMP_newClientForServerSocket 返回 +1 保留计数来解决此问题。 Clang ARC 文档解释了“Retained return values” 下的操作:
返回可保留对象指针类型的函数或方法可以标记为返回保留值,表示调用者期望获得 +1 保留计数的所有权。这是通过将ns_returns_retained 属性添加到函数或方法声明来完成的,如下所示:
id foo(void) __attribute((ns_returns_retained));
- (id) foo __attribute((ns_returns_retained));
此属性是函数或方法类型的一部分。
当从这样的函数或方法返回时,ARC 会在返回语句的求值点保留值,然后再离开所有局部作用域。
当接收到来自此类函数或方法的返回结果时,ARC 会释放包含在其中的完整表达式末尾的值,但会受到局部值的通常优化。
这是我的测试程序。如果没有ns_returns_retained 属性,它的内存使用会无限增长。有了这个属性,它的内存使用很快就稳定下来了。
@import Foundation;
@interface Client: NSObject
+ (instancetype _Nonnull)newClient;
@end
@implementation Client
+ (instancetype)newClient { return [self new]; }
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
typedef Client *(*IMP_newClient_type)(id, SEL) __attribute((ns_returns_retained));
IMP_newClient_type IMP_newClient = (IMP_newClient_type)[Client methodForSelector:@selector(newClient)];
while (true) {
Client *c = IMP_newClient(Client.class, @selector(newClient));
[c self]; // do something with c to avoid an unused variable warning
}
}
return 0;
}