Author: Dai Pei
Address: http://blog.csdn.net/dp948080952/article/details/54882811
Reprinted please indicate the source
This article is not about method exchange. There are many such articles. If you don't know what method exchange is, you can read this article. Method Swizzling
trap
Method swapping is a dangerous thing, which can cause some unknown errors. Recently, when I used method swapping, I encountered such a problem. After I exchanged a system method, when I called this method, the loop could not jump out of the method I exchanged.
In the end, I found the key to the problem, which is that this method may not exist!
Every iOS version update brings up new API s that did not exist in previous versions, and if you swap a method without considering its existence, it can lead to serious errors.
For example, I once exchanged a method:
- (void)openURL:(NSURL*)url options:(NSDictionary<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion
This method only exists after iOS 10. Others will judge whether this method will be responded to when they use it. But we will see that the exchange code below will add this method first. If this method does not exist, then the original response will become a response. Then the system before iOS 10 will enter this method, resulting in a dead cycle.
Class class = [self class];
Method method1 = class_getInstanceMethod(class, sel1);
Method method2 = class_getInstanceMethod(class, sel2);
BOOL didAddMethod =
class_addMethod(class,
sel1,
method_getImplementation(method2),
method_getTypeEncoding(method2));
if (didAddMethod) {
class_replaceMethod(class,
sel2,
method_getImplementation(method1),
method_getTypeEncoding(method1));
} else {
method_exchangeImplementations(method1, method2);
}
Solution
It's also easy to solve this problem by making a judgment before exchanging:
if (![class instancesRespondToSelector:sel1] || ![class instancesRespondToSelector:sel2]) {
return ;
}
If you do not respond to this method, return directly.
encapsulation
In order not to repeat the code exchanged by methods and reduce errors, we can encapsulate it and place it in the Category of NSObject.
NSObject+DPExtension.h
#import <Foundation/Foundation.h>
@interface NSObject (DPExtension)
+ (void)intanceMethodExchangeWithOriginSelector:(SEL)sel1 swizzledSelector:(SEL)sel2;
@end
NSObject+DPExtension.m
#import "NSObject+DPExtension.h"
#import <objc/runtime.h>
@implementation NSObject (DPExtension)
+ (void)intanceMethodExchangeWithOriginSelector:(SEL)sel1 swizzledSelector:(SEL)sel2 {
Class class = [self class];
if (![class instancesRespondToSelector:sel1] || ![class instancesRespondToSelector:sel2]) {
return ;
}
Method method1 = class_getInstanceMethod(class, sel1);
Method method2 = class_getInstanceMethod(class, sel2);
BOOL didAddMethod =
class_addMethod(class,
sel1,
method_getImplementation(method2),
method_getTypeEncoding(method2));
if (didAddMethod) {
class_replaceMethod(class,
sel2,
method_getImplementation(method1),
method_getTypeEncoding(method1));
} else {
method_exchangeImplementations(method1, method2);
}
}
@end
That's all.^^