我有一个场景,在alloc
对象之后调用的初始化程序在运行时才知道,我无法控制它。它也可能有各种各样的论点。所以目前我正在这样做:
...
id obj = [MyClass alloc];
return [self invokeSelectorOn:obj];
}
-(id) invokeSelectorOn:(id) obj {
SEL initSelector = ...;
NSMethodSignature *sig = [[MyClass class] instanceMethodSignatureForSelector:initSelector];
NSinvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
inv.selector = initSelector;
[inv retainArguments];
// ... setting arguments ...
[inv invokeWithTarget:obj];
id returnValue;
[inv getReturnValue:&returnValue];
return returnValue;
}
问题(我认为!)我有的是因为initSelector
是由NSInvocation调用的,所以它不返回retain + 1对象。因此,当自动释放池尝试释放对象时,上述结果就是崩溃。
我尝试添加CFBridgingRetain(...)
来解决内存问题,但是我不确定这是正确的解决方案,而静态分析器会将其标记为内存泄漏。
所以我的问题是如何通过NSInvocation调用初始化器并获取正确的retain + 1对象?
答案 0 :(得分:1)
哇....我想我偶然发现了答案。我花了一些时间在网上搜索Clang documentation on ARC。其中大部分都充满了'如果',但是和'也许',我想我必须花一些时间来理解它。无论如何,这是修改后的代码:
...
id obj = [MyClass alloc];
return [self invokeSelectorOn:obj];
}
-(id) invokeSelectorOn:(id) obj {
SEL initSelector = ...;
NSMethodSignature *sig = [[MyClass class] instanceMethodSignatureForSelector:initSelector];
NSinvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
inv.selector = initSelector;
[inv retainArguments];
// ... setting arguments ...
[inv invokeWithTarget:obj];
id __unsafe_unretained returnValue;
[inv getReturnValue:&returnValue];
return returnValue;
}
以某种方式将__unsafe_unretained
添加到接收来自初始化程序的响应的变量似乎可以解决问题。我没有时间研究Clang文档并弄清楚为什么会这样。我很开心。
也许对ARC内存管理有一定技术知识的人可以进一步解释。
答案 1 :(得分:1)
只需将invokeSelector方法重命名为createObjectByInvokingSelector,使其符合命名方案,并且不会释放returnValue
答案 2 :(得分:1)
getReturnValue:
只是将返回值复制到指针指向的位置,作为普通的旧哑二进制数据。它并不关心类型是什么,它只是以二进制方式复制它,如果它是一个托管对象类型,它就像内存管理一样不做任何事情。
因此,传递id __strong *
是不合适的,因为该类型要求在指向事物的位置分配时,释放先前的值并保留新值(getReturnValue:
不不这样做。)
将id __unsafe_unretained *
传递给它是合适的,因为该类型与指向事物的东西不进行任何内存管理的行为完全匹配。这就是为什么声明returnValue
为id __unsafe_unretained
(或MyClass * __unsafe_unretained
)然后传递&returnValue
的原因。
修复此问题后,您可以使用正常方法调用它。但在这种情况下,您调用初始化程序,初始化程序具有与常规方法不同的内存管理规则。初始化程序在它们被调用的引用上使用引用计数,并返回保留的引用。如果初始化程序返回它被调用的对象(这是大多数初始化程序所做的),那么那些取消它并且它就像普通方法一样工作。但是,也允许初始化器
nil
,在这种情况下,它将释放调用它的对象,并返回nil
。 因此,通常需要更复杂的处理才能与初始化程序一起使用。我不会进入那个。如果你知道你的初始化者总是返回它被调用的对象,那么你不必担心这个。