I'm running into some odd behavior when trying to Method Swizzle isEqualToString:
on the NSString
class. Here is the code in question:
#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h>
@interface NSString (SwizzleString)
- (BOOL) custom_isEqualToString:(NSString *)aString;
- (NSRange)custom_rangeOfString:(NSString *)aString;
@end
@implementation NSString (SwizzleString)
- (BOOL) custom_isEqualToString:(NSString *)aString;
{
NSLog(@"Inside custom_isEqualToString method definition");
return [self custom_isEqualToString:aString];
}
- (NSRange)custom_rangeOfString:(NSString *)aString;
{
NSLog(@"Inside custom_rangeOfString method definition");
return [self custom_rangeOfString:aString];
}
@end
int main(int argc, const char * argv[])
{
Method m1, m2;
m1 = class_getInstanceMethod([NSString class], @selector(isEqualToString:));
m2 = class_getInstanceMethod([NSString class], @selector(custom_isEqualToString:));
method_exchangeImplementations(m1, m2);
m1 = class_getInstanceMethod([NSString class], @selector(rangeOfString:));
m2 = class_getInstanceMethod([NSString class], @selector(custom_rangeOfString:));
method_exchangeImplementations(m1, m2);
NSString *foo = @"Foo";
// Does not log anything, is still using isEqualToString: implementation
[foo isEqualToString:@"Foo"];
// Also does not log anything, since it is using the method implementation from isEqualToString:
[foo custom_isEqualToString:@"Foo"];
// Does log something because rangeOfString now uses custom_rangeOfString IMP
[foo rangeOfString:@"Foo"];
// Does not log anything because it uses the method implementation from rangeOfString:
[foo custom_rangeOfString:@"Foo"];
}
isEqualToString:
and rangeOfString:
are both defined in a category on NSString
called (NSStringExtensionMethods)
, so I included the rangeOfString
swizzle to show that I'm swizzling methods correctly and specifically an NSString
object successfully so I could eliminate questions about class cluster problems.
When I produce the assembly for the code above, instead of seeing normal objc_msgSend
calls I instead see stuff like l_objc_msgSend_fixup_isEqualToString_
. This led me to finding out more about the objective-c vtable , in which it seems isEqualToString:
can be found:
static const char * const defaultVtable[] = {
"allocWithZone:",
"alloc",
"class",
"self",
"isKindOfClass:",
"respondsToSelector:",
"isFlipped",
"length",
"objectForKey:",
"count",
"objectAtIndex:",
"isEqualToString:",
"isEqual:",
"retain",
"release",
"autorelease",
};
I've been digging through the objective-c source and the internet all day on how it would be possible to somehow still be able to swizzle isEqualToString:
.
As you're aware, this is a class cluster. You need the actual class, not the public class, so just ask the string object that you have:
NSString *foo = @"Foo";
m1 = class_getInstanceMethod([foo class], @selector(isEqualToString:));
m2 = class_getInstanceMethod([foo class], @selector(custom_isEqualToString:));
method_exchangeImplementations(m1, m2);
There's also the fragile alternative of using the class name itself:
NSClassFromString(@"__NSCFConstantString")
NSClassFromString(@"__NSCFString")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.