Warning: Before MSHookMessageEx was added to Substrate, MSHookMessage (now deprecated) provided similar functionality. Developers should not use this API, and it will not be available on platforms supporting Objective-C other than iOS (where it remains for backwards compatibility).
Rather than providing the original implementation through a pointer old
argument, it was simply returned: this causes a race condition with usage of the class (as if another thread sent this message after this API has hooked the method but before it has returned, you will not yet have a way to call through).
However, even this functionality had been retrofitted later into the original API, which actually used to return void
. Instead, you would pass a fourth argument that would be used to rename the existing method such that it could be called using the underlying Objective-C message send mechanism.
As an example, if you were hooking the method "description", you might rename the existing method to "wb$description", replacing it with your own implementation. Your function could then call the original implementation of this API by sending the "wb$description" message to the object.
This functionality, however, while quite conceptually similar to standard Objective-C "method swizzling", causes complexities when operating in environments where there are more developers making modifications to the same messages at different points in the class hierarchy.
IMP MSHookMessage(Class _class, SEL message, IMP hook, const char *prefix);
Parameter | Description |
---|---|
_class |
Objective-C class on which a message will be instrumented. This class can be a meta-class (obtained directly using objc_getMetaClass or by calling object_getClass on a class), so as to allow hooking non-instance or "class" messages. |
message |
Objective-C selector of message that will be instrumented. This might be a literal using @selector or generated at runtime with sel_registerName. |
hook |
The address of an ABI-compatible replacement for the implementation of the message being instrumented. |
prefix |
A string to prepend to the original selector name, in order to allow it to be called by the new implementation. |
return |
A function pointer to a stub which may be used to call the original implementation. |
NSString *(*oldDescription)(id self, SEL _cmd); NSString *newDescription(id self, SEL _cmd) { NSString *description = (*oldDescription)(self, _cmd); description = [description stringByAppendingString:@"!"]; return description; } oldDescription = MSHookMessage( [NSObject class], @selector(description), &newDescription, NULL // no prefix );
NSString *newDescription(id self, SEL _cmd) { NSString *description = [self example$description]; description = [description stringByAppendingString:@"!"]; return description; } MSHookMessage( [NSObject class], @selector(description), &newDescription, "example$" );