美文网首页
Runtime的应用

Runtime的应用

作者: iOS程序媛ff | 来源:发表于2019-10-24 15:25 被阅读0次

    一、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

    相关文章

      网友评论

          本文标题:Runtime的应用

          本文链接:https://www.haomeiwen.com/subject/fhqygqtx.html