I have some question about Obj-C message.
(1) It's my main question: how to make sure a class has an implementation from its superclass before swizzling it or invoking it?
If I want to do method swizzling with a method which the target class didn't implement it, but its superclass did, how to make sure this class has the implementation from its superclass before method swizzling?
I know when a message sent to an object which did not implement it, the Obj-C runtime will first look up its inherit hierarchy to find the implementation from its superclass. If there is one, Obj-C will cache it in a different dispatch table and invoke it.
Can I do method swizzling with this cached implementation? If can, how to make sure there is an cached implementation before method swizzling or sending any message to this object? If not, how to add a real implementation to this class which will just invoke the super's implementation before method swizzling?
For example, there is a class hierarchy like this:
@interface A : NSObject
- (void) helloWorld;
@end
@interface B : A
@end
@interface C : B
@end
@interface D : C
@end
@interface E : D
@end
and the implementation like this:
@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
I want to just exactly exchange (not adding) the implementation of -helloWorld with the Class D at runtime. This trick will add an additional task to the original implementation if the swizzled method like this:
@implemenation D (AdditionalWorkForHelloWorld)
- (void) additionalWorkForHelloWorld{
[self additionalWorkForHelloWorld];
NSLog(@"hello every one!");
}
@end
So when I send a -helloWorld message to the instance of Class D or Class E, the console will print the following messages:
-> hello world from class A
-> hello world from class C
-> hello every one!
Someday, after the class D implements its -helloWorld as following:
@implemenation D
- (void) helloWorld{
[super helloWorld];
NSLog(@"hello world from class D");
}
@end
and send a -helloWorld message to the instance of Class D or Class E, the console will print the following messages:
-> hello world from class A
-> hello world from class C
-> hello world from class D
-> hello every one!
This method exchanging will make sure the additional task will be invoked whether class D implements -helloWorld or not.
I'v asked a question about how to make a C function to invoke an object's super's implementation, and it seems not easy to do that. How does Obj-C achieve this cached mechanism to forward the message to super's implementation?
(2) How dose Obj-C handle super keyword? and where is super?
In the Obj-C message, the first two arguments are hidden: self and _cmd. You can use them in an Obj-C implementation. But where is super?
Is a super equal to the self's isa pointer? How dose Obj-C handle super keyword?
Every Obj-C message will convert to objc_msgSend() or objc_msgSend_stret(). So does every message sent to super will convert to objc_msgSendSuper() and objc_msgSendSuper_stret()?
In the example mentioned previously, when I send a -helloWorld message to the Class E, and Class D and E don't respond it, Obj-C will send the message to Class C's implementation. In the Class C's implementation, it invokes its super's implementation, but Class B did not implement it, so forward it to Class A.
In this case, the super of Class C should be Class B, so it actually invokes Class B's cached implementation, doesn't it? May I swizzle the Class B's cached implementation?
A message to
superis sent toself. However, the lookup of the implementation starts at a different place. When you messageself, the Objective-C run time (objc_msgSend(), etc.) looks first in the class pointed to byself'sisapointer. If no implementation is found there, the search moves to the superclass, and so on.When you message
super, the search starts in the superclass of the class whose code contained the invocation ofsuper. Yes, the message tosupertranslates into a call toobjc_msgSendSuper()or one of its variants. The compiler constructs anobjc_superstructure that containsselfand the superclass of the class currently being compiled. (Note: that's not the same as[self superclass]. That expression is dynamic and depends on the actual class ofself. The superclass targeted by a message tosuperis not dynamic and doesn't depend on the actual class ofself.)Anyway,
objc_msgSendSuper()uses the superclass referenced in theobjc_superstructure to control where it starts the search for the implementation, but otherwise behaves likeobjc_msgSend(). That's it. That's all thatsuperdoes: it starts the search for the implementation further along the chain of classes.As to how to accomplish your swizzling goal… I think you want to first try adding an implementation that calls
super. If that fails (because the class already has such a method), then swizzle in a different implementation that calls the original.So, something like:
Then try adding the implementation of
-addedHelloWorldIfNotPresentusingclass_addMethod(). Obtain the implementation using+[NSObject instanceMethodForSelector:]with@selector(addedHelloWorldIfNotPresent). If adding the method fails, then swizzle in-additionalWorkForHelloWorldfor the existing-helloWorld, instead.