- 目錄
1、模型在軟件開發(fā)中的作用
2、誕生背景
3、整體架構(gòu)
4、核心流程
模型在軟件開發(fā)中的作用
后臺下發(fā)的數(shù)據(jù)格式一般是JSON格式,我們可以方便的將其轉(zhuǎn)換為字典,即便如此,在使用時候仍然存在一些問題,如key值直接寫在代碼中,不能一眼看清下發(fā)的數(shù)據(jù)結(jié)構(gòu),維護(hù)起來極其不方便,而且通過字典數(shù)組的方式獲取數(shù)據(jù)一個不小心就會crash,最主要的是會花費(fèi)很多時間在字段匹配上,這時候模型的作用就體現(xiàn)出來了
@interface Person : NSObject
// 接口文檔的鏈接注釋在這里
@property (nonatomic, copy) NSString *name; // 名字
@property (nonatomic, assign) NSUInterger age; // 年齡
...
@end
如上,接口下發(fā)的數(shù)據(jù)結(jié)構(gòu)一目了然,直接訪問屬性方便、安全,而且可以直接定位到相關(guān)文檔,也便于后續(xù)拓展,這就是抽象的力量
誕生背景
將后臺下發(fā)的數(shù)據(jù)結(jié)構(gòu)抽象成Model,給程序員帶來極大的方便,但是隨著業(yè)務(wù)的爆發(fā),這種數(shù)據(jù)轉(zhuǎn)模型的代碼會急劇增加,而且各個業(yè)務(wù)接口的數(shù)據(jù)結(jié)構(gòu)幾乎都不相同,轉(zhuǎn)換的代價會越來越大,并且難于維護(hù),每次修改數(shù)據(jù)結(jié)構(gòu)都要修改數(shù)據(jù)轉(zhuǎn)模型部分的代碼,這大大降低了敏捷分開的迭代的速度,這時候程序員迫切需要一種數(shù)據(jù)自動轉(zhuǎn)模型的工具,于是YYModel誕生了
整體架構(gòu)
一共有兩個實現(xiàn)文件NSObject+YYModel和YYClassInfo:3個分類,6個類,如下
| NSObject+YYModel | YYClassInfo |
|---|---|
| NSObject (YYModel) | YYClassIvarInfo |
| NSArray (YYModel) | YYClassMethodInfo |
| NSDictionary (YYModel) | YYClassPropertyInfo |
| _YYModelPropertyMeta | YYClassInfo |
| _YYModelMeta |
YYClassInfo是類對象的OC封裝,內(nèi)部聚合了Ivar ivar、Method method、objc_property_t property
如下:
/**
Class information for a class.
*/
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties
公有方法有四個如下:類信息更新狀態(tài)存取方法,和兩個初始化器
- (void)setNeedUpdate;
- (BOOL)needUpdate;
+ (nullable instancetype)classInfoWithClass:(Class)cls;
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
NSObject+YYModel中定義了兩個內(nèi)部類_YYModelPropertyMeta、_YYModelMeta,分別表示模型中的屬性和類信息,YYClassPropertyInfo 和YYClassInfo可以理解為單純的數(shù)據(jù)結(jié)構(gòu),而_YYModelPropertyMeta、_YYModelMeta則是組合在一起的模型單元,分別扮演樹枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)角色
/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name
YYEncodingType _type; ///< property's type
YYEncodingNSType _nsType; ///< property's Foundation type
BOOL _isCNumber; ///< is c number type
Class _cls; ///< property's class, or nil
Class _genericCls; ///< container's generic class, or nil if threr's no generic class
SEL _getter; ///< getter, or nil if the instances cannot respond
SEL _setter; ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
/*
property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; ///< the key mapped to
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info; ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@end
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
@end
頭文件定義了一系列的公有方法,包含初始化器和工具函數(shù),這里不再詳述
核心流程
先調(diào)用_yy_dictionaryWithJSON把json轉(zhuǎn)成字典,然后調(diào)用modelWithDictionary生成model
+ (instancetype)modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self modelWithDictionary:dic];
}
下面這個函數(shù)用來構(gòu)造類信息和參數(shù)校驗
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
if ([one modelSetWithDictionary:dictionary]) return one;
return nil;
}
初始化模型上下文,遍歷所有屬性,進(jìn)行賦值
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
// 構(gòu)造_YYModelMeta 模型
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
// 字典中的每個鍵值對調(diào)用 ModelSetWithDictionaryFunction
// ModelSetWithDictionaryFunction 通過字典設(shè)置模型
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
// 每個映射 keyPath 的屬性元執(zhí)行 ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
// 每個映射多個 key 的屬性元執(zhí)行 ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
// 每個 modelMeta 屬性元執(zhí)行 ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
最終調(diào)用到ModelSetValueForProperty函數(shù),實現(xiàn)單個屬性的賦值,通過objc_msgSend函數(shù)直接調(diào)用屬性的setter方法進(jìn)行賦值