views:

50

answers:

3

Objective-C keeps all its methods in a huge hashtable - so shouldn't it possible to patch this table and replace an existing method with my own patched method (which then calls the original)?

I need a way to hook up the NSWindow KeyUp method in a window which i can't subclass cause it's already created.

I need some code or at least some keywords i can use for further searching.

A: 

Of course it is possible. In fact, you don't even need to look into the hash table — there's standard API for this.

For example:

typedef void (*NSWindow_keyUp__IMP)(NSWindow* self, SEL _cmd, NSEvent* evt);
static NSWindow_keyUp__IMP original_NSWindow_keyUp_;

void replaced_NSWindow_keyUp_(NSWindow* self, SEL _cmd, NSEvent* evt) {
  NSLog(@"Entering keyUp:. self = %@, event = %@", self, evt);
  original_NSWindow_keyUp_(self, _cmd, evt);
  NSLog(@"Leaving keyUp:. self = %@, event = %@", self, evt);
}

...

Method m = class_getInstanceMethod([NSWindow class], @selector(keyUp:));
original_NSWindow_keyUp_ = method_setImplementation(m, replaced_NSWindow_keyUp_);
KennyTM
+2  A: 

You should NOT swizzle methods for this. This is deprecated behavior. This will affect ALL windows in your app not just the one you wanted to change. However, what you should do instead is to subclass NSWindow already and then change the class of that window at runtime. This can be done using this runtime function:

Class object_setClass(id object, Class cls)

Reference is here: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/doc/uid/TP40001418-CH1g-SW12

Your code should then look like this:

object_setClass(theWindow, [MyWindowSubclass class]);

On problem you might experience is that window already being a subclass of NSWindow. If that's the case there are more complicated ways to achieve this. You can construct a class dynamically at runtime. Here's some more code. Given that window is the target window:

Class newWindowClass = objc_allocateClassPair([window class], "MyHackyWindowSubclass", 0);
Method upMethod = class_getInstanceMethod(newWindowClass, @selector(keyUp:));
method_setImplementation(upMethod, new_NSWindow_keyUp_);
object_setClass(window, newWindowClass);

I'm not totally sure this does not change the implementation of the superclass. The documentation is a bit unspecific about it. However, you should still try it. If it does not work, replace the second and third line by this one:

class_replaceMethod(newWindowClass, @selector(keyUp:), new_NSWindow_keyUp_, "v@:@");

In any case you need to define the new Method implementation. It could look like that (partially by KennyTM):

void new_NSWindow_keyUp_(NSWindow* self, SEL _cmd, NSEvent* evt) {
  [super keyUp: evt];
  ... // do your changes
}
Max Seelemann
+1, I didn't think of that. Although I still wonder why the OP can't just change the window's identity in the nib file...
David
He said the window already exists, so I assume that it comes from a framework or so. Not that I think this is any good design to alter framework-components...
Max Seelemann
The reason is that i have to subclass the menu window to solve my problem described here:http://stackoverflow.com/questions/3429749/cocoa-custom-nsview-in-nsmenuitem-does-not-repsonse-to-enter-key
Lothar
I've updated my answer to cope with an unknown private NSWindow class, just as it might exist for NSMenus.
Max Seelemann