Chris
Chris

Reputation: 2465

Subclassing in Objective C Runtime

I am attempting to implement a solution from How to set canBecomeKeyWindow? Into my native C application using Objective-C runtime (The app is already written with objective C Runtime). Is there a way to create a subclass purely in Objective-C Runtime?

Right now I just create NSWindow object but need to be able to create my own so I can override the function specified in that question.

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

Upvotes: 0

Views: 372

Answers (2)

storoj
storoj

Reputation: 1867

The signature of can_become_key_window_true is slightly incorrect. According to the documentation (https://developer.apple.com/documentation/objectivec/objective-c_runtime/imp?language=objc) the function should have at least two arguments: "self" and "_cmd". So the signature should be like:

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

You could also use @encode to construct the type encoding for the function.

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

... or you could get a method from UIWindow and get its type encoding like:

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

And as you might have noticed you could use sel_getUid() instead of sel_registerName as you expect this selector to be already registered by this time (because you are about to override an existing method).

To allocate a new instance you could use

window = class_createInstance(__UIWindow);

Upvotes: 1

Chris
Chris

Reputation: 2465

Figured it out after a lot of code searching:

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

And then can_become_key_window_true is defined as:

static bool can_become_key_window_true() {
    return true;
}

I use 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 to register my new class before using it as I would a normal NSWindow.

Upvotes: 0

Related Questions