Custom init method in Objective-C, how to avoid recursion?

[亡魂溺海] 提交于 2019-12-23 20:11:46

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!