【问题标题】:Incorrect scope范围不正确
【发布时间】:2014-05-19 11:31:09
【问题描述】:

我有两节课

啊.h

@interface A : UIView

@end

上午

@implementation A

-(id)initWithCoder:(NSCoder*)aDecoder{
  self = [super initWithCoder:aDecoder];
   if(self){ 
      [self initialize];
   }
return self;
}

-(void)initialize{
 [[NSNotificationCenter defaultCenter]addObserver:self name:keyboardnotificationwillshow];
}
@end

B.h

@interface B : A

@end

@implementation B

-(id)initWithCoder:(NSCoder*)aDecoder{
  self = [super initWithCoder:aDecoder];
   if(self){ 
      [self initialize];
   }
return self;
}

-(void)initialize{
 //do some stuff to prepare B
   [self setBackgroundColor:[UIColor redColor]];
}
@end

现在我将 B 放入界面构建器中并等待它通过故事板加载 但是!

现在我的问题来了:

在我用来编写代码的所有语言中,我上面编写的实现将导致以下结果

  1. 创建B,调用它的initWithCoder方法,看到它需要去super,去A的initWithCoder,看到它需要在A中调用initialize(注意initialize是一个发明的方法,它是私有的,它是没有在两个类的 h 文件中声明),执行用 A 编写的初始化
  2. 返回自己,返回 B 的 initWithCoder 看到它需要执行 B 的初始化,执行 B 的初始化,将 B 返回给任何想要使用编码器创建它的人

现在我已经通过调试运行了它,这是实际结果: 创建 B,initWithCoderB -> initWithCoderA ->initializeB ->return B

我的实际问题是:为什么它跳过了 initializeA ?

日志输出

2014-05-19 14:45:23.270 MyApp[17780:60b] B initWithCoder
2014-05-19 14:45:23.270 MyApp[17780:60b] A initWithCoder
2014-05-19 14:45:23.272 MyApp[17780:60b] B initialize
2014-05-19 14:45:23.278 MyApp[17780:60b] B initialize

如果我将 initializeA 重命名为 initStuffA 这是日志输出

2014-05-19 14:48:41.700 MyApp[17795:60b] B initWithCoder
2014-05-19 14:48:41.701 MyApp[17795:60b] A initWithCoder
2014-05-19 14:48:41.701 MyApp[17795:60b] A initialize
2014-05-19 14:48:41.702 MyApp[17795:60b] B initialize

那么为什么我不能有 2 个与 private 同名的方法,而没有它们相互覆盖呢? 因为它们是私人的,所以我不能对它们调用 super

【问题讨论】:

  • 与其说是两个毫无意义的、空洞的、本来可以解释的骨架类和一些本来可以作为示例代码更好的复杂废话,不如向我们展示产生意外的确切代码结果?
  • 你确定 initializeB 没有被调用两次而不是一次吗?
  • 但这是我的确切问题,“initializeA”被跳过了
  • 还向我们展示了您如何确定正在调用的方法。如果方法实际上是空的,那么它们被优化出来并非不可能。
  • 您不能在超类和子类中拥有两个同名的方法,而不会使它们相互覆盖(正如@giorashc 在他的回答中所解释的那样)。它们是否是私有的并不重要,因为查找是在运行时完成的。 - 如果你不想这样,那么你必须选择不同的名字。一种可能的替代解决方案是 static 方法,如Benefits when using static init in Objective C?

标签: ios objective-c


【解决方案1】:

由于您在类 B 中覆盖了初始化方法,并且在调用初始化方法时创建了类 B 的实例,因此它将指向继承层次结构中最深的实现(在您的情况下是类 B 的初始化方法)

【讨论】:

  • 这可能是 Apple 建议不要在 init 方法中使用属性访问器的部分原因。您可能应该完全避免在init 方法中调用self 上的任何内容。设置键盘的代码应该直接在init 方法中,而不是在辅助方法中。
  • 但是无论你如何初始化这个对象,我都希望这段代码发生,所以我想要一个新的方法来进行初始化,而不必复制粘贴 3 次?
  • B 类和 A 类的初始化行为是否不同?
  • 初始化A的目的是为了注册键盘通知,初始化B的目的是改变B的外观,initializeA应该运行因为我想使用继承
【解决方案2】:

你到底想在这里实现什么?

如果你想让接口B的实例通过接口A的initWithCoder,那么你不需要在接口B中覆盖它。只需将initialize设为公共方法并调用:

- (void)initialize { [super initialize]; //Any further initialization here }

目前,接口 B 不知道它的超类还实现了一个名为 initialize 的方法,因为它是一个私有方法。

如果你的接口A中的initWithCoder:方法调用你的initialize方法并且它是一个公共方法,那么当你创建接口B的实例时,它会经过接口A的initWithCoder:,然后通过到接口 B 的 initialize 然后应该调用 [super initialize]; 以确保执行接口 A 的 initialize 方法中的任何代码。

编辑 1:

好的,所以接口 B 继承自接口 A。

默认情况下(即,使用空白实现),接口 B 的实例将与接口 A 完全相同,因为所有方法都将默认通过它们在接口 A 中的实现。

作为接口 A 的子接口,接口 B 有机会覆盖接口 A 公开可用的任何方法或属性。

要使它们公开可用,请在 .h 文件中声明它们。

如果你有一个方法,例如接口A 的.h 文件中的updateView,那么接口B 的实现就有机会覆盖它。如果您希望接口 B 在其自身和接口 A 中执行操作,则需要使用 super 关键字调用继承树。例如,在接口 B(子接口)的实现中具有如下内容:

- (void)updateView { [super updateView]; //This calls any code written in interface A's updateView method //Add your own code here }

这表明您的子类在获得对接口 A 创建的updateView 方法的访问权限时执行继承。

接口 B,因为它是接口 A 的子接口,将使用其父类的实现填充任何空方法。在您的示例中,您在两个接口中都实现了initWithCoder:。这是不必要的,并且会导致覆盖复杂化。通过在接口 B 中省略 initWithCoder: 的声明,它将使用接口 A 的实现,它的父类调用 initialize。您只需记住每次覆盖方法实现并希望执行您自己的代码和父类的代码时都调用 super。

希望这能消除您对 Objective-C 中继承的一些困惑。

【讨论】:

  • 我想用继承的概念
  • 查看我的编辑以了解更多关于如何使用继承的信息
  • initialize 在任何接口中都不是公开可用的,这就是为什么我认为它会分别执行每个接口,因为理论上一个人不应该知道另一个接口的存在
  • 为什么需要让它们分开运行?您总是可以不从孩子的initialize 方法中调用超级实现。这意味着该方法仍会在正确的时间被调用,但不会执行父级的实现。但是,如果您希望发生不同的事情,老实说,我认为您最好使用不同的方法,例如在不同的接口中使用 initializeXinitializeY,以使实现之间的差异更加明显。
  • 如果您在父类中定义一个方法,然后在子类中调用它而不调用 super,则永远不应该执行父类的实现,因为编译器会将其视为不存在的覆盖不想打电话给超级。
猜你喜欢
  • 2021-08-18
  • 1970-01-01
  • 2014-04-20
  • 1970-01-01
  • 2022-10-01
  • 1970-01-01
  • 2018-12-21
  • 2015-08-23
  • 1970-01-01
相关资源
最近更新 更多