在 Objective C 运行时子类化

Subclassing in Objective C Runtime

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

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

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

经过大量代码搜索后弄明白了:

        // 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 to subclass the object and return a Class of that object. Then I use class_addMethod to override the method canBecomeKeyWindow. And finally use objc_registerClassPair 注册我的新 class,然后像正常 NSWindow 一样使用它。

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