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

Yur*_*nko 6 objective-c super

我已经从使用objective-c的编程中读到了苹果的"消息传递"章节,并得到了关于self和super的几个问题.AFAIK当编译器发现任何消息时,它将其转换为带有两个隐藏参数的objc_msgSend - 接收器,选择器和选择器的变量参数.例如[self test]将是这样的:

objc_msgSend(self, @selector(test));
Run Code Online (Sandbox Code Playgroud)

如果接收者的调度表中没有方法实现,那么函数将尝试在超类中查找实现.super只是编译器开始在当前对象的超类中搜索方法实现的标志,并且在文档中,apple说当编译器找到'super'时,它会将其转换为类似的东西:

struct objc_super mySuperClass = {
    self,
    [self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardedMethod));
Run Code Online (Sandbox Code Playgroud)

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

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

@interface SecondClass : FirstClass
@end

@interface ThirdClass : SecondClass
@end
Run Code Online (Sandbox Code Playgroud)

我在根视图控制器中创建了第三个类的实例,并调用了他的方法"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];
}
Run Code Online (Sandbox Code Playgroud)

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

//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));
}
Run Code Online (Sandbox Code Playgroud)

这会导致对第二个类'forwardMethod'的递归调用.我使用self和[self superclass]在第二个类的'forwardMethod'中创建了一个struct,但self是thirdclass,而
我的超类将始终是'second class'.也许我做错了什么,但我怎么能进入基类'前进方法'?

CRD*_*CRD 6

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

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

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

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

现在的价值self不会改变,因为方法的调用,所以在你的榜样,即使在FirstClassforwardMethod价值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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

@implementation SecondClass

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

   objc_msgSendSuper2(&mySuperClass, @selector(forwardMethod));
}
Run Code Online (Sandbox Code Playgroud)

请注意,编译器传递编译时当前的class([SecondClass class])并调用objc_msgSendSuper2以执行查找 - 这将在运行时继承链中找到m 之后 的第一个方法,SecondCLass`objc_msgSendSuperSecondClass自己开始搜索.

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