我对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
等于self
的isa
指针吗?Obj-C如何处理super
关键字?
每个Obj-C消息将转换为objc_msgSend()
或objc_msgSend_stret()
。那么,发送到super
的每条消息都会转换为objc_msgSendSuper()
和objc_msgSendSuper_stret()
吗?
在前面提到的示例中,当我向类E
发送-helloWorld
消息,而类D
和E
没有响应时,Obj-C将把消息发送到类C
的实现。在类C
的实现中,它调用其super
的实现,但类B
没有实现它,所以将它转发到类A
。
在这种情况下,类C
的super
应该是类B
,所以它实际上调用了类B
的缓存实现,不是吗?我可以刷新类B
的缓存实现吗?
一条到super
的消息被发送到self
。但是,对实现的查找从另一个地方开始。当您发送消息self
时,Objective-C运行时(objc_msgSend()
等)在self
的isa
指针指向的类中首先出现。如果在那里找不到实现,则搜索移到超类,依此类推
当您发送消息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
。