使用Runtime的objc_msgSend实现模型和字典的互转

一、介绍

模型转字典,字典转模型,这是开发中最基本的功能。系统类中提供了一个setValuesForKeysWithDictionary方法来实现字典转模型,至于模型转字典,这个就需要使用runtime来实现了。其实字典和模型的互转可以完全使用运行时runtime来实现。典型的第三方有MJExtension和YYModel。现在我就来借助runtime的思想来进行大致的实现。

 

二、思路

  • 字典转模型
    • 遍历key
    • objc_msgSendkey发送消息
  • 模型转字典
    • class_copyPropertyList获取属性列表properties
    • 遍历properties,获取属性名name
    • objc_msgSend对name发送消息,设置value
    • 存入dic并返回

  

三、示例

person.h

//
//  Person.h
//  运行时
//
//  Created by 夏远全 on 2019/10/11.
//  Copyright © 2019 北京华樾教育科技有限公司. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
@property (nonatomic,   copy) NSString *age;
@property (nonatomic,   copy) NSString *name;

/**
 字典转模型
 @param dic 字典
 @return 模型
 */
-(instancetype)initWithDictionary:(NSDictionary *)dic;


/**
 模型转字典
 @return 字典
 */
-(NSDictionary *)convertModelToDictionary;

@end

NS_ASSUME_NONNULL_END

person.m

//
//  Person.m
//  运行时
//
//  Created by 夏远全 on 2019/10/11.
//  Copyright © 2019 北京华樾教育科技有限公司. All rights reserved.
//

#import "Person.h"
#import <objc/message.h>

@implementation Person

-(instancetype)initWithDictionary:(NSDictionary *)dic{
    
    self = [super init];
    if (self) {
        for (NSString *key in dic.allKeys) {
            //创建一个set选择器
            NSString *methodName = [NSString stringWithFormat:@"set%@:",key.capitalizedString];
            SEL selector = NSSelectorFromString(methodName);
            
            //类型异常处理
            id value = dic[key];
            if([value isKindOfClass:[NSNull class]]) continue;
            
            //发送消息
            if (!selector) continue;
            ((void (*)(id, SEL, id))objc_msgSend)(self, selector, value);
        }
    }
    return self;
}

-(NSDictionary *)convertModelToDictionary {
    
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for (int i=0; i<count; i++) {
        objc_property_t property = properties[i];
    
        //属性名称
        const char * name = property_getName(property);
        NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
        
        //创建一个get选择器
        SEL selector = NSSelectorFromString(propertyName);
        
        //发送消息
        if (!selector) continue;
        id value = ((id (*)(id, SEL))objc_msgSend)(self, selector);
        
        //空与类型异常处理
        if(!value || [value isKindOfClass:[NSNull class]]) continue;
        
        //值存储
        [dic setValue:value forKey:propertyName];
    }
    
    //copy创建的需要释放
    free(properties);
    
    return dic;
}

@end

 

四、测试

1、dic -> model

-(void)test_objc_DicToModel {
    
    /// 这里主要提供一个思想,全部用字符串作为字段。其他类型:对象类型/NSNumber/int/float...,需要自己再去特殊处理
    NSDictionary *dic = @{@"age":@"20",@"name":@"张三"};
    Person *person = [[Person alloc] initWithDictionary:dic];
    NSLog(@"age=%@,name=%@",person.age, person.name);
}
2019-10-11 18:44:47.366662+0800 运行时[84069:2402809] age=20,name=张三

2、model -> dic

-(void)test_objc_ModelToDic {
    
    NSDictionary *dic = @{@"age":@"20",@"name":@"张三"};
    Person *person = [[Person alloc] initWithDictionary:dic];
    NSDictionary *dic2 = [person convertModelToDictionary];
    NSLog(@"%@",dic2);
}
2019-10-11 18:44:47.367024+0800 运行时[84069:2402809] {
    age = 20;
    name = "\U5f20\U4e09";
}

 

posted @ 2019-10-11 18:45  XYQ全哥  阅读(452)  评论(2编辑  收藏  举报