Marco
Marco

Reputation: 803

Alternative ways to use objc_msgSend without a cast with the new runtime

I am writing an objc bridge and I found a very efficient way to call objc methods using objc_msgSend.

Basically the code was able to produce a macro that pass to objc_msgSend the right number of parameters from a NSArray (metamacros.h required).

#import "metamacros.h"

#define CFIEXTRACTARGS(COUNT, ARR) \
, ARR[COUNT] \

#define objc_call(RECIEVER, SELECTOR, COUNT, ARR) \
objc_msgSend(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
) \

Everything works as expected and pretty well but unfortunately I just discovered the due to Apple changes in the most recent runtime, objc_msgSend cannot be directly called without an explicit cast to the right function pointer.

From: https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html : "Although the prototype for the message functions has a variadic form, the method function that is called by the Objective-C runtime does not share the same prototype. The Objective-C runtime directly dispatches to the function that implements the method, so the calling conventions are mismatched, as described previously. Therefore you must cast the objc_msgSend function to a prototype that matches the method function being called."

Example:

- (int) doSomething:(int) x { ... }
- (void) doSomethingElse {
    int (*action)(id, SEL, int) = (int (*)(id, SEL, int)) objc_msgSend;
    action(self, @selector(doSomething:), 0);
}

Obviously I cannot cannot create a cast for every functions combination so I wondering what could be a valid alternative. An NSInvocation would be really too slow.

Upvotes: 3

Views: 1304

Answers (1)

CRD
CRD

Reputation: 53000

A bit confused as using an NSArray for your arguments surely means they must all be object types?

However, apart from that issue there is no need to stop using your macro based approach, just add the cast as an argument:

#define objc_call(RECIEVER, TYPE, SELECTOR, COUNT, ARR) \
(TYPE objc_msgSend)(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
)

And use it as follows:

- (NSString *) doSomething:(NSString *) x { ... }

(note use of NSString - an object type, unlike the int in the question)

- (void) doSomethingElseToo
{
   NSArray *args = @[ @"too" ];
   NSString *res = objc_call(self, (NSString * (*)(id, SEL, NSString *)), @selector(doSomething:), 1, args);
   NSLog(@"res = %@", res);
}

HTH

Addendum

@RichardJ.RossIII has suggested in the comments that he believes you wish to avoid supplying any cast at all; not that you wish to avoid have different macros for different types, which I took to be the case and provided a solution above.

Given that you wish to avoid NSInvocation let's look at your macro again. As already noted it uses an NSArray to extract the arguments from, so every argument is an object, i.e. of type id. If all the arguments are of the same type the number of possible casts reduces dramatically.

We've consider the arguments, how many return types do you wish to support? Let's assume for the moment it is one, id. The following two macros will do the job up to 19 arguments:

#define metamacro_cast(COUNT) \
metamacro_at(COUNT, \
(id (*)(id, SEL)), \
(id (*)(id, SEL, id)), \
(id (*)(id, SEL, id, id)), \
(id (*)(id, SEL, id, id, id)), \
... // repeat until there are 19 id's
)

#define objc_call2(RECIEVER, SELECTOR, COUNT, ARR) \
(metamacro_cast(COUNT) objc_msgSend)(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
)

Use of this macro is exactly the same as yours, no extra argument:

- (void) doSomethingElseThree
{
   NSArray *args = @[ @"three" ];
   NSString *res = objc_call2(self, @selector(doSomething:), 1, args);
   NSLog(@"res = %@", res);
}

If you wish to support more than one return type you'll either need multiple objc_call_returning_type macros; or write a macro which accepts just the return type, unlike my first one which took the whole cast, and try some more macro gymnastics with metamacros.h.

Upvotes: 1

Related Questions