3

I need to implement a universal C function which will invoke super's implementation and return the value. I will inject this function to a target class at runtime. The number of arguments of the target selector could be any number, and would be known only at runtime.

I implement a C function as following, but I don't know how to invoke the super's implementation with the variable argument list:

id forwardMessageToSuper (id self, SEL _cmd, ...) {

    // Is this `super` definition right?
    struct objc_super super = {
        self,
        [self superclass]
    };  

    // How can I pass the variable argument list (...) to super? 
    id result = objc_msgSendSuper(&super, _cmd, ...);

    return result;
}

Another question: how and when to use objc_msgSendSuper_stret to invoke super's implementation?

Xaree Lee
  • 3,188
  • 3
  • 34
  • 55
  • 2
    You can't in general pass variadic arguments on to another variadic function. You're going to need to use libffi for this. For the struct return part, cf. [Can Foundation tell me whether an ObjC method requires a special structure return?](http://stackoverflow.com/q/18143636) – jscs Dec 02 '13 at 01:31

1 Answers1

2

One solution is swizzle out the IMP for the method with super's IMP and call it and then swizzle back.

This is how wax did it.

Code taken from https://github.com/probablycorey/wax/blob/master/lib/wax_instance.m

static int superMethodClosure(lua_State *L) {
    wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
    const char *selectorName = luaL_checkstring(L, lua_upvalueindex(1));

    // If the only arg is 'self' and there is a selector with no args. USE IT!
    if (lua_gettop(L) == 1 && lua_isstring(L, lua_upvalueindex(2))) {
        selectorName = luaL_checkstring(L, lua_upvalueindex(2));
    }

    SEL selector = sel_getUid(selectorName);

    // Super Swizzle
    Method selfMethod = class_getInstanceMethod([instanceUserdata->instance class], selector);
    Method superMethod = class_getInstanceMethod(instanceUserdata->isSuper, selector);        

    if (superMethod && selfMethod != superMethod) { // Super's got what you're looking for
        IMP selfMethodImp = method_getImplementation(selfMethod);        
        IMP superMethodImp = method_getImplementation(superMethod);
        method_setImplementation(selfMethod, superMethodImp);

        methodClosure(L);

        method_setImplementation(selfMethod, selfMethodImp); // Swap back to self's original method
    }
    else {
        methodClosure(L);
    }

    return 1;
}

As @JesseRusak point out, this is not thread-safe so you need to lock it. Also it may have problem with recursive call.

for objc_msgSendSuper_stret you can check this answer

objc_super superstruct = {self, [SuperClass class]};
CGSize size = ((CGSize(*)(struct objc_super *, SEL, NSString*))objc_msgSendSuper_stret)(&superstruct , @selector(contentSize:), text);

also for this code

struct objc_super super = {
    self,
    [self superclass]
}; 

generally you don't want to use [self superclass] because it may cause infinite loop. you can try to figure out what happen when subclass is calling super. you need to somehow make sure calling super is not calling this method again.

Community
  • 1
  • 1
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143