在 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);
我正在尝试使用 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);