Objective-C 父子指针及其关系

the*_*guy 3 objective-c

我有一个父类

@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)

尽管编译器给出了警告“语义错误不兼容的指针”,但是当我运行它时,它就像第一种情况一样。我无法理解为什么运行时允许它工作?

CRD*_*CRD 5

你没有说为什么你对这种行为感到惊讶,但一种可能性是静态动态方法查找和输入之间的区别。

首先要注意的是,无论你在哪里有一个引用,你实际上都可能有一个继承- 后者可以做前者可以做的一切,并且可以代替一个。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)

上面故意不讨论dynamicstatic的相对优缺点,你会从一本好书中读到更好。