0

I am facing a weird OC Exception, it looks that I am sending a message to a released address, but when I

  1. try to check if it is NULL, it still crashs.
  2. try to debug or add @try @catch, it catches nothing but runs for days, no crash at all.

exception

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

code below is not strict ,just shows the logic ( code is running in one serial dispatch queue )

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... }
}

Could anyone help me what I can do next to fix this error?

Henry
  • 531
  • 3
  • 12
  • 1
    This is an atypical pattern in ObjC. What are you trying to accomplish? Is the purpose of `st_type` to pass around an untyped object while `dictionary` handles memory management? There might be another way to do this. – sbooth Oct 05 '20 at 14:45
  • it is sample, not real in project. The code written like that is because it's a bridge between oc and c library. The st_type is a struct managed by c lib. – Henry Oct 05 '20 at 15:27
  • Is the lifetime of `obj` tied to the lifetime of the struct? What is the purpose of `dictionary`? – sbooth Oct 05 '20 at 15:36
  • the dictionary is the store of oc object to prevent it releasing, struct's lifetime is longer than oc object, but when oc needs to dealloc, it will set struct's pointer to NULL. – Henry Oct 05 '20 at 15:42

1 Answers1

0

If I understand correctly you want to store a reference to an Objective-C object as a void *. This is a similar use case as the old-style context or userInfo pointers that were passed as callbacks to sheets, for example.

See 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).

Assuming the lifetime of the struct is longer than that of the Objective-C object, and that you explicitly set and release the object in the struct, you shouldn't need a dictionary for memory management.

Once the struct and object are allocated (using malloc and [[XX alloc] init] respectively), you can transfer ownership of the object out of ARC and store it in st->arg using a (__bridge_retained void *) cast.

To use the object, cast using (__bridge NSObject *). This will not change ownership.

When you are ready to release the object, pass ownership back to ARC by casting using (__bridge_transfer NSObject *). Then you can set the void * pointer to NULL.

So overall something like:

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;
}
sbooth
  • 16,646
  • 2
  • 55
  • 81
  • Although it's not the key reason of the exception, I think it's the best practice of using `bridge`. Thank you. – Henry Oct 10 '20 at 13:11