我已经阅读过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;也许我做错了什么,但我怎么能进入基类'前进方法'?
答案 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非常简单),因为它必须发出对类的引用,甚至像冒充和/或指针操作这样的东西 - 糟糕的程序员,没有甜甜圈 - 仍然按预期工作。我很长时间没有仔细研究细节,以确切知道它是如何工作的。