通过#import <objc/runtime.h>
1.我们可以找到class_getInstanceMethod方法(获取类的实例方法)
/**
* Returns a specified instance method for a given class.
*
* @param cls The class you want to inspect.
* @param name The selector of the method you want to retrieve.
*
* @return The method that corresponds to the implementation of the selector specified by
* \e name for the class specified by \e cls, or \c NULL if the specified class or its
* superclasses do not contain an instance method with the specified selector.
*
* @note This function searches superclasses for implementations, whereas \c class_copyMethodList does not.
*/
OBJC_EXPORT Method _Nullable
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
2.用method_exchangeImplementations来交换两个方法实现
/**
* Exchanges the implementations of two methods.
*
* @param m1 Method to exchange with second method.
* @param m2 Method to exchange with first method.
*
* @note This is an atomic version of the following:
* \code
* IMP imp1 = method_getImplementation(m1);
* IMP imp2 = method_getImplementation(m2);
* method_setImplementation(m1, imp2);
* method_setImplementation(m2, imp1);
* \endcode
*/
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
应用实例
1.hook所有按钮的点击事件
#import "UIControl+Extension.h"
#import <objc/runtime.h>
@implementation UIControl (Extension)
//只执行一次
+ (void)load
{
//hook
Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method method2 = class_getInstanceMethod(self, @selector(new_sendAction:to:forEvent:));
method_exchangeImplementations(method1, method2);
}
- (void)new_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
NSLog(@"%@ - %@ - %@", self, target, NSStringFromSelector(action));
//在这里做些什么
//..
// if ([self isKindOfClass:[UIButton class]]) {
// // 拦截了所有按钮的事件
//
// }
//调用系统原来的
[self new_sendAction:action to:target forEvent:event];
}
@end
实际使用:
UIButton *btn = [[UIButton alloc] initWithFrame:(CGRectMake(50, 100, 100, 50))];
btn.backgroundColor = [UIColor grayColor];
[btn addTarget:self action:@selector(btAction) forControlEvents:(UIControlEventTouchUpInside)];
[self.view addSubview:btn];
- (void)btAction {
NSLog(@"点击事件");
}
输出: <UIButton: 0x7fececb09f90; frame = (50 100; 100 50); opaque = NO; layer = <CALayer: 0x600002bfd280>> - <ViewController: 0x7fecec9079a0> - btAction
2.防止可变数组插入nil时,导致的crash
实例:
NSString *obj = nil;
NSMutableArray *array = [NSMutableArray array];
[array addObject:@"jack"];
[array insertObject:obj atIndex:0];
报错:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
terminating with uncaught exception of type NSException
解决方法:
#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Extension)
+ (void)load
{
//保证方法只被替换一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"__NSArrayM");
Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method method2 = class_getInstanceMethod(cls, @selector(new_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
});
}
- (void)new_insertObject:(id)anObject atIndex:(NSUInteger)index
{
if (anObject == nil) {
return;
}
[self new_insertObject:anObject atIndex:index];
}
@end
3.防止可变字典插入的key或value为nil时,导致的crash
实例:
NSString *obj = nil;
NSMutableDictionary *mDict = [NSMutableDictionary dictionary];
mDict[@"name"] = @"jack";
mDict[obj] = @"lilei";
mDict[@"age"] = obj;
NSLog(@"%@",mDict);
报错:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKeyedSubscript:]: key cannot be nil'
解决方法:
#import "NSMutableDictionary+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableDictionary (Extension)
+ (void)load
{
//保证方法只被替换一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"__NSDictionaryM");
Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:));
Method method2 = class_getInstanceMethod(cls, @selector(new_setObject:forKeyedSubscript:));
method_exchangeImplementations(method1, method2);
Class cls2 = NSClassFromString(@"__NSDictionaryI");
Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:));
Method method4 = class_getInstanceMethod(cls2, @selector(new_objectForKeyedSubscript:));
method_exchangeImplementations(method3, method4);
});
}
- (void)new_setObject:(id)anObject forKeyedSubscript:(nonnull id<NSCopying>)key
{
if (!key) {
return;
}
[self new_setObject:anObject forKeyedSubscript:key];
}
- (id)new_objectForKeyedSubscript:(id)key
{
if (!key) {
return nil;
}
return [self new_objectForKeyedSubscript:key];
}
@end