Objective-C消息传递是如何工作的?“super”在哪里?如何确保一个类在方法swizzling之前有一个来自其



我对Obj-C消息有一些问题。

(1) 这是我的主要问题:如何确保一个类在刷新或调用它之前有来自其超类的实现

如果我想用一个目标类没有实现它,但它的超类实现了它的方法来进行方法swizzling,那么在方法swizzing之前,如何确保这个类有超类的实现?

我知道,当一条消息发送到一个没有实现它的对象时,Obj-C运行时将首先查找其继承层次结构,以从其超类中找到实现。如果有,Obj-C将把它缓存在另一个调度表中并调用它

我可以用这个缓存的实现进行方法切换吗?如果可以的话,在方法swizzling或向该对象发送任何消息之前,如何确保有一个缓存的实现?如果没有,如何将实际实现添加到这个类中,它只会在方法swizzling之前调用super的实现?

例如,有这样一个类层次结构:

@interface A : NSObject
- (void) helloWorld;
@end
@interface B : A
@end
@interface C : B
@end
@interface D : C
@end
@interface E : D
@end

以及类似的实现:

@implemenation A
- (void) helloWorld{
    NSLog(@"hello world from class A");
}
@end
@implemenation B
// did not override -helloWorld
@end
@implemenation C
- (void) helloWorld{
    [super helloWorld];
    NSLog(@"hello world from class C");
}
@end
@implemenation D
// did not override -helloWorld
@end
@implemenation E
// did not override -helloWorld
@end

我只想在运行时与类D精确地交换(而不是添加)-helloWorld的实现。如果像这样的swizzled方法,这个技巧将在原始实现中添加一个额外的任务:

@implemenation D (AdditionalWorkForHelloWorld)
- (void) additionalWorkForHelloWorld{
    [self additionalWorkForHelloWorld];
    NSLog(@"hello every one!");
}
@end

因此,当我向类D或类E的实例发送-helloWorld消息时,控制台将打印以下消息:

-> hello world from class A
-> hello world from class C
-> hello every one!

某一天,在类D实现其-helloWorld之后,如下所示:

@implemenation D
- (void) helloWorld{
    [super helloWorld];
    NSLog(@"hello world from class D");
}
@end

并向类别D或类别E的实例发送-helloWorld消息,控制台将打印以下消息:

-> hello world from class A
-> hello world from class C
-> hello world from class D
-> hello every one!

这种方法交换将确保无论类D是否实现-helloWorld,都将调用附加任务。

我问了一个问题,关于如何制作一个C函数来调用对象的super的实现,这似乎并不容易。Obj-C是如何实现这种缓存机制来将消息转发到super的实现的?

(2) Obj-C如何处理super关键字?super在哪里

在Obj-C消息中,前两个参数是隐藏的:self_cmd。您可以在Obj-C实现中使用它们。但是super在哪里?

super等于selfisa指针吗?Obj-C如何处理super关键字?

每个Obj-C消息将转换为objc_msgSend()objc_msgSend_stret()。那么,发送到super的每条消息都会转换为objc_msgSendSuper()objc_msgSendSuper_stret()吗?

在前面提到的示例中,当我向类E发送-helloWorld消息,而类DE没有响应时,Obj-C将把消息发送到类C的实现。在类C的实现中,它调用其super的实现,但类B没有实现它,所以将它转发到类A

在这种情况下,类Csuper应该是类B,所以它实际上调用了类B的缓存实现,不是吗?我可以刷新类B的缓存实现吗?

一条到super的消息被发送到self。但是,对实现的查找从另一个地方开始。当您发送消息self时,Objective-C运行时(objc_msgSend()等)在selfisa指针指向的类中首先出现。如果在那里找不到实现,则搜索移到超类,依此类推

当您发送消息super时,搜索从代码包含super调用的类的超类开始。是的,发送给super的消息转换为对objc_msgSendSuper()或其变体之一的调用。编译器构造一个objc_super结构,该结构包含self和当前正在编译的类的超类。(注意:这是而不是[self superclass]相同。该表达式是动态的,取决于self的实际类。发送给super的消息所针对的超类不是动态的,不取决于self的实际类)

无论如何,objc_msgSendSuper()使用objc_super结构中引用的超类来控制它在哪里开始搜索实现,但在其他方面的行为与objc_msgSend()类似。就是这样。这就是super所做的全部:它开始沿着类链进一步搜索实现。

至于如何实现您的快速目标……我认为您应该首先尝试添加一个调用super的实现。如果失败(因为类已经有了这样的方法),那么就换一个调用原始方法的实现。

所以,类似于:

@implemenation D (AdditionalWorkForHelloWorld)
- (void) addedHelloWorldIfNotPresent{
    [super helloWorld];
    NSLog(@"hello every one!");
}
- (void) additionalWorkForHelloWorld{
    [self additionalWorkForHelloWorld];
    NSLog(@"hello every one!");
}
@end

然后尝试使用class_addMethod()添加-addedHelloWorldIfNotPresent的实现。使用+[NSObject instanceMethodForSelector:]@selector(addedHelloWorldIfNotPresent)获得实现。如果添加方法失败,则在-additionalWorkForHelloWorld中切换现有的-helloWorld

最新更新