如果一个类没有记录的指定初始化程序怎么办?
Posted
技术标签:
【中文标题】如果一个类没有记录的指定初始化程序怎么办?【英文标题】:What if a class has no documented designated initializer? 【发布时间】:2012-10-30 20:13:46 【问题描述】:我正在寻找有关如何处理这种情况的一般指导。这是一个具体的例子。
我是 UIImageView 的子类,并且我想重写 initWithImage 以在使用提供的图像初始化超类本身之后添加我自己的初始化代码。
但是,对于 UIImageView 没有记录的指定初始化程序,所以我应该调用哪个超类初始化程序来确保我的子类被正确初始化?
如果一个类没有指定初始化器,我应该:
-
假设调用任何类的 (UIImageView) 初始化程序是安全的?
查看指定初始化程序的超类 (UIView)?
在这种情况下,#1 似乎是答案,因为在我的重写初始化程序中执行以下操作是有意义的:
- (id)initWithImage:(UIImage *)image
self = [super initWithImage:image];
if (self)
// DO MY EXTRA INITIALIZATION HERE
return self;
【问题讨论】:
这对我来说很好。您正在调用超类的初始化程序。问题是什么? Objective-C 不提供调用指定超类的初始化程序的机制。 (AFAIK) UIImageView 将是您的超类。您可以调用(使用super
)该类的任何有效初始化程序,包括它可能继承的初始化程序(尽管其中一些可能没有意义)。
这确实工作正常,但我的问题更笼统。我的理解是子类应该总是调用超类的指定初始化程序,但是如果没有记录,我怎么知道指定初始化程序是什么?如果没有任何东西被标记为“指定”,那么调用超类中的任何初始化程序是否安全?
不知道您所说的“指定”是什么意思。您必须调用超类的初始化器*,但没有一个是“指定”的。
【参考方案1】:
UIImageView 有两个初始化器,因此您可能需要确保您的子类处理这两个初始化路径。
您可以简单地声明-initWithImage:
是您的 指定初始化程序,并且不支持所有其他初始化程序。
此外,您可以实现 -initWithImage:highlightedImage:
并抛出异常以表明它不受支持。
或者,您可以将-initWithImage:highlightedImage:
声明为您的指定初始化程序,并让-initWithImage:
调用您指定的初始化程序。
或者,您可能会发现无论您的类是使用 -initWithImage:
还是 -initWithImage:highlightedImage:
初始化,都会调用您的 -initWithImage:
初始化程序。
【讨论】:
Darren,你说 UIImageView 有 2 个初始化器,但我怎么知道哪个是 UIImageView 的指定初始化器?或者如果两者都没有被标记为“指定”,它们是否都充当指定的初始化器? UIImageView 没有指定的初始化程序(可能是因为直到 ios 3.0 才添加第二个初始化程序)。如果是这样,那么这就是您通常会覆盖的初始化程序,它将涵盖所有基础。但是由于它没有指定的初始化程序,您可能希望处理这两个初始化程序并将它们都指向您自己指定的初始化程序。否则,如果有人无意中调用了您不支持的初始化程序,则可能会产生难以追踪的错误。 @Darren - 好的,在这种情况下,您显然使用“指定”来表示init
方法,该方法在规范中描述为另一个 @ 的“父”(我的术语) 987654330@ 类的方法。例如,可能有一个initWithX:
方法和一个initWithX:andY:
方法。通常第一个(这是一个“方便”的方法)会调用第二个,所以你只需要覆盖第二个方法。规范可能会或可能不会告诉您这一点。如果不是,您需要覆盖所有在使用您的类时可能调用的init
方法,但如果您知道只使用了一个方法,请覆盖它。
@HotLicks,您需要在 Apple 的文档中搜索“指定初始化程序”一词。这不是虚构的术语。这是important concept in Cocoa。【参考方案2】:
UIImageView 文档很糟糕。它显示了两个初始化程序,但您可能会遇到没有调用它们的情况。例如,我使用的是 IB,只有 initWithCoder:
被调用。
- (id)init
return [super init];
- (id)initWithCoder:(NSCoder *)aDecoder
return [super initWithCoder:aDecoder];
- (id)initWithFrame:(CGRect)frame
return [super initWithFrame:frame];
- (id)initWithImage:(UIImage *)image
self = [super initWithImage:image];
return self;
- (id)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage
self = [super initWithImage:image highlightedImage:highlightedImage];
return self;
子类化 UIImageView 的唯一正确方法是子类化所有初始化程序,并且每个子类只能调用具有相同名称的父初始化程序。例如:
subclass -init
可以调用UIImageView -init
,但不能调用UIImageView -initWithCoder:
。
是的,没有指定真的很痛苦。
【讨论】:
这是一个常见问题——如果从 NIB 加载对象,将调用-initWithCoder:
。因此,您通常也必须覆盖它。
我不相信这种方法是安全的。由于没有指定任何初始化程序,您无法知道对super
的调用不会回调您对另一个初始化程序的覆盖。如果它们不是微不足道的,那么你可能会做两次不应该做两次的工作。 (如果它们是微不足道的,你就不会实现它们中的任何一个。)【参考方案3】:
没有指定初始化程序的危险在于,您可能调用的任何初始化程序都可以根据其他初始化程序之一完成其工作。在这种情况下,它可能会意外调用您的某个覆盖,这将无法按预期方式工作。
如果你的类只有一个初始化器并且它是超类初始化器的覆盖,那么调用它覆盖的初始化器是安全的。那是因为超级初始化器不可能(直接或间接)重新进入自己,所以它不可能重新进入你的覆盖。
你的类也可以实现任意数量的初始化器,只要它们中的 none 与超类中的 any 同名。由于您的名字是唯一的,因此任何超类初始化程序都不会意外调用它们。
【讨论】:
【参考方案4】:从NSObject
派生的每个类都有init
方法作为一个初始化器,它将为该对象执行初始化过程。因此,如果您不确定,您始终可以在自定义初始化程序中使用 self = [super init]
。考虑到UIImageView
有两个由苹果提供的初始化程序,您可能必须同时覆盖它们或向用户抛出他们无法使用此方法的异常(不推荐)。
例如:-
- (id)initWithCustomParam:(NSString *)param
if (self = [super init])
self.myparam = param;
return self;
然后你可以实现其他初始化器,
- (id)initWithImage:(UIImage *)image
if (self = [self initWithCustomParam:@"default value"])
self.image = image;
return self;
或定义,
- (id)initWithImage:(UIImage *)image customParam:(NSString *)string
if (self = [self initWithCustomParam:string])
self.image = image;
return self;
【讨论】:
【参考方案5】:另一种方法是偷懒。 您可以使用 viewDidLoad 或 viewDidMoveToSuperview 等方法进行一些设置。这实际上取决于设置何时很重要。
【讨论】:
这根本不能回答问题。 当然可以。这是一个懒惰地进行设置的地方。以上是关于如果一个类没有记录的指定初始化程序怎么办?的主要内容,如果未能解决你的问题,请参考以下文章
电脑显示“没有被指定在windows上运行,或者它包含错误。”怎么办?