I am facing a weird OC Exception, it looks that I am sending a message to a released address, but when I
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?
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;
}