问题
I want to create a subclass of UINavigationController which always starts with the same root controller. Nothing special, so (to me) it makes perfect sense to override the init method like this:
- (id) init {
rootController = [[MyController alloc] init];
if (self = [super initWithRootViewController:rootController]) {
// do some more initialization
}
return self;
}
This obviously creates a problem, because [super initWithRootViewController] will call [UINavigationController init], which is of course our overridden init method, so infinite recursion will occur.
I don't want to create an init method with a different name like "initCustom".
There's currently only one solution I can come up with, but I really hate this kind of hack:
- (id) init {
if (initCalled)
return self;
initCalled = YES;
rootController = [[MyController alloc] init];
if (self = [super initWithRootViewController:rootController]) {
// do some more initialization
}
return self;
}
So my question is: is there a better way of handling this? I'm sure I'm missing something very obvious, but I don't see it.
EDIT: The reason why I want to do this, as can be seen in one of my comments below:
I want to create a navigation controller that always starts with a specific view controller. I want to hide this from the consumer of the class. No need to expose things that don't need exposing. Makes life a lot easier, and code a lot cleaner (one of the reasons encapsulation was invented, right?)
回答1:
First of all UINavigationController is not intended for subclassing.
Anyway, the simplest way to do that is to override initWithRootViewController:
- (id) initWithRootViewController:(UIViewController) viewController {
return [super initWithRootViewController:[[[MyController alloc] init] autorelease]];
}
You better don't autorelease MyController, but you understand the idea...
回答2:
I've read a lot of things here on WHY this behaves like this, but no real clean solution. As Gcamp pointed out, the documentation explicitly tells us not to subclass UINavigationController.
So I started thinking: if subclassing is not allowed, that leaves us with encapsulation, which seems like an acceptable solution to solve this:
@interface MyNavigationController : UIViewController {
UINavigationController *navController;
UIViewController *myController;
}
Implementation:
@implementation MyNavigationController
- (id) init {
if (self = [super init]) {
myController = [[MyController alloc] init];
navController = [[UINavigationController alloc] initWithRootViewController:myController];
}
return self;
}
- (void) loadView {
self.view = navController.view;
}
- (void) dealloc {
[navController release];
[myController release];
[super dealloc];
}
@end
I'm not an expert in Objective-C, so this may not be an the best way to do this.
回答3:
Did you actually test this code? Why should [super initWithRootViewController]
call the overriden init
method? It will call the [super init]
method, which is (as you said) [UINavigationController init]
(not your overriden init
).
回答4:
Just override and use -initWithRootViewController: designated initializer. You can pass nil as argument
- (id) initWithRootViewController:(UIViewController*)ignored {
rootController = [[MyController alloc] init];
if (self = [super initWithRootViewController:rootController]) {
// do some more initialization
}
return self;
}
Read more here: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/ObjectiveC/Articles/ocAllocInit.html#//apple_ref/doc/uid/TP30001163-CH22-106376
来源:https://stackoverflow.com/questions/1888970/custom-init-method-in-objective-c-how-to-avoid-recursion