Runtime

作者: 那抹浮沉 | 来源:发表于2020-08-04 18:13 被阅读0次
    • 相关简单介绍
    • 消息机制
      消息传递机制
      消息转发机制-动态添加方法
      消息转发机制-快速转发
      消息转发机制-慢速转发
      消息转发机制-崩溃处理
    • MethodSwizzeing方法交换
    • 动态添加属性
    • 获取类的成员变量,进行字典转模型
    • 动态创建类,有兴趣自己研究

    相关简单介绍

    Runtime :运行时,所属框架 #import <objc/message.h>

    • Runtime 是一种运行机制,OC就是运行时机制,运行时动态的根据函数名调用方法(只有声明,编译时未实现不报错),而c:编译时调用(方法未实现时报错
    • 默认情况下,使用Runtime的方法是不会有参数提示的,需要更改一项设置(原因:苹果不推荐我们使用runtime)
    审查.jpg
    方法的存储位置:
    • 对象方法保存在类对象的方法列表,
    • 类方法保存在元类中的方法列表

    扩展:元类
    自行百度

    扩展:

    • 内存分配区
      栈 堆 静态区 常量区 方法区
    • isa指针
      只要是对象(类是类对象,由元类对象创建),就有isa指针
      类自身也有一个isa指针,指向一个元类
    • isa指向


      isa.png
    • Student实例对象->Student类对象->Student元类对象->根元类对象
    • person实例对象->person类对象->person元类对象->根元类对象-
    • nsobject实例对象->nsobject类对象->根元类对象
    消息机制
    • 其实就是方法的调用,告诉对象要干什么,给对象传递一个消息,采用动态绑定机制,运行时去确定要执行的代码
    • 使用场景:调用私有方法的时候,即没有声明只有实现
    消息传递机制(方法的调用流程):

    1.通过isa指针找到对应的类,去类中查找
    2.注册方法编号(把方法名转换成方法编号)
    3.根据方法编号查找对应的方法
    4.找到的是函数实现的地址,根据地址去方法区调用对应的函数
    5.未找到(没有实现方法,只声明)继续从父类找,如果到了根类还未找到,就会触发消息转发机制

    //viewdidload
          [[YSHPerson new] aMouthd:@"hhh"];
    //    objc开头代表只有对象才能发送消息
    //    参数1:消息调用者(对象)   参数2:发送的消息(方法名),参数3:发送的参数
    //    objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
    //无参数
          objc_msgSend([YSHPerson new], @selector(aMouthd:));
    //有参数
          objc_msgSend([YSHPerson new], @selector(aMouthd:),@"hhh");
    //    后面为底层的写法,等效
    //    [YSHPerson new]  等价于  objc_getClass("YSHPerson")
    //    @selector(aMouthd:)  等价于 sel_registerName(aMouthd:)
    
    消息转发机制-动态添加方法(未实现aMouthd:方法)
    //YSHPerson.m 
    //时机:一个对象调用了一个未实现的方法,就会调用这个方法
    //作用:动态添加方法,处理未实现
    //根据需求动态创建的2个动态方法, 一般是实例方法
    //+ (BOOL)resolveClassMethod:(SEL)sel;
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        /*匹配方法
         if (sel == NSSelectorFromString(@"aMouthd:")) {
            //等效于下面2句代码,按个人喜好来
         }
         */
        NSString *methodName = NSStringFromSelector(sel);
        //注意带参数 加上 :冒号
        if ([methodName isEqualToString:@"aMouthd:"]) {
            //动态添加方法
            //参数1:给谁添加  参数2:添加那个方法 参数3: 添加的函数的方法名, 参数4:方法类型,描述参数3这个函数
            return class_addMethod(self, sel, (IMP)aMouthd, "v@:@");
            //v@:@  对应:方法返回参数(返回的void)  对象类型(id/yshperson)  方法类型(SEL) 对象类型(nsstring)
        }
        return [super resolveInstanceMethod:sel];;
    }
    
    //函数方法
    //任何方法都有2个隐式参数 self,  _cmd:当前方法的方法编号
    void aMouthd(id self, SEL _cmd, NSString *str){
        NSLog(@"传递的参数 = %@",str);//hhh
        
    }
    
    

    在方法内判断传过来的方法是不是当前调用的方法,是的话返回一个对应的方法,不是的话走快速转发

    消息转发机制-快速转发

    将消息发给另一个对象去处理,即备用对象,备用去对象中查看是否实现了改方法,实现则执行,未实现则向上从自身继承树寻找,若还没有则走慢速转发

    //快速转发
    //YSHPerson.m
    - (id)forwardingTargetForSelector:(SEL)aSelector //Target:目标
    {
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"aMouthd:"]) {//注意带参数 加上 :
            return [ChapterOne new];//备用对象
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    //ChapterOne.m
    -(void)aMouthd:(NSString *)str
    {
        //输出hhh
        NSLog(@"快慢速转发备用对象ChapterOne——str = = %@",str);
    }
    @end
    
    消息转发机制-慢速转发

    手动生成方法,并转发给另一个对象(1.设置方法签名,2.找另一个对象进行消息转发)

    
    //慢速转发
    //1.方法签名  2.消息转发
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"aMouthd:"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:@"];//签名
        }
        return [super methodSignatureForSelector:aSelector];
    }
    - (void)forwardInvocation:(NSInvocation *)anInvocation //forwardInvocation  向前声明
    {
        SEL sel = [anInvocation selector]; //获得对应的方法编号
        ChapterOne *co = [ChapterOne new];
        if ([co respondsToSelector:sel]) {
            [anInvocation invokeWithTarget:co];
        }else{
            [super forwardInvocation:anInvocation];
        }
        
    }
    
    消息转发机制-崩溃处理

    如果都未找到,则会造成崩溃,补写doesNotRecognizeSelector方法并实现,可以避免崩溃

    //防止调用未被实现的方法的时候,造成崩溃
    -(void)doesNotRecognizeSelector:(SEL)aSelector
    {
        NSLog(@"找不到方法,但是你不能给我崩溃");
    }
    
    MethodSwizzeing方法交换

    交换方法:使用场景:修改系统方法的时候(当然也可以写2个方法进行交换,但是毫无意义)

    //添加一个uiimage的分类 -> UIImage+yshImage.m
    //如下,判断每个图片是否加载成功
    /*
    方案1:修改imagenamed的实现,首先不可以用分类,因为会直接覆盖父类方法,且无法调用[super imagenamed]
    方案2:使用自定义类,但是1.需要频繁导入,2.频繁修改类名,项目庞大时,工作量巨大
    */
    UIImage * im = [UIImage imageNamed:@"1"];
    
    UIImage+yshImage.m
    //方法交换只需要执行一次,所以放在load中,把类加载到内存的时候调用
    + (void)load
    {
        //获取方法名(选择器)
        SEL selInstMethod1 = @selector(imageNamed:);
        SEL selInstMethod2 = @selector(ysh_imageNamed:);
        
        //根据方法名获取方法对象(前两个是实例方法,后2个是类方法)
        //Method method1 = class_getInstanceMethod(self, selInstMethod1);
        //Method method2 = class_getInstanceMethod(self, selInstMethod2);
        Method method1 = class_getClassMethod(self, selInstMethod1);
        Method method2 = class_getClassMethod(self, selInstMethod2);
    
        //交互2个实例方法的实现
        if (!method1 || !method2) {
            NSLog(@"方法交换失败");
            return;
        }
        
        //交换2个方法
        method_exchangeImplementations(method1, method2);
        
    }
    
    + (UIImage *)ysh_imageNamed:(NSString *)str
    {
        UIImage * i = [UIImage ysh_imageNamed:str];//此处因为已经交换方法,所以不能使用imageName,使用了会死循环
        if (i) {
            NSLog(@"yes");
        }else{
            NSLog(@"no");
        }
        return i;
    }
    
    动态添加属性(场景:给系统类添加属性的时候)

    本质是让某个属性与对象产生关联

    //UIImage+yshImage.h
    //定义一个属性,分类中无所谓属性修饰符,反正也不会生成set和get方法的实现,也不会有成员变量
    @property NSString * name;
    
    //UIImage+yshImage.m重写set和get方法
    - (void)setName:(NSString *)name
    {
        //给那个对象添加属性   属性名 属性值  保存策略
        objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    - (NSString *)name
    {
        return objc_getAssociatedObject(self, @"name");
    }
    
    //调用
    UIImage * im = [UIImage imageNamed:@"1"];
        im.name = @"添加了name属性";
        NSLog(@"%@",im.name);//添加了name属性
    
    获取类的成员变量,进行字典转模型
      //MJExtention的字典转模型策略
      //YSHPerson中要有firstName,lastName,major属性
      //Major中有heig  age属性(Major用于2级转换)
      NSDictionary * dic = @{
            @"firstName":@"ysh",
            @"lastName":@"hsy",
            @"major":@{
                @"heig":@"188",
                @"age":@"18"
            }
        };
        YSHPerson * y = [YSHPerson dicToModel:dic];
        Major * m = y.major;
        NSLog(@"%d",m.age);  //18
    

    添加NSObject的分类 NSObject+dicToModel

    //MJExtention: 使用runtime,遍历模型中所有的属性(即根据属性从字段中找,KVC反之,是遍历字典找属性)
    
    //kvc字典转模型,dic中的key在类中必须有,不然会崩溃,可以重写崩溃的方法
    //- (void)setValue:(id)value forUndefinedKey:(NSString *)key
    
    +(instancetype)dicToModel:(NSDictionary *)dic
    {
        id object = [[self alloc]init];
        //方法列表
        //class_copyMethodList(<#Class  _Nullable __unsafe_unretained cls#>, <#unsigned int * _Nullable outCount#>)
        //属性列表
        //class_copyPropertyList(<#Class  _Nullable __unsafe_unretained cls#>, <#unsigned int * _Nullable outCount#>)
        
        //成员变量列表(通常使用这个,属性包含成员变量,定义的是属性,会自动生成_name 成员变量)
        unsigned int count = 0;
        Ivar * ivarList = class_copyIvarList(self, &count);
        for (int i = 0; i < count; i++) {
            //获取成员变量名, 字符char转换成nsstring
            NSString * ivarName = [NSString stringWithUTF8String:ivar_getName(ivarList[i])];
            //去掉下划线的 属性
            NSString * key = [ivarName substringFromIndex:1];
            NSLog(@"成员变量%d,成员变量%@,属性名或者说是字典的key%@",i,ivarName,key);
            //取出了key,之后给模型中的属性赋值
            id value = dic[key];
            
            
            //获取class的类型,假设是Major类
            NSString * ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivarList[i])];
            //获取的是@\"Major\"
            //字符串处理
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            
            //二级转换:value是字典,并且是自定义的模型,都转换成模型
            if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
                
                //获取类Major
                Class modelClass = NSClassFromString(ivarType);
                //给modelClass赋值
                value = [modelClass dicToModel:value];
            }
            //给类的属性赋值
            if (value) {
                [object setValue:value forKey:key];
            }
            
        }
        return object;
    }
    

    相关文章

      网友评论

          本文标题:Runtime

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