你没有说为什么你对这种行为感到惊讶,但一种可能是 static 和 dynamic 方法查找和键入之间的区别。
首先请注意,使用 inheritance,无论您有一个 Parent 引用,您实际上都可能有一个 Child 引用 - 后者可以做前者可以做的所有事情,并且可以替代一个。 p>
现在在许多面向对象的语言中,例如 C++,变量类型和方法查找都是基于静态(即声明的)类型。因此,例如在 C++ 中,您的变量 c 被假定为 Child 的一个实例,并且当在 c 上调用方法时,C++ 编译器仅根据这一点确定要调用的方法。因此分配Parent 的实例是不安全 - 这样的实例没有Child 的方法,肯定会导致灾难。您可以使用强制转换使 C++ 中的赋值安全:
Parent *p;
Child *c;
...
c = (Child *)p;
强制转换将在运行时检查p 引用的对象是否为Child(或任何继承自Child 等的类),如果不是则产生错误。由于这是在运行时完成的,因此它通常包含在一个条件中,该条件首先检查 p 是否为 Child。
但是,在 Obj-C 中,方法查找是在运行时动态根据被引用对象的实际类型而不是引用它的变量的类型来完成的。因此,如果您的 c 变量包含对 Parent 的引用并且您尝试调用 Child 方法,您将收到运行时错误(这将是一个干净的错误,在 C++ 情况下,您的代码将可能只是故障和/或爆炸)。
Obj-C 的这种动态特性很容易漏掉很多编程错误,这些错误只有在代码运行时才会显现出来——而且由于很难在代码交付后完全测试应用程序顾客。因此,Obj-C 编译器会尽可能多地进行类型检查,以帮助最大限度地减少运行时出现的错误数量。但是,再次由于动态特性,它有时只会警告而不是报告错误并拒绝编译 - 就像在您的示例中那样。
Obj-C 中的解决方案与上面的 C++ 相同 - 使用强制转换来声明对象必须是某种类型并用条件保护它:
Parent *p;
Child *c;
...
if([p isKindOfClass:[Child class]) // we need a Child
{
c = (Child *)p;
...
}
else
{ // handle p not being a Child
...
}
以上内容有意不讨论 dynamic 与 static 的相对优缺点,您最好从一本好书阅读。