lukaskollmer / objc

🔮 NodeJS ↔ Objective-C bridge (experimental)
MIT License
98 stars 20 forks source link

Correct way to use ffi to map dispatch_create_queue? #5

Closed jmbldwn closed 6 years ago

jmbldwn commented 6 years ago

I need to use dispatch queues in my app. It's not super clear to me whether I have the mapping of this call right.

Apple docs say the call looks like this:

dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

After some amount of whack-a-mole, I arrived at this code which seems to not cause an exception:

const c = new ffi.Library(null, {
    'dispatch_queue_create': ['pointer', ['string', 'long']]
});

I call the function like this:

let queue = c.dispatch_queue_create('myQueue', 0);

But I am suspicious of defining the dispatch_queue_attr_t parameter as a 'long'.

What should it be?

jmbldwn commented 6 years ago

Additional probing yields I can map dispatch_queue_create as:

const c = new ffi.Library(null, {
    dispatch_queue_create: ['pointer', ['string', 'pointer']]
});

Which seems more correct, as I need to pass it an objective c NULL when I use it, and NULL is a pointer to address 0, not just 0.

I read more about 'ref' and it looks like this call to use the function may be closer:

let queue = c.dispatch_queue_create(ref.allocCString('myQueue'), ref.NULL);

That call does not throw exceptions, but if I inspect 'queue' after it, it seems to have a type of 'void'"

queue.type
Object {size: 0, indirection: 1, get: , set: , name: "void", …}
ffi_type:Uint8Array(0) []
get:function get(buf, offset) { … }
indirection:1
name:"void"
set:function set(buf, offset, val) { … }
size:0

And of course, in the end, it doesn't appear to be working, so I still have something wrong.

lukaskollmer commented 6 years ago

dispatch_queue_attr_t is declared as

typedef NSObject<OS_dispatch_queue_attr> *dispatch_queue_attr_t;

so you'd probably need to make it a pointer in the ffi declaration

jmbldwn commented 6 years ago

It seems as if this dispatch_queue_attr_t parameter is my current blocking issue. If I follow the call I end up getting an NSInvalidArgumentException (see below), and spelunking around in the debugger leads to this happening when in instance.js when processing this parameter.

Is ref.NULL the right way to pass NULL to an objective-c method?

Error: NSInvalidArgumentException *** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: get)
instance.js:110
    at Instance.call (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:110:13)
    at Object.apply (/Users/jim/development/node/objc/test/node_modules/objc/src/proxies.js:21:19)
    at Function.ns (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:249:20)
    at argv.map (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:90:31)
    at Array.map (<anonymous>)
    at Instance.call (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:68:23)
    at Object.apply (/Users/jim/development/node/objc/test/node_modules/objc/src/proxies.js:21:19)
    at Function.ns (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:249:20)
    at argv.map (/Users/jim/development/node/objc/test/node_modules/objc/src/instance.js:90:31)
    at Array.map (<anonymous>)
lukaskollmer commented 6 years ago

as of right now, you should use javascript's null instead of ref.NULL if you want to pass nil to an objc method.

i'll add support for ref.NULL in a future release

jmbldwn commented 6 years ago

JS null makes more sense. Would prefer to not have to invoke ref directly, but I'm shooting in the dark a bit with this one.

Should I be using let ref.allocCString('myQueue') for the string parameter?

let queue = c.dispatch_queue_create(ref.allocCString('myQueue'), null);

lukaskollmer commented 6 years ago

that shouldn't be necessary, the ffi module also accepts JavaScript strings for string parameters

jmbldwn commented 6 years ago

Thanks. I'll close this issue as I think I've got clarification on how to call this function properly (and hopefully this isn't my issue). To summarize, the function I'm calling is:

dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

I use ffi to access it:

const ffi = require('ffi');
const c = new ffi.Library(null, {
    dispatch_queue_create: ['pointer', ['string', 'pointer']]
});

and call it thus:

let queue = c.dispatch_queue_create('myQueue', null);