2

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?

Community
  • 1
  • 1
Xaree Lee
  • 3,188
  • 3
  • 34
  • 55
  • 1) "Messaging" is (a rather complex form of) simply "calling" in any other language. 2) If your class is a subclass of any other class it has a "super". Using "super" in an invocation simply clues the compiler to look for the superclass version of the named method. You should know what the class is you subclassed and what methods it supports, hence what methods you can use "super" to invoke. – Hot Licks Dec 02 '13 at 16:38
  • If this isn't making much sense you probably need to study up more on classes and class inheritance. – Hot Licks Dec 02 '13 at 16:40
  • As close as I can understand your description of how `additionalWorkForHelloWorld` should work, that's not readily possible in Objective-C (though Objective-C has many features that allow "impossible" things to be done with some difficulty). (And, BTW, your implementation of that method would suffer from infinite recursion.) – Hot Licks Dec 02 '13 at 16:54
  • (Well, actually it is possible. Just implement `helloWorld` in D, have it invoke the `super` version, *then* have it print your added message.) – Hot Licks Dec 02 '13 at 16:56
  • Sometimes, the Class `D` is not really implemented by you. If I want to add an extra code to the target method, and I don't know this implementation would be implemented by this class or its superclass, I need to check at runtime. If it is not implemented by the class itself, I should add an implementation before exchanging them. – Xaree Lee Dec 02 '13 at 17:26
  • In terms of calling a "super" method it makes no difference whether the method is implemented in the immediately "super" class or its "super" or its "super" -- the check is done recursively until either the "bottom" is reached or a matching method is found. In terms of "intercepting" a call to a method not in your class, there are ways to do this but they are graduate level interfaces. – Hot Licks Dec 02 '13 at 18:15

1 Answers1

3

A message to super is sent to self. However, the lookup of the implementation starts at a different place. When you message self, the Objective-C run time (objc_msgSend(), etc.) looks first in the class pointed to by self's isa pointer. 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 of super. Yes, the message to super translates into a call to objc_msgSendSuper() or one of its variants. The compiler constructs an objc_super structure that contains self and 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 of self. The superclass targeted by a message to super is not dynamic and doesn't depend on the actual class of self.)

Anyway, objc_msgSendSuper() uses the superclass referenced in the objc_super structure to control where it starts the search for the implementation, but otherwise behaves like objc_msgSend(). That's it. That's all that super does: 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:

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

Then try adding the implementation of -addedHelloWorldIfNotPresent using class_addMethod(). Obtain the implementation using +[NSObject instanceMethodForSelector:] with @selector(addedHelloWorldIfNotPresent). If adding the method fails, then swizzle in -additionalWorkForHelloWorld for the existing -helloWorld, instead.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154