itenyh
itenyh

Reputation: 1939

Can not find selector when exchange method

I come across a problem when trying to exchange method:

@implementation LoginViewModel

+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method fromMethod = class_getClassMethod([NSURL class], @selector(URLWithString:));
        Method toMethod = class_getClassMethod([self class], @selector(TempURLWithString:));
        method_exchangeImplementations(fromMethod, toMethod);
    }
});

}

+ (NSURL *)TempURLWithString:(NSString *)URLString {
    NSLog(@"url: %@", URLString);
    return [LoginViewModel TempURLWithString:URLString];
}

When calling [NSURL URLWithString:], I successfully get the parameter in the exchanged method TempURLWithString:. But it crashed when returning the result from the original implementation:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[LoginViewModel 
URLWithString:relativeToURL:]: unrecognized selector sent to class 
0x10625ff80'

What I want to do is modifying the url String when init NSURL, any one can give me some help, thanks!

Upvotes: 1

Views: 84

Answers (1)

lukas
lukas

Reputation: 2410

The implementation of +[NSURL URLWithString:] is basically the following:

+ (NSURL *)URLWithString:(NSString *)string {
    return [self URLWithString:string relativeToURL:nil];
}

The important thing to note here is that the self refers to the NSURL class.

However, when you call [LoginViewModel TempURLWithString:URLString], the self in the original URLWithString: method is now a reference to the LoginViewModel class, meaning that when the original implementation calls [self URLWithString:string relativeToURL:nil], that call gets dispatched to +[LoginViewModel URLWithString:relativeToURL:], which doesn't exist (hence the exception).

You can fix this by also adding a stub for URLWithString:relativeToURL to your class which just forwards the call to +[NSURL URLWithString:relativeToURL:]:

@implementation LoginViewModel
+ (NSURL *)URLWithString:(NSString *)string relativeToURL:(nullable NSURL *)relativeURL {
    return [NSURL URLWithString:string relativeToURL:relativeURL];
}
@end

Upvotes: 2

Related Questions