使用NSInvocation调用类似如下方法,而产生过度释放Crash。
// code 1 - 1
// ---> 被调用的初始化方法
// @class MLNTestMe
- (instancetype)init
{
// 做一些强校验,入参为空时初始化失败,且不能使用会闪退的断言
if (YES) {
// 这里为了复现,判断条件直接写为YES
// 走到这个分支时,会出现double release
return nil;
}
if (self = [super init]) {
// do something ...
}
return self;
}
// code 1 - 2
// ---> NSInvocation 代码
- (void)testInvoke
{
MLNTestMe *target = [MLNTestMe alloc];
// 构建invocation
NSMethodSignature *ms = [target methodSignatureForSelector:@selector(init)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:ms];
// 准备调用
[invocation setTarget:target];
[invocation setSelector:@selector(initWithtesttttt)];
[invocation invoke];
void *ret;
[invocation getReturnValue:&ret];
NSLog(@"%@", (__bridge id)ret);
} // <------ 这个方法调用结束后,会crash
首先要明确一点,出现double release的对象,应该是target。
难道init方法中必须返回self,不能返回nil或其他值?
// code 2 - 1
// 这么调,没问题
MLNTestMe *target = [MLNTestMe alloc];
MLNTestMe *var = [target init];
// code 2 - 2
// 我再这么调,也没问题
MLNTestMe *target = [[MLNTestMe alloc] init];
看来可以不返回self,问题应该在NSInvocation上。
It is conventional, but not required, for an init method to return self. < 出自Clang文档 >
到底是哪里多一次release, 实在没有思路… 反汇编试试 ?
// 反汇编 code 1 - 1
// ---> 被调用的初始化方法
void * -[MLNTestMe init](void * self, void * _cmd) {
*((r31 - 0x20) + 0x10) = r29;
*((r31 - 0x20) + 0x18) = r30;
*((r31 - 0x20) + 0x8) = self;
objc_storeStrong(_cmd + 0x8, zero_extend_64(0x0)); // <- 这里release了self
r0 = zero_extend_64(0x0); // 返回 nil
return r0;
}
// 反汇编 code 1 - 2
// ---> NSInvocation 代码
void -[MLNViewController testInvoke](void * self, void * _cmd) {
*((r31 - 0x60) + 0x50) = r29;
*((r31 -