YYModel , 模型字典,自动转化,
模型转字典, 主要是通过运行时,把模型的属性名取出来,递归构建字典
YYModel 设计 4 个模型,记录类与其属性信息,
-
类 + 信息记录
-
类
-
属性 + 信息记录
-
属性
两个用于类,两个用于属性
如果从上到下,逐个包含,就很清晰
YYModel 为了方便逻辑处理,四个模型包含交叉,有点绕
本文通过模型转字典,看下其运转
调用:
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
Book * page = [Book yy_modelWithJSON: jsonData];
NSDictionary * dict = [page yy_modelToJSONObject];
实现:
- (id)yy_modelToJSONObject {
// 取出 json
id jsonObject = ModelToJSONObjectRecursive(self);
// 判断数组
if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
// 判断字典
if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
return nil;
}
对模型,递归处理
// 拿到模型,算出字典
static id ModelToJSONObjectRecursive(NSObject *model) {
// 不存在,就返回
if (!model || model == (id)kCFNull) return model;
// 抵达终端的值
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) return model;
// ...
// 处理字典
// 处理集合 Set
// 处理数组
if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
if ([model isKindOfClass:[NSDate class]]) return @"ha ha";
if ([model isKindOfClass:[NSData class]]) return nil;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
if (!modelMeta) return nil;
// 建立字典
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
__unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
if (!propertyMeta->_getter) return;
// 获取值
id value = nil;
if (propertyMeta->_isCNumber) {
value = ModelCreateNumberFromProperty(model, propertyMeta);
} else if (propertyMeta->_nsTypeX) {
// _nsTypeX 是苹果默认对象
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
} else {
switch (propertyMeta->_typeA & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
// 处理自定义对象
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
// 对于对象,递归处理
value = ModelToJSONObjectRecursive(v);
if (value == (id)kCFNull) value = nil;
} break;
case YYEncodingTypeClass: {
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
} break;
case YYEncodingTypeSEL: {
SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromSelector(v) : nil;
} break;
default: break;
}
}
if (!value) return;
if (!dic[propertyMeta->_mappedToKey]) {
// 赋值
dic[propertyMeta->_mappedToKey] = value;
}
}];
return result;
}
其中,
if (propertyMeta->_nsTypeX)
如果 _nsTypeX 不存在,就是 _nsTypeX = 0, YYEncodingTypeNSUnknown
结合下面代码,
如果 _nsTypeX 是 NSString 、NSNumber、NSValue、NSData、NSArray、NSDictionary, 就递归处理掉
static YYEncodingNSType YYClassGetNSType(Class cls){
if (!cls) return YYEncodingTypeNSUnknown;
if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
return YYEncodingTypeNSUnknown;
}
runtime:
@interface NSObject (Model)
给 NSObject 加扩展,model 可以直接调用
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic方法中,
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
@interface _YYModelMeta: NSObject
类信息,并添加自定义信息
先类方法,再实例方法
类方法加锁,做缓存
实例方法,处理属性
+ (instancetype)metaWithClass:(Class)cls方法中,
meta = [[_YYModelMeta alloc] initWithClass:cls];
- (instancetype)initWithClass:(Class)cls方法中,
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
@interface YYClassInfo : NSObject
类信息
先类方法,再实例方法
类方法加锁,做缓存
实例方法,处理属性
+ (instancetype)classInfoWithClass:(Class)cls方法中,
info = [[YYClassInfo alloc] initWithClass:cls];
- (instancetype)initWithClass:(Class)cls方法中,
- (instancetype)initWithClass:(Class)cls {
// ...
unsigned int propertyCount = 0;
// 从类取出属性
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = 0; i < propertyCount; i++) {
// 实例化,每一个属性
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}
// ...
return self;
}
@interface YYClassPropertyInfo : NSObject
属性信息
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
const char *name = property_getName(property);
if (name) {
// 获取属性名,做了一个 UTF8 编码
_name = [NSString stringWithUTF8String:name];
}
YYEncodingType type = 0;
unsigned int attrCount;
// 从属性中,翻出所有的信息
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
// 处理属性中,每一条信息
switch (attrs[i].name[0]) {
case 'T': { // Type encoding
if (attrs[i].value) {
// 拿到编码方式
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
type = YYEncodingGetType(attrs[i].value);
if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
// 拿到类信息
if (clsName.length) _clsX = objc_getClass(clsName.UTF8String);
}
// _protocols
// ...
}
}
} break;
// ...
}
}
// ...
return self;
}
@interface _YYModelPropertyMeta: NSObject
属性信息,和属性的添加类中
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// ...
_YYModelPropertyMeta *meta = [self new];
// 把上一步,获取的属性信息,拷贝出来
meta->_name = propertyInfo.name;
meta->_typeA = propertyInfo.type;
meta->_info = propertyInfo;
// ...
// 获取类型信息
if ((meta->_typeA & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsTypeX = YYClassGetNSType(propertyInfo.clsX);
} else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_typeA);
}
模型转字典部分,通过记录的属性类型,处理
是数字,直接处理
非数字,系统类型优先处理
最后的情况下,处理对象等