如何在Objective-C中编写调用`super`实现的c函数? [英] How to write c function which invoke `super`'s implementation in Objective-C?

查看:35
本文介绍了如何在Objective-C中编写调用`super`实现的c函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要实现一个通用的 C 函数,它将调用 super 的实现并返回值.我将在运行时将此函数注入目标类.目标选择器的参数数量可以是任意数量,并且只能在运行时知道.

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.

我实现了一个 C 函数如下,但我不知道如何使用变量参数列表调用 super 的实现:

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;
}

另一个问题:如何以及何时使用 objc_msgSendSuper_stret 来调用 super 的实现?

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

推荐答案

一种解决方案是用 super 的 IMP 调出方法的 IMP,然后调用它,然后调回来.

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

这就是 wax 做到的.

代码取自 https://github.com/probablycorey/蜡/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;
}

正如@JesseRusak 指出的那样,这不是线程安全的,因此您需要锁定它.它也可能有递归调用的问题.

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

对于objc_msgSendSuper_stret,您可以查看此答案

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

也用于此代码

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

一般你不想使用 [self superclass] 因为它可能会导致无限循环.你可以试着弄清楚当子类调用 super 时会发生什么.您需要以某种方式确保调用 super 不会再次调用此方法.

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.

这篇关于如何在Objective-C中编写调用`super`实现的c函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆