NSInvocation与init方法簇引发的内存问题

  • 缘起

使用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 - 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值