1

I have subclassed a system class (UINavigationBar, to be specific) to add some specific functionality. I've been using this everywhere, as a replacement for UINavigationBar. However, now I want to replace some UINavigationBars used in system frameworks with my custom subclass, so they provide the same behaviour. In specific, I would like the UINavigationBar in the UITabBarController's more view controller to be an instance of my class.

I thought this might be impossible, so I tried creating a category on UINavigationBar, which would propagate everywhere in the system. However, my category would need to do some custom initialisation and teardown (Subscribing and unsubscribing from a NSNotificationCenter's notification). If I overwrite the init/dealloc methods in my category, I won't be able to call the original methods (as implemented by UINavigationBar), which could be very dangerous/fatal/probably not very functional.

One potential solution is method swizzling, but I'm not quite sure how to use it and it could apparently be quite complicated.

If anyone's able to elaborate on how to implement something which would solve my problems (or some custom code for how I could use method swizzling), I'd be very grateful.

Tom H
  • 1,316
  • 14
  • 26

1 Answers1

3

You can override the +allocWithZone: method on UINavigationBar class to instead create an instance of your subclass (just as a class cluster behaves), using the method_setImplementation() function (check the Objective-C Runtime Reference for more details of the function)

chmeee
  • 3,608
  • 1
  • 21
  • 28
  • How do I get a Method parameter? I can get the IMP with IMP theImplementation = [self methodForSelector:theSelector]; And where would I call method_setImplementation? Is application:didFinishLaunching: called before nibs are loaded? Or is main() a better place? – Tom H Aug 31 '11 at 13:51
  • Woop, got it, the class_getInstanceMethod function. Sweet, thanks for your help! – Tom H Aug 31 '11 at 14:01
  • 1
    Actually, a category on UINavigationBar class that overrides +allocWithZone: to provide an instance of your subclass is cleaner. This way it's loaded before main(), so is always used in place of the original. – chmeee Aug 31 '11 at 14:02
  • Why would I need to use method_setImplementation then? – Tom H Aug 31 '11 at 14:14
  • 1
    That was the first thing that came to my mind when I answered. It's also useful for dynamically overriding methods, but in this case, it's probably unnecessary unless the existing +allocWithZone: does something more than just allocating a class instance, which I doubt it does. – chmeee Aug 31 '11 at 14:22
  • Unless... if I create a category on UINavigationBar, and overwrite allocWithZone:, then won't the overwritten implementation trickle down so when I try to call alloc on my subclass I'll enter an infinite loop? [Or do category methods not inherit like that?] – Tom H Aug 31 '11 at 14:30
  • 1
    If you write your category +allocWithZone: to simply do an NSAllocateObject([MyUINavigationBar class], 0, zone), then it'll simply create an instance of your class, no matter what (it's always better to prefix it with an: if (self == [UINavigationBar class]) / else [super allocWithZone:zone] so that other subclasses don't instead end up creating instances of your subclass by mistake) – chmeee Aug 31 '11 at 14:41
  • Just to be clear, this is not so much "overriding" `allocWithZone:` as "swizzling" it, which might help future SO spelunkers with their searches, right? – Olie Aug 04 '14 at 22:53
  • Also, `NSAllocateObject()` is not available under ARC. – Olie Aug 04 '14 at 22:55