【问题标题】:Inheritance: Restricting rather than extending? [duplicate]继承:限制而不是扩展? [复制]
【发布时间】:2013-05-31 16:15:10
【问题描述】:

假设你有一个 UIView 子类。您定义了一个初始化方法“myInitWithFrame: ... andWhatNot:...”。您知道您不会使用从 UIView 继承的 init 方法永远,并且您的自定义 init 方法会进行一些重要的自定义初始化,以便您希望强制客户端类从不使用继承的 initWithFrame 方法。

是否可以隐藏继承自 UIView 的标准 initWithFrame 方法?

【问题讨论】:

  • 很抱歉,它是如何重复的?

标签: ios objective-c


【解决方案1】:

实际上,您可以收到有关在子类上调用方法的编译时警告。使用__attribute((deprecated)) 属性。如果您希望人们使用-initWithPizza: 而不是-initWithFrame:,请执行以下操作:

@interface MyView : UIView
- (id)initWithPizza:(MyPizza *)pizza;
@end

@interface MyView (Deprecations)
- (id)initWithFrame:(CGRect)frame __attribute((deprecated("Use initWithPizza: instead")));
@end

-initWithFrame: 声明放在一个单独的类别中是必要的,以避免Xcode 抱怨您在标头中声明了该方法但没有实现它。既然你只是从超类继承它,那很好;您根本没有 来实现它。但是如果你想实现它来抛出异常,或者使用默认参数调用-initWithPizza:,那很好。

当然,这不会阻止 UIKit 调用-initWithFrame:,如果它已经这样做的话。但如果你能保证不会发生,那你就没事。

【讨论】:

  • 谢谢!特别是在我已经接受另一个答案之后发布它。这太棒了!
  • 如果我尝试将这两个东西放入视图的头文件中,它将无法编译。它声称该属性的参数数量错误。我可以“修复”它并改用- (id)initWithFrame:(CGRect)frame __attribute__((deprecated)); 使其编译。然后它编译但没有任何警告,即使我在其他地方调用此方法以引发编译器警告。
  • 嗯。您使用的是最新版本的 Xcode 吗?我使用的是 Xcode 4.6.2,我提供的代码在我调用它时会编译并提供警告。
  • 对不起,不,我不是。 :)
【解决方案2】:

实际上,您可以使用子类进行限制。您可以在子类的.h 文件中覆盖要阻止的任何方法。您可以通过将以下内容放入您的 .h 文件中来使 initWithFrame 不可用。

- (id) initWithFrame:(CGRect) frame __attribute__((unavailable("message")));

这将使使用您的子类的任何人都无法使用initWithFrame: 方法。

要保持其他代码形式调用此方法,您可以通过将其放入您的 .m 文件中来进一步限制。

- (id) initWithFrame:(CGRect) frame
{
    return nil;     
}

【讨论】:

  • 这似乎无法编译。但是,如果我删除它的论点。将您的第二个建议添加到类中会产生编译器警告“方法属性只能在方法声明中指定”。但即便如此:我发现您的回答非常有用,并且支持您并没有错!
  • 也许你可以编辑你的答案并修改第一部分代码以便编译?
  • 不要在实现上添加属性或完全省略实现。
  • 我从这个答案复制并粘贴到 Xcode 中,它在我的机器上编译。也许你可以把你看到的错误告诉我?
  • 哦,对不起,我的第二条评论应该是“....以便编译没有错误”。 T_T
【解决方案3】:

没有。您不能阻止子类的用户调用超类的方法。您可以覆盖它们并在内部抛出异常,但这只会产生损坏的子类。

请记住,继承作为 "is a" 扩展工作,也就是说,您的子类的实例应该在任何不知道该特定子类但知道其超类的上下文中正常运行。只有在对您的子类有明确了解的地方,您才能从添加额外的初始化和其他方法中受益。

例如,UIKit 不知道您的子类。因此,如果你想让你的 UIView 子类在 NIB 中可用,你需要使用将由 NIB 加载系统调用的初始化方法,即initWithCoder:。您可以简单地在initWithCoder: 中调用您自己的初始化方法。但是,如果您想将任何其他参数传递给 init 方法,则必须提供一种在初始化后配置它们的方法。

【讨论】:

  • 对。我确实有我传递的参数,这就是为什么我不能从标准方法中调用我的自定义方法。呃,真是两难选择。
  • 这是一个大型开源项目中关于这个主题的讨论——AFNetworking 最近决定在init 中抛出异常:github.com/AFNetworking/AFNetworking/pull/…
猜你喜欢
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多