【问题标题】:Subclassing in Objective C RuntimeObjective C 运行时中的子类化
【发布时间】:2020-04-28 17:12:22
【问题描述】:

我正在尝试使用 Objective-C 运行时将How to set canBecomeKeyWindow? 的解决方案实施到我的本机 C 应用程序中(该应用程序已经使用 Objective C 运行时编写)。有没有办法纯粹在 Objective-C Runtime 中创建子类?

现在我只创建 NSWindow 对象,但需要能够创建自己的对象,以便我可以覆盖该问题中指定的函数。

objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc"));

【问题讨论】:

  • 我们确定 C 标签适用于这个问题吗?
  • 是的,这个应用程序是使用 Objective-C 运行时用 C 语言编写的,不过我想通了,见下文。

标签: c nswindow objective-c-runtime


【解决方案1】:

can_become_key_window_true 的签名略有错误。根据文档 (https://developer.apple.com/documentation/objectivec/objective-c_runtime/imp?language=objc),该函数应该至少有两个参数:“self”和“_cmd”。所以签名应该是这样的:

static bool can_become_key_window_true(__unused id _self, __unused SEL _cmd) {
    return true;
}

您也可以使用@encode 来构造函数的类型编码。

char encoding[10]; // should be enough
snprintf(encoding, 10, "%s%s%s", @encode(BOOL), @encode(id), @encode(SEL));

...或者您可以从UIWindow 获取方法并获取其类型编码,例如:

Method m = class_getInstanceMethod(objc_lookUpClass("UIWindow"), sel_getUid("canBecomeKeyWindow"));
const char *encoding = method_getTypeEncoding(m);

您可能已经注意到,您可以使用 sel_getUid() 而不是 sel_registerName,因为您希望此时此选择器已经注册(因为您将要覆盖现有方法)。

要分配一个你可以使用的新实例

window = class_createInstance(__UIWindow);

【讨论】:

  • 有趣,我的回答对我来说效果很好。我想知道为什么我不需要can_become_key_window()的正确签名
  • IMO 签名是调用者和函数体的“约定”。所以调用者知道如何传递参数,而函数知道如何“解包”它们。从技术上讲,您可以将任何函数转换为任何签名并调用它,将任何垃圾作为参数传递。因此,如果您不在函数主体中使用任何参数,则不会发生任何可怕的事情,因为您只是不使用传递的“垃圾”数据。但我想这可能取决于架构、调用约定等。
【解决方案2】:

经过大量代码搜索后想通了:

        // Subclass NSWindow with overridden function
        Class __NSWindow =
            objc_allocateClassPair(objc_getClass("NSWindow"), "__NSWindow", 0);
        class_addMethod(__NSWindow,
                        sel_registerName("canBecomeKeyWindow"),
                        (IMP)can_become_key_window_true, "B@:");
        objc_registerClassPair(__NSWindow);

        // Allocate a new __NSWindow
        window = objc_msgSend((id)__NSWindow, sel_registerName("alloc"));

然后can_become_key_window_true定义为:

static bool can_become_key_window_true() {
    return true;
}

我使用objc_allocateClassPair 对对象进行子类化并返回该对象的Class。然后我使用class_addMethod 覆盖方法canBecomeKeyWindow。最后使用objc_registerClassPair 注册我的新课程,然后再使用它,就像我使用普通的NSWindow 一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    • 2014-08-03
    • 1970-01-01
    • 2014-08-15
    • 2014-07-26
    • 1970-01-01
    相关资源
    最近更新 更多