14

I have a function:

  myFunction (MyProc callback, void * ref)

This function is called from within an Objective-C class. The function is passed a pointer to the callback (a function in the class) and a reference. The reference is necessary because the callback is called statically and therefore doesn't have a context. The ref can be used to provide a context to the callback.

I want to be able to pass the Objective-C class as the reference. So the question is:

How do I cast an NSObject to a void * and how do I cast a void * as an NSObject.

Thanks in advance.

James Andrews
  • 3,257
  • 3
  • 31
  • 45

3 Answers3

38

Do something like this:

void func(void *q)
{
    NSObject* o = CFBridgingRelease(q);
    NSLog(@"%@", o);
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSObject* o = [NSObject new];
        func((void*)CFBridgingRetain(o));
    }
    return 0;
}

Note that CFBridgingRetain() and CFBridgingRelease() are macros around compiler attributes. Feel free to use either. I like the API variant as it is in more common use in our codebases and it is more explicitly / less confusing.

CFBridgingRetain() effectively hard-retains the object that must be balanced by a CFBridgingRelease(). It also happens to return a CFTypeRef which is compatible with a cast to void*. CFBridgingRelease() effectively undoes that hard-retain and, thus, q will only remain valid within the scope that o is valid.

Valid for basic callbacks, but you'd probably not what that with a void *context; type thing that has to stick around for a while. For that:

void callback(void *context)
{
    // grab an ARC aware reference without impacting hard-retain
    NSObject* o = (__bridge NSObject *)(context);
    NSLog(@"%@", o);
}

void freeContext(void *context)
{
    // release the hard-retain
    CFBridgingRelease(context);
}

Note that Xcode is quite good about suggesting exactly what you should do if you leave out the cast / API call. It even explains the meanings of each of the alternative solutions (I relied on this heavily until I could keep 'em straight in my head).

bbum
  • 162,346
  • 23
  • 271
  • 359
8

I assume you are using ARC. You can use something like this when calling myFunction

id ref = ...; // your Objective-C object
myFunction(callback, (__bridge_retained void *) ref);

In your callback, you must transfer the ownership back:

void callback(void* refPtr) {
    id refObj = (__bridge_transfer id) refPtr;
}

Replace id with your object type as appropriated.

tia
  • 9,518
  • 1
  • 30
  • 44
  • When I try to cast to an id it says: Incompatible types casting void * to id with a bridge transfer cast – James Andrews Oct 16 '12 at 14:28
  • I put my `callback` function in my test project exactly the same as I post here and I don't see such compile error. Can you try that? – tia Oct 16 '12 at 15:09
  • 1
    If the callback will be called more than once, `__bridge_transfer` should not be used as it will transfer ownership to ARC and the object will be automatically released. – jtbandes Sep 16 '20 at 18:12
1

Answer: in no way. void * is implicitly compatible with any pointer type, so if you have an object, which is a pointer of type id (alias for struct objc_object *), you can simply pass it where the void pointer is needed, without casting. Example:

// this is the declaration of the callback function:
void callback(void *context);

// then you can call it like this:
SomeClass *obj = [[SomeClass alloc] init];
callback(obj);
  • When I try to pass 'self' to the function I get a compiler warning: saying that an implicit conversion of Objective-C pointer to C pointer type void * requires bridging cast. – James Andrews Oct 16 '12 at 14:10
  • @BenSmiley when you do that (what?), then what? –  Oct 16 '12 at 14:10
  • 1
    When I do a bridging cast I get a BAD_ACCESS exception casting the void * back to an Objective-C object. – James Andrews Oct 16 '12 at 14:15
  • @BenSmiley uhm. Try turning off ARC for a moment, then retry. What does that do? –  Oct 16 '12 at 14:15
  • Without ARC it works. But I'm planning to use ARC for this project. – James Andrews Oct 16 '12 at 14:19
  • @BenSmiley in this case, you can cast it to `(__strong id)`, I hope that will finally work... (Damn ARC!) –  Oct 16 '12 at 14:44
  • This is no longer true under the ARC compiler because `id` and `void*` are not the same thing. This decision was made exactly because casts between `void*` and `id` were rife with errors. The casting operators / API was created to support this explicitly in a fashion that both the compiler can reason about and the developer must explicitly express intent. – bbum Oct 16 '12 at 16:26
  • @bbum ***I never said `id` and `void *` were the same thing!*** I said that `void *` is implicitly compatible with any data pointer type in C. –  Oct 16 '12 at 16:28
  • The first line of your answer says ***void* is implicitly compatible with any pointer type***. `id` is a pointer type. It isn't compatible with `void*` under ARC. Thus, that statement is not true. – bbum Oct 16 '12 at 16:30
  • Specifically because you cast between `void*` and `id` without explicitly declaring the reference counting intentions across the cast. Thus, bare casts are no longer possible which is a specific divergence from the "`void*` is a pointer type anywhere/everywhere" mantra of C. – bbum Oct 16 '12 at 16:38
  • @bbum "you cast between void* and id without explicitly declaring the reference counting intentions" <-- Isn't the point that you don't have to cast at all? –  Oct 16 '12 at 16:38
  • No, the point is that you *do* have to cast because the cast requires additional annotation. Implied casts no longer work (because they were the source of so many really nasty to fix crashes when integrating C libraries into Objective-C code). Really, it is an explicit cast from `id` to `CFTypeRef`. Once that is done, `void*` works with that result just as it normally does (though `CFTypeRef` -> `void*` required the cast you see in my example, which seems... different, but preferable). – bbum Oct 16 '12 at 16:42
  • @bbum Thanks for the clarification. By the way, does ARC know about CoreFoundation types as well? If not, why casting `CFTypeRef` to `void *` is required? (I really hate ARC. It was supposed to make prorammers' life easier, but there are so many exceptions and corner cases, not to mention the ugly ownership-related qualifiers prefixed with a *double underscore* that in fact I find MRC way simpler than ARC.) –  Oct 16 '12 at 16:45
  • The intention of ARC is to know about CFType's, but I'm not sure how much support is in ToT. Not that top-of-tree's analyzer also has support for malloc()/free() verification, too. I suspect that cast is required exactly because the goal is to make the compiler fully aware of CFTypes, too, but I'll have to ask. – bbum Oct 16 '12 at 16:46