如何编写一个自定义初始化程序来阻止调用viewDidLoad?

时间:2011-10-22 20:36:56

标签: objective-c uiviewcontroller initialization ios5

我想为UIViewController构建一个自定义的init方法,但是在浏览互联网之后,特别是在SO中我对指定的初始化器感到困惑。

我有一个UIViewController的子类,带有这两个初始化器:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  if ( self ) {

  }
  return self;
}

- (id) initWithFilename:(NSString *)aFilename {
  self = [self initWithNibName:@"WallpaperDetailsViewController" bundle:nil];
  if ( self ) { 
    self.filename = aFilename;
  }
  return self;
}

然后我有一个viewDidLoad方法根据filename属性自定义视图:

- (void)viewDidLoad {
  [super viewDidLoad];

  // Create a UIImageView to display the wallpaper
  self.wallpaper = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.filename]];
  // ...
}

在另一个UIViewController中,我拨打以下电话:

WallpaperDetailsViewController *detailsViewController = [[WallpaperDetailsViewController alloc] initWithFilename:@"foobar.png"];
[[self navigationController] pushViewController:detailsViewController animated:YES];

结果是viewDidLoad由于[self initWithNibName:]而被调用,UIImageView未初始化self.filename,因为viewDidLoad:为空。

根据其他SO问题和答案,这应该是预期的行为。我不确定这是因为我自己在iOS 5之前的其他项目中的经验。我的问题是:

如何确保在initWithFilename:后调用initWithFilename:,而不是在initWithNibNameOrNil:bundle:和{{1}}之间调用?

如果不可能,我如何实现接收自定义数据的初始化方法来创建和自定义视图?

谢谢!

4 个答案:

答案 0 :(得分:2)

我找到了问题。

WallpaperDetailsViewController不会直接从UIViewController继承,而是从我已实施的其他自定义UIViewController继承。

问题是什么?我已经在父initWithNibName方法中初始化了子视图,而不是遵循延迟加载技术并在viewDidLoad中执行。当WallpaperDetailsViewController调用其父初始值设定项时,它会变得混乱并导致viewDidLoad无法正常运行。

解决方案?我将父类中的每个子视图初始化移动到其viewDidLoad方法,并使WallpaperDetailsViewController的原始实现保持不变。现在一切都按预期工作了

感谢@Josh Caswell和@logancautrell

答案 1 :(得分:1)

您不需要initWithNibName:bundle:的空实现。此外,看起来你的类正在建立它的指定初始化器为initWithFilename:如果这是真的,initWithFilename:应该调用超类的D.I。:

- (id) initWithFilename:(NSString *)aFilename {
  // Call super's designated initializer
  self = [super initWithNibName:@"WallpaperDetailsViewController" 
                         bundle:nil];
  if ( self ) { 
    self.filename = aFilename;
  }
  return self;
}

规则是类中的所有初始值设定项应该调用类的D.I.和D.I.本身应该调用超类的D.I。

在初始化程序完成之前,您发布loadView:的原因并不完全清楚。 Logancautrell的评论建议在视图加载方法中设置断点是好的。

答案 2 :(得分:0)

为什么不在每次设置文件名时使用自定义setter来初始化UIImage的filename属性?

或者,或者在viewWillAppear中设置文件名属性的UIImage:而不是viewDidLoad。

答案 3 :(得分:0)

首先,建议您不要在初始化程序中使用点语法。有关一些好的讨论,请参阅以下内容:

Objective-C Dot Syntax and Init

其次,您可以做的是在初始化程序中分配图像。所以你可以按照

的方式做点什么
- (id) initWithFilename:(NSString *)aFilename {
   self = [self initWithNibName:@"WallpaperDetailsViewController" bundle:nil];
   if ( self ) { 
      filename = [aFilename retain];
      wallpaper = [[UIImageView alloc] initWithImage:[UIImage imageNamed:aFileName]];
   }
   return self;
}

这将允许您在调用viewDidLoad之前完成所有设置并保持良好状态。

祝你好运!