一、Runtime交换方法 method_exchangeImplementations
第三方框架或者系统原生方法功能 不能满足我们的时候,我们可以在保持系统原有方法功能的基础上,添加额外的功能
1.1、无侵入式埋点(运行时方法替换,hook系统方法)
#import "UIViewController+BigDataExtend.h"
#import <objc/runtime.h>
@implementation UIViewController (BigDataExtend)
+ (void)load {
Method method1 = class_getInstanceMethod(self, @selector(viewWillDisappear:));
Method method2 = class_getInstanceMethod(self, @selector(bigdata_viewWillDisappear:));
method_exchangeImplementations(method1, method2);
}
- (void)bigdata_viewWillDisappear:(BOOL)animated {
// 插入相关代码,
// 再执行原方法
[self bigdata_viewWillDisappear:animated];
}
@end
1.2、数组插入空对象崩溃,进行错误判断(感兴趣的同学可以去弄一下字典的判断)
#import "NSMutableArray+Extend.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Extend)
+ (void)load {
// 类簇:NSStirng、NSArray、NSDictionary,真实类型是其他类型
Class cls = NSClassFromString(@"__NSArrayM");
Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method method2 = class_getInstanceMethod(cls, @selector(wj_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
}
- (void)wj_insertObject:(id)anObject atIndex:(NSUInteger)index {
if (anObject == nil) return;
// 只有对象存在才会去执行系统方法
[self wj_insertObject:anObject atIndex:index];
}
@end
1.3、手机字体适配
#import "UIFont+Extend.h"
#import <objc/runtime.h>
@implementation UIFont (Extend)
+ (void)load {
Method method1 = class_getInstanceMethod(self, @selector(systemFontOfSize:));
Method method2 = class_getInstanceMethod(self, @selector(wj_systemFontOfSize:));
method_exchangeImplementations(method1, method2);
}
+ (UIFont *)wj_systemFontOfSize:(CGFloat)fontSize {
// 这里可根据手机屏幕尺寸制定系数
const CGFloat modulus = 1.5;
[self wj_systemFontOfSize:fontSize * modulus];
}
@end
1.4、拦截所有button点击事件
#import "UIControl+Extend.h"
#import <objc/runtime.h>
@implementation UIControl (Extend)
+ (void)load {
Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method method2 = class_getInstanceMethod(self, @selector(wj_sendAction:to:forEvent:));
method_exchangeImplementations(method1, method2);
}
- (void)wj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
// if ([self isKindOfClass:[UIButton class]]) {
// // 拦截了所有按钮的事件
// }
// 重新实现系统自带方法
[self wj_sendAction:action to:target forEvent:event];
}
@end
二、给系统分类动态添加属性
给系统的类添加额外属性的时候,可以使用runtime动态添加属性方法
给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存添加到内存空间
- (void)dynamicAddObject {
// 动态创建一个Person类
NSString *ocObjectName = @"Person";
const char *cObjectName = [ocObjectName cStringUsingEncoding:NSUTF8StringEncoding];
Class newClass = objc_allocateClassPair([NSObject class], cObjectName, 0);
// 动态添加成员变量
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_weight", 4, 1, @encode(int));
class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
/*
* class_addIvar 只能在objc_registerClassPair之前调用
* 不支持将实例变量添加到已有的类
*/
objc_registerClassPair(newClass);
id person = [[newClass alloc] init];
[person setValue:@10 forKey:@"_age"];
[person setValue:@20 forKey:@"_weight"];
[person run];
NSLog(@"%@ -- %@", [person valueForKey:@"_age"], [person valueForKey:@"_weight"]);
// 在不需要这个类时释放
objc_disposeClassPair(newClass);
}
void run(id self, SEL _cmd){
NSLog(@"动态方法调用");
}
三、字典转模型
字典转模型 KVC 实现
KVC 字典转模型弊端:必须保证,模型中的属性和字典中的key 一一对应。
如果不一致,就会调用[ setValue:forUndefinedKey:] 报key找不到的错。
分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。
解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖,就能继续使用KVC,字典转模型了。
字典转模型 Runtime 实现
思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值(从提醒:字典中取值,不一定要全部取出来);提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类实现字典转模型。
考虑情况:
1.当字典的key和模型的属性匹配不上。
2.模型中嵌套模型(模型属性是另外一个模型对象)。
3.数组中装着模型(模型的属性是一个数组,数组中是一个个模型对象)。
注解:
根据上面的三种特殊情况,先是字典的key和模型的属性不对应的情况。不对应有两种,一种是字典的键值大于模型属性数量,这时候我们不需要任何处理,因为runtime是先遍历模型所有属性,再去字典中根据属性名找对应值进行赋值,多余的键值对也当然不会去看了;另外一种是模型属性数量大于字典的键值对,这时候由于属性没有对应值会被赋值为nil,就会导致crash,我们只需加一个判断即可。考虑三种情况下面一一注解
MJExtension 字典转模型实现
底层也是对 runtime 的封装,才可以把一个模型中所有属性遍历出来。(我之所以看不懂,是MJ封装了很多层而已_.)。
四、利用关联对象(AssociatedObject)给分类添加属性
为一个已有的类(比如说不想影响其文件结构)、第三方库提供的类增加几个property,已经是十分常见且需要的操作了,有人会单独起草一份category.m文件,也有人直接继承。
category的好处就是:
1、能减少类文件的数量提高编译速度;
2、也是为了代码结构更加清晰。作者:强子ly
例:SDWebImage 使用url作为key进行关联
static char imageURLKey;
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
摘自
不知名刘先生 https://mp.weixin.qq.com/s?__biz=MzUxMjAzMTI4Mw==&mid=2247483832&idx=1&sn=e9eb1ca026bc322e8e772467fdcb2749&chksm=f96beee4ce1c67f294ceb32b2de46df705977c52cc14a95d1a88da8f611c5c8ba96a568a378a&token=911032679&lang=zh_CN#rd、
强子ly https://www.jianshu.com/p/0326e2f13ce6
网友评论