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?

1

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Swapping the IMPs sounds terrifying from multithreading and performance perspectives, no?
@JesseRusak I agree. But I can't think any other way to achieve it.
Thanks for your answer, but I'm still seeking for other answers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.