Runtime

作者: AlanGe | 来源:发表于2017-08-15 23:38 被阅读14次

    一、runtime简介

    • RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。
    • 对于C语言,函数的调用在编译的时候会决定调用哪个函数。
    • 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
    • 事实证明:
      • 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
      • 在编译阶段,C语言调用未实现的函数就会报错。

    二、runtime作用

    1.发送消息

    • 方法调用的本质,就是让对象发送消息。
    • objc_msgSend,只有对象才能发送消息,因此以objc开头.
    • 使用消息机制前提,必须导入#import <objc/message.h>
    • 消息机制简单使用
    //  Person.h
    //  Runtime(消息机制)
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    + (void)eat;
    - (void)run:(int)age;
    - (void)eat;
    @end
    
    //  Person.m
    //  Runtime(消息机制)
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)eat
    
    {
        NSLog(@"对象方法-吃东西");
    }
    
    + (void)eat
    
    {
        NSLog(@"类方法-吃东西");
    }
    
    - (void)run:(int)age
    
    {
        NSLog(@"%d",age);
    }
    @end
    
    
    //  ViewController.m
    //  Runtime(消息机制)
    
    #import "ViewController.h"
    
    #import "Person.h"
    
    // 运行时使用运行时的步骤
    // 第一步:导入<objc/message.h>
    // 第二步:Build Setting -> 搜索msg -> 设置属性为No
    #import <objc/message.h>
    
    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 创建person对象
        Person *p = [[Person alloc] init];
        
        // 调用对象方法
        [p eat];
        
        // OC:运行时机制,消息机制是运行时机制最重要的机制
        // 消息机制:任何方法调用,本质都是发送消息
        
        // SEL:方法编号,根据方法编号就可以找到对应方法实现
        // performSelector:动态添加方法
        [p performSelector:@selector(eat)];
        
        // 运行时,发送消息,谁做事情就那谁
        // xcode5之后,苹果不建议使用底层方法
        // xcode5之后,使用运行时.
        
        // 让p发送消息
        // 不带参数
        objc_msgSend(p, @selector(eat));
        // 带参数
        objc_msgSend(p, @selector(run:),10);
        
        // 调用类方法的方式:两种
        // 第一种通过类名调用,类名调用类方法,本质类名转换成类对象
        [Person eat];
        // 第二种通过类对象调用
        [[Person class] eat];
        
        // 获取类对象
        Class personClass = [Person class];
        
        [personClass performSelector:@selector(eat)];
        
        // 运行时
        // 用类名调用类方法,底层会自动把类名转换成类对象调用
        // 本质:让类对象发送消息
        objc_msgSend(personClass, @selector(eat));
    }
    
    @end
    
    • 消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现


    2.交换方法

    • 开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。
    • 方式一:继承系统的类,重写方法.
    • 方式二:使用runtime,交换方法.
    //  UIImage+Image.h
    //  Runtime(交换方法)
    
    #import <UIKit/UIKit.h>
    
    @interface UIImage (Image)
    + (__kindof UIImage *)ge_imageNamed:(NSString *)imageName;
    @end
    
    //  UIImage+Image.m
    //  Runtime(交换方法)
    
    #import "UIImage+Image.h"
    
    #import <objc/message.h>
    
    @implementation UIImage (Image)
    
    // 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.
    // 在分类里面不能调用super,分类木有父类
    //+ (UIImage *)imageNamed:(NSString *)name
    //{
    //    [super im]
    //}
    
    // 利用运行时
    // 先写一个其他方法,实现这个功能
    // 既能加载图片又能打印
    + (UIImage *)ge_imageNamed:(NSString *)imageName
    {
        // 1.加载图片
        UIImage *image = [UIImage ge_imageNamed:imageName];
        
        // 2.判断功能
        if (image == nil) {
            NSLog(@"加载image为空");
        }
    
        return image;
    }
    
    // 加载这个分类的时候调用
    + (void)load
    {
        // 交换方法实现,方法都是定义在类里面
        // class_getMethodImplementation:获取方法实现
        // class_getInstanceMethod:获取对象
        // class_getClassMethod:获取类方法
        // IMP:方法实现
        
        // imageNamed
        // Class:获取哪个类方法
        // SEL:获取方法编号,根据SEL就能去对应的类找方法
        Method imageNameMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
        
        // ge_imageNamed
        Method ge_imageNamedMethod = class_getClassMethod([UIImage class], @selector(ge_imageNamed:));
        
        // 交换方法实现
        method_exchangeImplementations(imageNameMethod, ge_imageNamedMethod);
    }
    @end
    
    //  ViewController.m
    //  Runtime(交换方法)
    
    #import "ViewController.h"
    //#import "UIImage+Image.h"
    
    @interface ViewController ()
    
    @end
    
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。
        // 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
        // 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。
        
    //    UIImage *image = [UIImage imageNamed:@"123"];
        // 不好的地方
        // 1.每次使用,都需要导入头文件
        // 2.当一个项目开发太久,使用这个方式不靠谱
        
        // imageNamed:
        // 实现方法:底层调用ge_imageNamed
        
        // 本质:交换两个方法的实现imageNamed和ge_imageNamed方法
        // 调用imageNamed其实就是调用ge_imageNamed
        
        // 系统imageNamed加载图片,并不知道图片是否加载成功
        // 交换以后调用imageNamed的时候,就知道图片是否加载
        
        [UIImage imageNamed:@"123"];
    }
    
    @end
    
    • 交换原理:
      • 交换之前:
      • 交换之前:

    3.动态添加方法

    • 开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
    • 经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。
    • 简单使用
    //  Person.h
    //  Runtime(动态添加方法)
    
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    
    @end
    
    
    //  Person.m
    //  Runtime(动态添加方法)
    
    #import "Person.h"
    
    #import <objc/message.h>
    
    @implementation Person
    
    // 动态添加方法,首先实现这个resolveInstanceMethod
    // resolveInstanceMethod调用:当调用了没有实现的方法没有实现就会调用resolveInstanceMethod
    // resolveInstanceMethod作用:就知道哪些方法没有实现,从而动态添加方法
    // sel:没有实现方法
    
    // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
    // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
    //    NSLog(@"%@",NSStringFromSelector(sel));
        
        // 动态添加eat方法
        if (sel == @selector(eat:)) {
            
            // 第一个参数:cls:给哪个类添加方法
            // 第二个参数:SEL:添加方法的方法编号是什么
            // 第三个参数:IMP:方法实现,函数入口,函数名
            // 第四个参数:types:方法类型
                // v 表示 void
                // @ 表示对象
                // : 表示SEL
            class_addMethod(self, sel, (IMP)aaaa, "v@:@");
            
            // 处理完
            return YES;
        }
        
        // 返回系统的方法,因为上面改了,尽量不要修改系统的方法
        return [super resolveInstanceMethod:sel];
    }
    
    // 默认一个方法都有两个参数,self,_cmd,属于隐式参数
    // self:方法调用者
    // _cmd:调用方法的编号
    
    // 定义函数
    // 没有返回值,参数(id,SEL)
    // void(id,SEL)
    void aaaa(id self, SEL _cmd, id param1)
    {  
        NSLog(@"调用eat %@ %@ %@",self,NSStringFromSelector(_cmd),param1);
    }
    @end
    
    //  ViewController.m
    //  Runtime(动态添加方法)
    
    #import "ViewController.h"
    #import "Person.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // performSelector:动态添加方法
        Person *p = [[Person alloc] init];
        
        // 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
        // 通过运行时后,动态添加方法就不会报错
        
        // 动态添加方法
        // 不带参数
        [p performSelector:@selector(eat)];
        // 带参数
        [p performSelector:@selector(eat:) withObject:@111];
    }
    @end
    

    4.给分类添加属性

    • 原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
    //  NSObject+Objc.h
    //  Runtime(分类添加属性)
    
    #import <Foundation/Foundation.h>
    
    @interface NSObject (Objc)
    
    // @property:只会生成set方法的声明,不会实现
    @property (nonatomic, strong) NSString *name;
    
    @end
    
    //  NSObject+Objc.m
    //  Runtime(分类添加属性)
    
    #import "NSObject+Objc.h"
    
    #import <objc/message.h>
    
    @implementation NSObject (Objc)
    
    // 定义关联的key
    //static NSString *_name;
    
    // set方法
    - (void)setName:(NSString *)name {
        // 添加属性,跟对象
        // 给某个对象产生关联,添加属性
        // 第一个参数:object:给哪个对象添加属性
        // 第二个参数:key:属性名,根据key去获取关联的对象 ,void * == id
        // 第三个参数:value:关联的值
        // 第四个参数:policy:缓存策略
        objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    // get方法
    - (NSString *)name {
        // 根据关联的key,获取关联的值。
        return objc_getAssociatedObject(self, @"name");
    }
    @end
    
    //  ViewController.m
    //  Runtime(分类添加属性)
    
    #import "ViewController.h"
    #import "NSObject+Objc.h"
    
    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        NSObject *objc = [[NSObject alloc] init];
        objc.name = @"123";
        NSLog(@"%@",objc.name);
    }
    @end
    

    5.字典转模型

    • 设计模型:字典转模型的第一步
      • 模型属性,通常需要跟字典中的key一一对应
      • 问题:一个一个的生成模型属性,很慢?
      • 需求:能不能自动根据一个字典,生成对应的属性。
      • 解决:提供一个分类,专门根据字典生成对应的属性字符串。
    //  NSObject+Property.h
    //  自动生成属性代码
    
    //  通过解析字典自动生成属性代码
    
    #import <Foundation/Foundation.h>
    
    @interface NSObject (Property)
    
    ///  通过解析字典自动生成属性代码
    + (void)createPropertyCodeWithDict:(NSDictionary *)dict;
    
    @end
    
    //  NSObject+Property.m
    //  自动生成属性代码
    
    //  通过解析字典自动生成属性代码
    
    #import "NSObject+Property.h"
    
    @implementation NSObject (Property)
    
    ///  通过解析字典自动生成属性代码
    + (void)createPropertyCodeWithDict:(NSDictionary *)dict {
        // 拼接属性字符串代码
        NSMutableString *strM = [NSMutableString string];
        
        /*********************** 方法1 ***************************/
        // 遍历字典
        [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull propertyName, id  _Nonnull value, BOOL * _Nonnull stop) {
    //        NSLog(@"%@ %@",propertyName,[value class]);
            
            // 类型经常变,抽出来
            NSString *code;
            
            if ([value isKindOfClass:NSClassFromString(@"__NSCFString")]) {
                code = [NSString stringWithFormat:@"@property (nonatomic, copy) NSString *%@;",propertyName]
                ;
            }else if ([value isKindOfClass:NSClassFromString(@"__NSCFNumber")]){
                code = [NSString stringWithFormat:@"@property (nonatomic, assign) int %@;",propertyName]
                ;
            }else if ([value isKindOfClass:NSClassFromString(@"__NSCFArray")]){
                code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",propertyName]
                ;
            }else if ([value isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){
                code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",propertyName]
                ;
            }else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
                code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",propertyName]
                ;
            }
            // 每生成属性字符串,就自动换行。
            [strM appendFormat:@"\n%@\n",code];
        }];
        
        /*********************** 方法2 ***************************/
        // 1.遍历字典,把字典中的所有key取出来,生成对应的属性代码
    //    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
    //        // 类型经常变,抽出来
    //        NSString *type;
    //        if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) {
    //            type = @"NSString";
    //        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){
    //            type = @"NSArray";
    //        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){
    //            type = @"int";
    //        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){
    //            type = @"NSDictionary";
    //        }
    //        // 属性字符串
    //        NSString *str;
    //        if ([type containsString:@"NS"]) {
    //            str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];
    //        }else{
    //            str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];
    //        }
    //        // 每生成属性字符串,就自动换行。
    //        [strM appendFormat:@"\n%@\n",str];
    //    }];
        
        // 把拼接好的字符串打印出来,就好了。
        NSLog(@"strM = %@",strM);
    }
    
    @end
    
    //  ViewController.m
    //  Runtime(自动生成属性代码)
    
    #import "ViewController.h"
    
    #import "NSObject+Property.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 解析Plist
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
        NSArray *dictArr = dict[@"statuses"];
        
        // 设计模型属性代码,生成打印后赋值粘贴到模型即可
        [NSObject createPropertyCodeWithDict:dictArr[0]];
    }
    @end
    
    • 字典转模型的方式一:KVC
    //  Status.m
    //  字典转模型KVC实现
    
    #import "Status.h"
    
    @implementation Status
    
    // 字典转模型 - 模型的属性名跟字典一一对应
    + (Status *)statusWithDict:(NSDictionary *)dict {
        Status *status = [[self alloc] init];
        
        // KVC
        [status setValuesForKeysWithDictionary:dict];
        
        return status;
    }
    @end
    
    • KVC字典转模型弊端:必须保证,模型中的属性和字典中的key一一对应。
      • 如果不一致,就会调用[<Status 0x7fa74b545d60> setValue:forUndefinedKey:] 报key找不到的错。
      • 分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。
      • 解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖, 就能继续使用KVC,字典转模型了。
    //  Status.m
    // 转模型KVC实现
    
    #import "Status.h"
    
    @implementation Status
    
    // 字典转模型 - 模型的属性名跟字典一一对应
    + (Status *)statusWithDict:(NSDictionary *)dict
    {
        Status *status = [[self alloc] init];
        
        // KVC
        [status setValuesForKeysWithDictionary:dict];
        
        return status;
    }
    
    // 解决KVC报错
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key
    {
        if ([key isEqualToString:@"id"]) {
            _ID = [value integerValue];
        }
        // key:没有找到key
        // value:没有找到key对应的值
        NSLog(@"没有找到key = %@,没有找到key对应的值 = %@",key,value);
    }
    
    @end
    
    //  ViewController.m
    //  Runtime(字典转模型KVC实现)
    
    #import "ViewController.h"
    #import "NSObject+Property.h"
    #import "Status.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 解析Plist
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
        NSArray *dictArr = dict[@"statuses"];
        
        // 设计模型属性代码,生成打印后赋值粘贴到模型即可
    //    [NSObject createPropertyCodeWithDict:dictArr[0]];
        
        NSMutableArray *statuses = [NSMutableArray array];
        
        for (NSDictionary *dict in dictArr) {
            // 字典转模型
            Status *status = [Status statusWithDict:dict];
            
            [statuses addObject:status];
        }
        
        NSLog(@"%@",statuses);
    }
    
    @end
    
    • 字典转模型的方式二:Runtime
      • 思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。
      • 步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。
    //  User.h
    //  Runtime(字典转模型)
    
    #import <Foundation/Foundation.h>
    
    @interface User : NSObject
    
    @property (nonatomic, copy) NSString *profile_image_url;
    
    @property (nonatomic, assign) BOOL vip;
    
    @property (nonatomic, copy) NSString *name;
    
    @property (nonatomic, assign) int mbrank;
    
    @property (nonatomic, assign) int mbtype;
    
    @end
    
    //  User.m
    //  Runtime(字典转模型)
    
    #import "User.h"
    
    @implementation User
    
    @end
    
    //  Status.h
    //  Runtime(字典转模型)
    
    #import <Foundation/Foundation.h>
    
    @class User;
    
    @interface Status : NSObject
    
    // 写一段程序自动生成属性代码
    
    @property (nonatomic, assign) NSInteger ID;
    // 解析字典自动生成属性代码
    @property (nonatomic, strong) NSString *source;
    
    @property (nonatomic, assign) NSInteger reposts_count;
    
    @property (nonatomic, strong) NSArray *pic_urls;
    
    @property (nonatomic, strong) NSString *created_at;
    
    @property (nonatomic, assign) int attitudes_count;
    
    @property (nonatomic, strong) NSString *idstr;
    
    @property (nonatomic, strong) NSString *text;
    
    @property (nonatomic, assign) int comments_count;
    
    @property (nonatomic, strong) User *user;
    
    @property (nonatomic, strong) NSDictionary *retweeted_status;
    
    @end
    
    //  Status.m
    //  Runtime(字典转模型)
    
    #import "Status.h"
    
    @implementation Status
    
    @end
    
    //  NSObject+Model.h
    //  Runtime(字典转模型)
    
    //  Runtime字典转模型分类
    
    #import <Foundation/Foundation.h>
    
    @interface NSObject (Model)
    
    + (instancetype)modelWithDict:(NSDictionary *)dict;
    
    @end
    
    //  NSObject+Model.m
    //  Runtime(字典转模型)
    
    //  Runtime字典转模型分类
    
    #import "NSObject+Model.h"
    
    #import <objc/message.h>
    /*
     Ivar ivar1;
     Ivar ivar2;
     Ivar ivar3;
     Ivar a[] = {ivar3,ivar1,ivar2};
     Ivar *ivar = &a;
     */
    
    @implementation NSObject (Model)
    
    + (instancetype)modelWithDict:(NSDictionary *)dict{
        
        // 1.创建对应类的对象
        id objc = [[self alloc] init];
        
        // 2.利用runtime给对象中的成员属性赋值
        
        // runtime:遍历模型中所有成员属性,去字典中查找
        // 属性定义在哪,定义在类,类里面有个属性列表(数组)
        
        // 遍历模型所有成员属性
        // ivar:成员属性
        // class_copyIvarList:把成员属性列表复制一份给你
        // Ivar *:指向Ivar指针
        // Ivar *:指向一个成员变量数组
        // class:获取哪个类的成员属性列表
        // count:成员属性总数
        unsigned int count = 0;
        Ivar *ivarList = class_copyIvarList(self, &count);
        for (int i = 0 ; i < count; i++) {
            // 获取成员属性
            Ivar ivar = ivarList[i];
            
            // 获取成员名
            NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
            // 获取key
            NSString *key = [propertyName substringFromIndex:1];
            
            // user value:字典
            // 获取字典的value
            id value = dict[key];
            // 给模型的属性赋值
            // value:字典的值
            // key:属性名
            
            // 获取成员属性类型
            NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            
            // user:NSDictionary
            // 二级转换
            // 值是字典,成员属性的类型不是字典,才需要转换成模型
            if ([value isKindOfClass:[NSDictionary class]] && ![propertyType containsString:@"NS"]) {
                
                // 字典转模型
                // 获取模型的类对象,调用modelWithDict
                // 模型的类名已知,就是成员属性的类型
                
                // 需要字典转换成模型
                // 转换成哪个类型
    //            NSLog(@"转换成哪个类型 = %@",propertyType);
                
                /*********** *********** 字符串截取 *********** ***********/
                // 字符串截取
                // 生成的是这种@"@\"User\"" 类型 -> @"User"  在OC字符串中 \" -> ",\是转义的意思,不占用字符
                // @"@\"User\"" User
                // \":算一个字符
                NSRange range = [propertyType rangeOfString:@"\""];
                propertyType = [propertyType substringFromIndex:range.location + range.length];
                // User\"";
                // 裁剪到哪个角标,不包括当前角标
                range = [propertyType rangeOfString:@"\""];
                propertyType = [propertyType substringToIndex:range.location];
                /*********** *********** 字符串截取 *********** ***********/
                
                // 获取需要转换类的类对象
                // 根据字符串类名生成类对象
                Class modelClass =  NSClassFromString(propertyType);
                
                // 有对应的模型才需要转
                if (modelClass) {
                    // 字典转模型
                    value =  [modelClass modelWithDict:value];
                }
            }
            
    //        // 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
    //        // 判断值是否是数组
    //        if ([value isKindOfClass:[NSArray class]]) {
    //            // 判断对应类有没有实现字典数组转模型数组的协议
    //            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
    //                // 转换成id类型,就能调用任何对象的方法
    //                id idSelf = self;
    //                // 获取数组中字典对应的模型
    //                NSString *type =  [idSelf arrayContainModelClass][key];
    //                // 生成模型
    //                Class classModel = NSClassFromString(type);
    //                NSMutableArray *arrM = [NSMutableArray array];
    //                // 遍历字典数组,生成模型数组
    //                for (NSDictionary *dict in value) {
    //                    // 字典转模型
    //                    id model =  [classModel modelWithDict:dict];
    //                    [arrM addObject:model];
    //                }
    //                // 把模型数组赋值给value
    //                value = arrM;
    //            }
    //        }
            
            // 有值,才需要给模型的属性赋值
            if (value) {
                // KVC赋值:不能传空
                [objc setValue:value forKey:key];
            }
        }
        return objc;
    }
    @end
    
    //  ViewController.m
    //  Runtime(字典转模型)
    
    #import "ViewController.h"
    #import "Status.h"
    #import "User.h"
    #import "NSObject+Model.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    /*
     KVC:遍历字典中所有key,去模型中查找有没有对应的属性名,没有对应的key就会报错
     runtime:遍历模型中所有属性名,去字典中查找,这样不会像KVC那样报错
     */
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 解析Plist
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
        NSArray *dictArr = dict[@"statuses"];
            
        NSMutableArray *statuses = [NSMutableArray array];
        // 遍历字典数组
        for (NSDictionary *dict in dictArr) {
            //  Runtime(字典转模型)
            Status *status = [Status modelWithDict:dict];
            [statuses addObject:status];
            User *user = status.user;
            NSString *name = user.name;
            NSLog(@"name = %@",name);
        }
        
    //    NSLog(@"%@",statuses);
    }
    @end
    

    相关文章

      网友评论

          本文标题:Runtime

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