MJExtension实现原理简单剖析

MJExtension实现原理简单剖析

剖析MJExtension实现字典转模型的大致流程,我们不难发现分为4步:
1.遍历模型中的所有属性
2.解析属性的类型
3.以属性名为key在字典中寻找对应的值
4.根据属性的类型转化成对应的值并赋值
首先实现第一步:
遍历模型以及模型父类的所有属性,重新封装一个Property类,然后将Property对象放在数组中以供调用

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@class Property;

//遍历属性的block
typedef void(^PropertyEnumeration)(Property *property, BOOL *stop);

@interface NSObject (Property)

+ (void)enumerateProperties:(PropertyEnumeration)enumeration;

+ (NSMutableArray *)properties;

@end

NS_ASSUME_NONNULL_END

@implementation NSObject (Property)

//枚举所有的属性
+ (void)enumerateProperties:(PropertyEnumeration)enumeration {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSArray *properties = [self properties];
    dispatch_semaphore_signal(semaphore);
    BOOL stop = NO;
    for (Property *property in properties) {
        enumeration(property, &stop);
        if (stop) {
            break;
        }
    }
}

+ (NSMutableArray *)properties {
    __block NSMutableArray *properties = [NSMutableArray new];
    //遍历类本身以及父类 基础类除外
    [self enumSuperClasses:^(Class  _Nonnull __unsafe_unretained c, BOOL * _Nonnull stop) {
        unsigned int count = 0;
        objc_property_t *obj_properties = class_copyPropertyList(c, &count);
        for (int i = 0; i < count; i ++) {
            Property *property = [[Property alloc]init];
            property.property = obj_properties[i];
            property.srcClass = c;
            [properties addObject:property];
        }
    }];
    
    return properties;
}

@end

封装一个Property类,表示我们解析出来的属性

NS_ASSUME_NONNULL_BEGIN

@interface Property : NSObject

@property (nonatomic, assign) objc_property_t property;
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) PropertyType *type;
//属性所属的类
@property (nonatomic, assign) Class srcClass;


- (void)setValue:(id)value forObject:(id)object;

- (id)valueForObject:(id)object;

@end

NS_ASSUME_NONNULL_END


@implementation Property

- (void)setProperty:(objc_property_t)property {
    _property = property;
    
    //属性名
    _name = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
    
    //成员类型
    NSString *attributed = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
//    NSLog(@"name = %@",_name);
//    NSLog(@"attributed = %@", attributed);
//    attributed = @"T@\"NSString\"";
    NSString *code = nil;
    NSArray *attributeArray = [attributed componentsSeparatedByString:@","];
    if (attributeArray.count > 0) {
        NSString *attributeStr = attributeArray[0];
        code = [attributeStr substringWithRange:NSMakeRange(1, attributeStr.length - 1)];
    }
//    NSLog(@"code = %@",code);
    PropertyType *type = [[PropertyType alloc]init];
    _type = type;
    type.code = code;
}

- (void)setValue:(id)value forObject:(id)object {
    [object setValue:value forKey:self.name];
}

- (id)valueForObject:(id)object {
    return [self valueForKey:self.name];
}

@end

2.解析属性的类型:
根据属性的property_getAttributes,获取到属性的属性串,解析属性串我们可以得到属性类型,这里我们封装了一个PropertyType来表示属性类型

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface PropertyType : NSObject

@property (nonatomic, copy) NSString *code;

@property (nonatomic, readonly) BOOL idType;

@property (nonatomic, readonly) BOOL numberType;

@property (nonatomic, readonly) BOOL boolType;

@property (nonatomic, readonly) Class typeClass;

@property (nonatomic, readonly) BOOL fromFoundation;

@property (nonatomic, readonly) BOOL KVCDisable;

@end

NS_ASSUME_NONNULL_END


@implementation PropertyType

- (void)setCode:(NSString *)code {
    _code = code;
    if ([code isEqualToString:PropertyTypeId]) {
        //ID类型
        _idType = YES;
    } else if (code.length == 0) {
        //无类型
        _KVCDisable = YES;
    } else if (code.length > 3 && [code hasPrefix:@"@\""]) {
        //正常类类型
        _code = [code substringWithRange:NSMakeRange(2, code.length - 3)];
        _typeClass = NSClassFromString(_code);
        _fromFoundation = [Foundation isClassFromFoundation:_typeClass];
        _numberType = [_typeClass isSubclassOfClass:[NSNumber class]];
    } else if ([code isEqualToString:PropertyTypeSEL] ||
               [code isEqualToString:PropertyTypeIvar] ||
               [code isEqualToString:PropertyTypeMethod]) {
        _KVCDisable = YES;
    }
    
    //是否为数字类型
    NSString *lowerCode = _code.lowercaseString;
    NSArray *numberTypes = @[PropertyTypeInt,PropertyTypeShort,PropertyTypeBOOL1,PropertyTypeBOOL2,PropertyTypeFloat,PropertyTypeDouble,PropertyTypeLong,PropertyTypeLongLong,PropertyTypeChar];
    if ([numberTypes containsObject:lowerCode]) {
        _numberType = YES;
        
        if ([lowerCode isEqualToString:PropertyTypeBOOL1] ||
            [lowerCode isEqualToString:PropertyTypeBOOL2]) {
            _boolType = YES;
        }
    }
}

@end

到此为止,我们已经重组了所有的模型属性,接下来就是赋值环节了,我们定义一个字典,一个数据模型,试着转一下:
数据模型

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;
@property BOOL isMan;

@end

NS_ASSUME_NONNULL_END

- (NSString *)description
{
    return [NSString stringWithFormat:@"{\n class = %@\n name = %@\n age = %ld \n isMan = %d \n}",
            NSStringFromClass(self.class),
            _name,
            _age,
            _isMan];
}

字典

 NSDictionary *dict = @{@"name":@"xiaoxiao",
                           @"age":@13,
                           @"isMan":@0
                           };
    Person *p = [Person objectWithKeyValues:dict];
    NSLog(@"person = %@",p);

转换赋值函数

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (KeyValue)

+ (instancetype)objectWithKeyValues:(id)keyValues;

@end

NS_ASSUME_NONNULL_END



@implementation NSObject (KeyValue)
+ (instancetype)objectWithKeyValues:(id)keyValues {
    if (!keyValues) {
        return nil;
    }
    return [[[self alloc] init]setKeyValues:keyValues];
}

- (instancetype)setKeyValues:(id)keyValues {
    NSArray *propertyArray = [self.class properties];
    for (Property *property in propertyArray) {
        PropertyType *type = property.type;
        Class typeClass = type.typeClass;
        id value = [keyValues valueForKey:property.name];
        if (!value) {
            continue;
        }
        if (type.boolType) {
            NSLog(@"BOOL");
        } else if(type.idType) {
            NSLog(@"ID");
        } else if (type.numberType) {
            NSLog(@"Number");
            if ([value isKindOfClass:[NSString class]]) {
               value = [[[NSNumberFormatter alloc]init]numberFromString:value];
            }
        } else {
            NSLog(@"%@",NSStringFromClass(typeClass));
        }
        [self setValue:value forKey:property.name];
    }
    return self;
}
@end

运行打印结果:

2019-04-17 12:17:37.007307+0800 RunTimeLearnApp[7833:433702] NSString
2019-04-17 12:17:37.007425+0800 RunTimeLearnApp[7833:433702] Number
2019-04-17 12:17:37.007519+0800 RunTimeLearnApp[7833:433702] BOOL
2019-04-17 12:17:37.007666+0800 RunTimeLearnApp[7833:433702] person = {
 class = Person
 name = xiaoxiao
 age = 13 
isMan = 0 
}

这只是MJExtension实现的最基础的原理,作为商业性项目的MJExtension做的更完善,包括键值替换、多级映射等,当然也更加全面稳定。
这只是一个学习心得,功力不够,如有错误欢迎@,共同学习进步。

附上MJExtension三方库链接:
MJExtension源代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值