编译器找到[super msg]时会发生什么?

时间:2013-06-30 17:19:02

标签: objective-c super

我已经阅读过apple' messaging'用Objective-c编程的章节,得到了关于self和super的几个问题。 AFAIK当编译器发现任何消息时,它将其转换为带有两个隐藏参数的objc_msgSend - 接收器,选择器和选择器的变量参数。 例如[self test]将是这样的:

objc_msgSend(self, @selector(test));

如果接收者的调度表中没有方法实现,那么函数将尝试在超类中查找实现。 super只是编译器开始在当前对象的超类中搜索方法实现的标志,而在文档中,apple说当编译器发现超级'它将它翻译成类似的东西:

struct objc_super mySuperClass = {
    self,
    [self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardedMethod));

我创建了一个包含3个类的项目,每个类都继承自另一个类。

@interface FirstClass : NSObject
- (void)forwardMethod;
@end

@interface SecondClass : FirstClass
@end

@interface ThirdClass : SecondClass
@end

我在根视图控制器中创建了第三个类的实例,并调用了名为' forwardMethod'的方法。 实施:

//First Class
- (void)forwardMethod {
   NSLog(@"Base class reached");
}

//SecondClass imp
- (void)forwardMethod {
   NSLog(@"second class");
   [super forwardMethod];
}

//ThirdClass imp
- (void)forwardMethod {
   NSLog(@"third class");
   [super forwardMethod];
}

一切正常。但后来我决定解释编译器:

//First Class
- (void)forwardMethod {
   NSLog(@"Base class reached");
}

//SecondClass imp
- (void)forwardMethod {
   NSLog(@"second class");
   struct objc_super mySuperClass = {
      self,
      [self superclass]
   };

   objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

//ThirdClass imp
- (void)forwardMethod {
   NSLog(@"third class");
    struct objc_super mySuperClass = {
        self,
        [self superclass]
    };

    objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

这导致递归调用第二个类' forwardMethod'。我在' forwardMethod'中创建了一个结构。在第二课使用自我和[自我超类],但自我是第三类和 我的超级课程将永远是第二堂课#39;也许我做错了什么,但我怎么能进入基类'前进方法'?

2 个答案:

答案 0 :(得分:6)

注意:仅限教育用途(这很好!),请勿在生产代码中使用!

你也是最多的,只有一个课程......

要了解为何获得递归,请考虑如何使用编译时运行时中可用的信息找到超类。

在运行时,self的值是对当前对象的引用,您可以使用self来查找对象的类 - 在您的示例中self是类型的对象ThirdClass

现在self的值不会随着方法的调用而改变,因此在您的示例中,即使在FirstClass的{​​{1}} forwardMethod中,self的值也是参考到ThirdClass类型的对象。所以self使您能够找到对象的类型,但它并没有告诉您当前在其继承链中执行方法的位置,因此它本身无法告诉您下一个类的内容链是。

所以考虑编译时间。编译SecondClass时,编译器知道超级类为FirstClass,因此调用super是对FirstClass 中方法的调用(除了警告即将到来)下文)。因此编译器可以使用self[FirstClass class]来确定调用方法的运行时对象以及开始搜索方法的编译时类(如任何方法查找都是从类开始并沿着继承链继续进行的搜索,直到找到实现为止。因此,在您的示例代码中,您只是一种方法:

@implementation SecondClass

- (void)forwardMethod
{
   NSLog(@"second class");
   struct objc_super mySuperClass =
   {
      self,
      [FirstClass class]
   };

   objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

如果您使用它,您的代码将起作用。的但是...

......上面提到的那个警告。 Objective-C允许使用类swizzling 在运行时更改继承链,因此编译器通常不能依赖编译时继承链来计算超类。那它可以用什么呢?那么当编译特定方法的源时,它知道该方法所属的类,因此它可以将该方法类编译到代码中以查找超类,并使用代码开始在 next <上搜索方法< 运行时继承链中的/ em>类。这实际上是编译器将为您的代码执行的操作,以防您使用类调码:

@implementation SecondClass

- (void)forwardMethod
{
   NSLog(@"second class");
   struct objc_super mySuperClass =
   {
      self,
      [SecondClass class]
   };

   objc_msgSendSuper2(&mySuperClass, @selector(forwardMethod));
}

注意编译器传递编译时当前类([SecondClass class])并调用objc_msgSendSuper2来执行查找 - 这将在运行时继承链中找到之后的第一个方法 SecondCLass m而`objc_msgSendSuper会在SecondClass处开始搜索。

玩得开心,但不要在一般代码中使用它(除非你有非常非常非常好的理由; - )

答案 1 :(得分:4)

在SecondClass中,此结构将填充与ThirdClass完全相同的内容:

   struct objc_super mySuperClass = {
      self,
      [self superclass]
   };

self在两种情况下具有相同的值,因此,当从ThirdClass实例调用时,[self superclass]将始终为SecondClass(即使实际实现 - 代码 - 驻留在SecondClass中)。编译器发出的东西有点神奇(objc_msgSendSuper非常简单),因为它必须发出对类的引用,甚至像冒充和/或指针操作这样的东西 - 糟糕的程序员,没有甜甜圈 - 仍然按预期工作。我很长时间没有仔细研究细节,以确切知道它是如何工作的。

source to the runtimecompiler可用。