The previous introduction of YYModel how to dictionary model,
YYModel source code analysis: dictionary to model
An overview of the
This article continues to explore modelMeta, a description file that stores the attributes of the model and the corresponding relationship between the keys of the dictionary
Structure:
Four models are mainly used:
@interface YYClassPropertyInfo : NSObject
Attributes of the corresponding model
@interface YYClassInfo : NSObject
Classes corresponding to the model
Contains attribute dictionary, NSDictionary
@interface _YYModelPropertyMeta : NSObject
Properties of the corresponding model, and its processing
Contains a member variable YYClassPropertyInfo *_info,
The property YYClassPropertyInfo obtained by the model and its processing are encapsulated together
@interface _YYModelMeta : NSObject
Corresponding model classes, and processing
Contains a member variable YYClassInfo *_classInfo,
The class information YYClassInfo obtained by the model and its processing are encapsulated together
Model, why do you design it like that
Here are two examples
Example one,+ (NSDictionary *)modelCustomPropertyMapper
The model is as follows:
@interface Author : NSObject
@property NSString *name;
@property NSString *birthday;
@end
@implementation Author
@end
@interface Book : NSObject
@property NSString *name;
@property NSUInteger pages;
@property Author *reporter;
@end
@implementation Book
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"pages" : @"p",
@"reporter" : @[@"author",@"writer"]};
}
@end
Copy the code
Code implementation
_YYModelMeta: an implementation of NSObject
First tag
- for
[ @"pages" : @"p" ]
.
A layer of mapping is maintained through the following member variables
NSDictionary *_mapper;
Copy the code
- for
[ @"reporter" : @[@"author",@"writer"]
.
Records are held through the following member variables
NSArray *_multiKeysPropertyMetas;
Copy the code
The specific implementation
- (instancetype)initWithClass:(Class)cls { // ... NSMutableDictionary *mapper = [NSMutableDictionary new]; / /... if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) { NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper]; [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) { _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName]; if (!propertyMeta) return; // AllPropertyMetas delete @"pages" corresponding // allPropertyMetas delete @"reporter" corresponding [allPropertyMetas RemoveObjectForKey: propertyName]; the if ([mappedToKey isKindOfClass: [nsstrings class]]) {/ / deal with this, / / / @ "pages" : @"p" ] if (mappedToKey.length == 0) return; propertyMeta->_mappedToKey = mappedToKey; NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."]; for (NSString *onePath in keyPath) { if (onePath.length == 0) { NSMutableArray *tmp = keyPath.mutableCopy; [tmp removeObject:@""]; keyPath = tmp; break; } } if (keyPath.count > 1) { propertyMeta->_mappedToKeyPath = keyPath; [keyPathPropertyMetas addObject:propertyMeta]; } propertyMeta->_next = mapper[mappedToKey] ?: Nil; // modify // go here, AllPropertyMetas @"pages" for propertyMeta // becomes allPropertyMetas @"p" for propertyMeta mapper[mappedToKey] = propertyMeta; // if ([mappedToKey isKindOfClass:[NSString class]]) } else if ([mappedToKey isKindOfClass:[NSArray Class]]) {// [@"reporter" : @[@"author",@"writer"]] NSMutableArray *mappedToKeyArray = [NSMutableArray new]; for (NSString *oneKey in ((NSArray *)mappedToKey)) { if (![oneKey isKindOfClass:[NSString class]]) continue; if (oneKey.length == 0) continue; NSArray *keyPath = [oneKey componentsSeparatedByString:@"."]; if (keyPath.count > 1) { [mappedToKeyArray addObject:keyPath]; } Else {[mappedToKeyArray addObject:oneKey];} if (! PropertyMeta ->_mappedToKey) {// Change propertyMeta->_mappedToKey = only once oneKey; propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : Nil;}} if (! PropertyMeta ->_mappedToKey) return; @[@"author",@"writer"] propertyMeta->_mappedToKeyArray = mappedToKeyArray addObject:propertyMeta]; propertyMeta->_next = mapper[mappedToKey] ?: nil; mapper[mappedToKey] = propertyMeta; // if ([mappedToKey isKindOfClass:[NSArray class]]) } }]; / / enumerateKeysAndObjectsUsingBlock} / / if ([CLS respondsToSelector: @ the selector (modelCustomPropertyMapper)]) / / abnormal situation: [ @"pages" : [allPropertyMetas] [allPropertyMetas] [allPropertyMetas] [allPropertyMetas] [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) { propertyMeta->_mappedToKey = name; propertyMeta->_next = mapper[name] ?: nil; mapper[name] = propertyMeta; }]; / /... if (mapper.count) _mapper = mapper; // handle this, // [@"reporter" : @[@"author",@"writer"]] if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas; / /... }Copy the code
To deal with
In the dictionary transformation model,
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic { // ... if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) { // [ @"pages" : @ "p"], the following CFDictionaryApplyFunction (CFDictionaryRef dic, ModelSetWithDictionaryFunction, & context). / /... if (modelMeta->_multiKeysPropertyMetas) { // [@"reporter" : @ [@ "author," @ "writer"]]. CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context); }} / /... }Copy the code
- for
[ @"pages" : @"p" ]
/ / _key is "p" static void ModelSetWithDictionaryFunction (const void * _key, const void * _value, void *_context) { ModelSetContext *context = _context; __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); // propertyMeta->_name is "pages" // Through the maintenance of a layer of mapping _mapper, __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; __unsafe_unretained id model = (__bridge id)(context->model); While (propertyMeta) {if (propertyMeta->_setter) {// assign, ModelSetValueForProperty(model, (__bridge __unsafe_unretained ID)_value, propertyMeta); } propertyMeta = propertyMeta->_next; }; }Copy the code
- for
[ @"reporter" : @[@"author",@"writer"]
.
The static void ModelSetWithPropertyMetaArrayFunction (const void * _propertyMeta, void * _context) {/ / to get the background information, Context ModelSetContext *context = _context; __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary); __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta); if (! propertyMeta->_setter) return; id value = nil; // If (propertyMeta->_mappedToKeyArray) {value = YYValueForMultiKeys(Dictionary, propertyMeta->_mappedToKeyArray); } / /... __unsafe_unretained id model = (__bridge id)(context->model); // Assignment if (value) {__unsafe_unretained id model = (__bridge id)(context->model); ModelSetValueForProperty(model, value, propertyMeta); }}Copy the code
Multi-key value, get the value, and we’re done
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) { id value = nil; for (NSString *key in multiKeys) { if ([key isKindOfClass:[NSString class]]) { value = dic[key]; If (value) break; } / /... } return value; }Copy the code
Example 2,+ (NSDictionary *)modelContainerPropertyGenericClass
The model is as follows:
@interface Character : NSObject
@property NSString *name;
@property NSString *birthday;
@end
@implementation Character
@end
@interface BookTwo : NSObject
@property NSString *name;
@property NSUInteger pages;
@property NSArray *character;
@end
@implementation BookTwo
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"character" : [Character class] };
}
@end
Copy the code
Code implementation
First tag
- for
@{@"character" : [Character class] }
Copy the code
This time, _YYModelPropertyMeta: NSObject
The member variable below it records the container class information
Class _genericCls;
Copy the code
- The specific implementation
As above, back to the initialization method of _YYModelMeta,
NSDictionary *genericMapper = nil; if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) { genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass]; NSMutableDictionary * TMP = [NSMutableDictionary new]; NSMutableDictionary * TMP = [NSMutableDictionary new]; [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {if (![key isKindOfClass:[NSString class]]) return; Class meta = object_getClass(obj); if (! Meta) return; if (class_isMetaClass(meta)) {TMP [key] = obj; }]; genericMapper = tmp; } // Initialize all property information _YYModelPropertyMeta NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new]; YYClassInfo *curClassInfo = classInfo; While (curClassInfo && curclassinfo.supercls! = nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy) for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {// Filter out NSObject with custom attribute names if (! propertyInfo.name) continue; _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo propertyInfo:propertyInfo generic:genericMapper[propertyInfo.name]]; if (! meta || ! meta->_name) continue; if (! meta->_getter || ! meta->_setter) continue; if (allPropertyMetas[meta->_name]) continue; allPropertyMetas[meta->_name] = meta; } / / recursive, enters a layer curClassInfo = curClassInfo. SuperClassInfo; }Copy the code
Go to the initialization method of _YYModelPropertyMeta,
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic { // ... _YYModelPropertyMeta *meta = [self new]; / /... Meta ->_genericCls = generic; / /... }Copy the code
To deal with
In the dictionary transformation model,
Once again into a familiar formula,
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, Void *_context) {// get the information ModelSetContext *context = _context; __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; __unsafe_unretained id model = (__bridge id)(context->model); / /... // The assignment method ModelSetValueForProperty(model, (__bridge __unsafe_unretained ID)_value, propertyMeta); / /... }Copy the code
Assignment methods handle data parsing of container classes
static void ModelSetValueForProperty(__unsafe_unretained id model, __unsafe_unretained id value, __unsafe_unretained _YYModelPropertyMeta *meta) { // ... switch (meta->_nsType) { // ... case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {if (meta->_genericCls) {// Enter the container class NSArray *valueArr = nil; if ([value isKindOfClass:[NSArray class]]) valueArr = value; else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects; if (valueArr) { NSMutableArray *objectArr = [NSMutableArray new]; For (id one in valueArr) {if ([one isKindOfClass:meta->_genericCls]) { Add [objectArr addObject:one] directly; } else if ([one isKindOfClass:[NSDictionary class]]) {CLS = meta->_genericCls; if (meta->_hasCustomClassFromDictionary) { cls = [cls modelCustomClassForDictionary:one]; if (! cls) cls = meta->_genericCls; // for xcode code coverage } NSObject *newOne = [cls new]; / / into the familiar parsing / / reference above [newOne yy_modelSetWithDictionary: one]; if (newOne) [objectArr addObject:newOne]; } } ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr); } else {// In this case, If ([value isKindOfClass:[NSArray class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value); } else { ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSArray *)value).mutableCopy); }} / /... } } break; / /... }}Copy the code