我有一个父类
@interface Parent : NSObject
@end
Run Code Online (Sandbox Code Playgroud)
还有一个儿童班
@interface Child : Parent
@end
Run Code Online (Sandbox Code Playgroud)
一般来说,我们可以像这样在父指针中存储子对象
Parent *p = [Child alloc]init];
Run Code Online (Sandbox Code Playgroud)
我很惊讶地知道当我像这样将父对象存储在子指针中时
Child *c = [[Parent alloc] init];
Run Code Online (Sandbox Code Playgroud)
尽管编译器给出了警告“语义错误不兼容的指针”,但是当我运行它时,它就像第一种情况一样。我无法理解为什么运行时允许它工作?
你没有说为什么你对这种行为感到惊讶,但一种可能性是静态和动态方法查找和输入之间的区别。
首先要注意的是,无论你在哪里有一个引用,你实际上都可能有一个继承- 后者可以做前者可以做的一切,并且可以代替一个。ParentChild
现在在许多面向对象的语言中,例如 C++,变量类型和方法查找是基于静态(即声明的)类型。因此,例如在 C++ 中,您的变量c被假定为一个实例,Child并且当c在 C++ 编译器上调用方法时,仅根据 this 确定要调用的方法。因此分配一个 的实例Parent是不安全的——这样的一个实例没有 的方法,Child肯定会导致灾难。您可以使用强制转换在 C++ 中使赋值安全:
Parent *p;
Child *c;
...
c = (Child *)p;
Run Code Online (Sandbox Code Playgroud)
强制转换将在运行时检查所引用的对象是否p是 a Child(或任何继承自Child等的类),如果不是,则产生错误。由于这是在运行时完成的,因此它通常包含在首先检查是否p为 的条件中Child。
然而,在OBJ - C的方法查找完成动态基于实际类型引用的对象,而不是在可变其中引用它的类型在运行时。因此,如果您的c变量包含对 a 的引用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
...
}
Run Code Online (Sandbox Code Playgroud)
上面故意不讨论dynamic与static的相对优缺点,你会从一本好书中读到更好。
| 归档时间: |
|
| 查看次数: |
2034 次 |
| 最近记录: |