4

This recent question got me thinking about category conflicts, specifically involving the UIViewControllerRotation category within UIViewController.h. This category includes the shouldAutorotateToInterfaceOrientation:. If one wanted to override this method (via a category) and make it run the same code for every UIViewController (as the linked post is trying to accomplish) then they'd have two categories with the same method - something that I read leads to undefined behavior.

I gave this a try and, for a standard view-based application in iOS 4.3, the rotation logic fell back to the default, portrait only rotation, effectively ignoring the category. This was with no shouldAutorotateToInterfaceOrientation: method defined in my UIViewController subclass. Interestingly, when I did define the shouldAutorotateToInterfaceOrientation: method and simply called return [super shouldAutorotateToInterfaceOrientation:] then the category was called. So this leaves me with two question:

  1. Are all bets off (behavior-wise) when you have conflicting category methods?
  2. Is one out of luck if they wanted to override a category method w/ out inheritance?

Any feedback is much appreciated! Thanks.

Community
  • 1
  • 1
Sam
  • 2,707
  • 20
  • 25

3 Answers3

8

Suggestion; don't do that!!!

You really don't want to override behavior of all instances of any given UIKit class across all instances within your application for a couple of reasons.

First, you'll be changing the behavior of the class in ways that the class was likely not designed to deal with. As well, instances that are in UI hierarchies that are beyond your control (embedded complex framework components, typically) will behave differently, too.

Secondly, there is no way your implementation can know the internal implementation details of the original implementation to the point of not risking failing to make the same assumptions. While you could do something swizzle-like, down that path is extremely fragile and will be a maintenance nightmare.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • +1 Despite loading the gun for you, I do agree with bbum that it is almost always a bad idea. The cases where I use this technique are related to debugging, profiling and the like. – Rob Napier Mar 10 '11 at 23:42
  • +1 I absolutely agree it's a bad idea. I was mostly curious on a technical level and the UIKit/UIViewController context was just used as an example. Thanks for the input. – Sam Mar 10 '11 at 23:49
  • +1 bad idea for an _uncontrolled_ way to override framework-level class behavior – Martin Babacaev Mar 11 '11 at 00:25
2

It is undefined what happens if you have conflicting implementations of a category method. UIViewController provides a default implementation of shouldAutorotateToInterfaceOrientation:, so you cannot attach your own implementation via a category.

You may, however, hijack -[UIViewController shouldAutorotateToInterfaceOrientation:] and insert your own implementation. I have a discussion on that in Hijacking with method_exchangeImplementations().

This must be used very carefully, and is dependent on certain implementation details of UIViewController which might change. So I don't recommend this approach for most problems. Generally if you want a "special rotating view controller" that is what subclassing is for. You make your MYSpecialViewController and you subclass from that. Using hijacking (or any other mechanism that inserts itself into the object model dynamically) will impact every view controller in the system, including Apple-provided ones who may or may not react well to it. But for certain problems, it is a very useful solution.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Great answer. As bbum suggests, I will certainly not use these techniques on a regular basis. Thanks again for the input. – Sam Mar 10 '11 at 23:58
0

I also do agree with bbum.

However, you can safely use Class Clusters approach. In that way you'll achieve additional benefit: accessing original shouldAutorotateToInterfaceOrientation: method through super.

Implementation may look like the following :

@interface UIViewController (MyViewController)
– (id) initMyControllerWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle;
@end

@implementation UIViewController (MyViewController)
– (id) initMyControllerWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
    [self release];
    return [[InheritedUIViewController alloc] initWithNibName:nibName bundle:nibBundle];
}
@end

Overriding shouldAutorotateToInterfaceOrientation: through simple inheritance :

@interface InheritedUIViewController: UIViewController 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
@end

@implementation InheritedUIViewController: UIViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// some implementation
return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; // optional
//return yourValue;
}
@end

So now, initializing UIViewController through categorized initMyControllerWithNibName::, InheritedUIViewController's shouldAutorotateToInterfaceOrientation: method implementation will be called, with possibility to access its original implementation.

Community
  • 1
  • 1
Martin Babacaev
  • 6,240
  • 2
  • 19
  • 34