奇怪的 KERN_INVALID_ADDRESS 异常?

weird KERN_INVALID_ADDRESS exception?

我遇到了一个奇怪的 OC 异常,看起来我正在向已释放的地址发送消息,但是当我

  1. 尝试检查它是否为 NULL,它仍然崩溃。
  2. 尝试调试或添加@try @catch,它什么也没捕获,但运行了几天,根本没有崩溃。

异常

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000010
VM Region Info: 0x10 is not in any region.  Bytes before following region: 4304617456
      REGION TYPE                      START - END             [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  
      __TEXT                        100934000-100a98000        [ 1424K] r-x/r-x SM=COW  ...x/APP

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [18302]
Triggered by Thread:  22

下面的代码并不严格,只是展示了逻辑(代码是 运行 在一个串行调度队列中)

struct st_type {
    void *arg; 
};

static NSMutableDictionary *dictionary;
    
// init values
void init ()
{

    // there is a static dictionary to retain the object
    dictionary = [[NSMutableDictionary alloc]init];
    
    
    // an object reference saved in dictionary and a struct holds it's pointer
    NSObject *obj = [[NSObject alloc]init];
    
    struct st_type *st = malloc(sizeof(struct st_type));
    st->arg = (__bridge void *)obj;
    dictionary[@"cached"] = obj;
    
    // then the struct * passes to every where, I think it's safe because the object always in the dictionary.
    ...
}


// the only place to release the nsobject, so I think there is no chance to EXC_BAD_ACCESS, but ...
void release_object(struct st_type *st, NSObject obj){
    [dictionary removeObjectForKey:@"cached"];
    st->arg = NULL;
}

// some where to use the struct
void use_struct(struct st_type *st){
    if(st->arg == NULL){
        return;
    }
// if I add try catch, it never crashs
//    @try {   
    NSObject *obj = (__bridge NSObject*)st->arg;
    [obj description]; // crash here.
//    } @catch (NSException *exception) { print some log but it never reaches here... }
}

有人能帮我解决这个错误吗?

如果我理解正确的话,您想将对 Objective-C 对象的引用存储为 void *。例如,这与作为回调传递给工作表的 old-style contextuserInfo 指针类似。

参见 ARC: __bridge versus __bridge_retained using contextInfo test case for an example. I also assume you're using ARC (please read Casting and Object Lifetime Semantics)。

假设结构的生命周期比 Objective-C 对象的生命周期长,并且您明确地在结构中设置和释放对象,那么您不需要字典来进行内存管理。

分配结构和对象后(分别使用 malloc[[XX alloc] init]),您可以将对象的所有权转移出 ARC 并使用 st->arg 将其存储在 st->arg 中=17=]投.

要使用该对象,请使用 (__bridge NSObject *) 进行转换。这不会改变所有权。

当您准备好释放对象时,通过使用 (__bridge_transfer NSObject *) 强制转换将所有权传回给 ARC。然后你可以将 void * 指针设置为 NULL.

总的来说是这样的:

struct st_type {
    void *arg; 
};

void init()
{
    NSObject *obj = [[NSObject alloc] init];
    struct st_type *st = malloc(sizeof(struct st_type));
    // transfer ownership out of ARC
    st->arg = (__bridge_retained void *)obj;
}

void use_struct(struct st_type *st){
    // no change in ownership
    NSObject *obj = (__bridge NSObject *)st->arg;
    // use `obj`
}

void release_object(struct st_type *st){
    // transfer ownership back to ARC
    NSObject *obj = (__bridge_transfer NSObject *)st->arg;
    st->arg = NULL;
}