7

I try to figure out how things really work. So I thought when I would overwrite certain methods using categories, I would get interesting NSLogs.

@implementation UIView(Learning)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"-hitTest:withEvent: event=%@", event);
    return [self hitTest:point withEvent:event];
}
@end

super and self don't work here. Is there a way to call the original implementation of -hitTest:withEvent:? What I want is an NSLog every time -hitTest:withEvent: is called on an UIView.

It's just for personal learning purposes. I want to see the event delivery in action.

chubao
  • 5,871
  • 6
  • 39
  • 64
Proud Member
  • 40,078
  • 47
  • 146
  • 231
  • Super doesn't work in a category because you're not calling it on the same class, you're calling it on the super of class. So in this case, the super is a UIResponder which doesn't have a hitTest:withEvent method. Calling self just results in an infinite loop because you're overriding hitTest:withEvent for "self". – Tom Irving Nov 28 '10 at 01:03
  • My comment looks a little out of place here, but it made sense before the previous commented deleted his. – Tom Irving Nov 28 '10 at 01:14

3 Answers3

16

You can do it, but not using a category. A category replaces a method. (Warning, car analogy) If you have a car, and you destroy that car and replace it with a new car, can you still use the old car? No, because it is gone and does not exist anymore. The same with categories.

What you could do is use the Objective-C runtime to add the method under a different name at runtime (say, "bogusHitTest:withEvent:"), then swap the implementations of hitTest:withEvent: and bogusHitTest:withEvent:. That way when the code calls hitTest:withEvent:, it's going to execute the code that was originally written for bogusHitTest:withEvent:. You can then have that code invoke bogusHitTest:withEvent:, which will execute the original implementation.

So the bogus method would look like:

- (UIView *) bogusHitTest:(CGPoint)point withEvent:(UIEvent *)event {
  NSLog(@"executing: %@", NSStringFromSelector(_cmd));
  return [self bogusHitTest:point withEvent:event];
}

The code to swap the methods would be something along the lines of:

Method bogusHitTest = class_getInstanceMethod([UIView class], @selector(bogusHitTest:withEvent:));
Method hitTest = class_getInstanceMethod([UIView class], @selector(hitTest:withEvent:));
method_exchangeImplementations(bogusHitTest, hitTest);
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
3

What you want to do is called method swizzling : http://www.cocoadev.com/index.pl?MethodSwizzling

Thomas Joulin
  • 6,590
  • 9
  • 53
  • 88
  • I'm not sure, but the article seems to be a little bit outdated. Dave DeLong posted a solution that works fairly well with far less code. Thanks anyways :-) – Proud Member Nov 28 '10 at 21:21
  • @BugAlert just so you're aware, my code *is* method swizzling. The linked article just has a lot more information about it. :) – Dave DeLong Nov 28 '10 at 21:29
0

Unfortunately, no, there is no way to call the original implementation of a method that you override. Once you implement it in the category you have eliminated the original method.

Sending the same message to super should work in your method; it will call the method on the superclass as normal (if there is one).

Sending the same message to self will create an infinite loop, as I'm sure you've discovered.

Adam Milligan
  • 2,826
  • 19
  • 17
  • 1
    As Adam has said, this isn't possible in this way, but you could take a look at Method Swizzling. There's a great example here: http://github.com/marksands/UITextViewLinkOptions – Tom Irving Nov 28 '10 at 01:13