123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839 |
- //
- // NSObject+YYModel.m
- // YYModel <https://github.com/ibireme/YYModel>
- //
- // Created by ibireme on 15/5/10.
- // Copyright (c) 2015 ibireme.
- //
- // This source code is licensed under the MIT-style license found in the
- // LICENSE file in the root directory of this source tree.
- //
- #import "NSObject+YYModel.h"
- #import "YYClassInfo.h"
- #import <objc/message.h>
- #define force_inline __inline__ __attribute__((always_inline))
- /// Foundation Class Type
- typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
- YYEncodingTypeNSUnknown = 0,
- YYEncodingTypeNSString,
- YYEncodingTypeNSMutableString,
- YYEncodingTypeNSValue,
- YYEncodingTypeNSNumber,
- YYEncodingTypeNSDecimalNumber,
- YYEncodingTypeNSData,
- YYEncodingTypeNSMutableData,
- YYEncodingTypeNSDate,
- YYEncodingTypeNSURL,
- YYEncodingTypeNSArray,
- YYEncodingTypeNSMutableArray,
- YYEncodingTypeNSDictionary,
- YYEncodingTypeNSMutableDictionary,
- YYEncodingTypeNSSet,
- YYEncodingTypeNSMutableSet,
- };
- /// Get the Foundation class type from property info.
- static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
- if (!cls) return YYEncodingTypeNSUnknown;
- if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
- if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
- if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
- if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
- if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
- if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
- if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
- if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
- if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
- if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
- if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
- if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
- if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
- if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
- if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
- return YYEncodingTypeNSUnknown;
- }
- /// Whether the type is c number.
- static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
- switch (type & YYEncodingTypeMask) {
- case YYEncodingTypeBool:
- case YYEncodingTypeInt8:
- case YYEncodingTypeUInt8:
- case YYEncodingTypeInt16:
- case YYEncodingTypeUInt16:
- case YYEncodingTypeInt32:
- case YYEncodingTypeUInt32:
- case YYEncodingTypeInt64:
- case YYEncodingTypeUInt64:
- case YYEncodingTypeFloat:
- case YYEncodingTypeDouble:
- case YYEncodingTypeLongDouble: return YES;
- default: return NO;
- }
- }
- /// Parse a number value from 'id'.
- static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
- static NSCharacterSet *dot;
- static NSDictionary *dic;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
- dic = @{@"TRUE" : @(YES),
- @"True" : @(YES),
- @"true" : @(YES),
- @"FALSE" : @(NO),
- @"False" : @(NO),
- @"false" : @(NO),
- @"YES" : @(YES),
- @"Yes" : @(YES),
- @"yes" : @(YES),
- @"NO" : @(NO),
- @"No" : @(NO),
- @"no" : @(NO),
- @"NIL" : (id)kCFNull,
- @"Nil" : (id)kCFNull,
- @"nil" : (id)kCFNull,
- @"NULL" : (id)kCFNull,
- @"Null" : (id)kCFNull,
- @"null" : (id)kCFNull,
- @"(NULL)" : (id)kCFNull,
- @"(Null)" : (id)kCFNull,
- @"(null)" : (id)kCFNull,
- @"<NULL>" : (id)kCFNull,
- @"<Null>" : (id)kCFNull,
- @"<null>" : (id)kCFNull};
- });
-
- if (!value || value == (id)kCFNull) return nil;
- if ([value isKindOfClass:[NSNumber class]]) return value;
- if ([value isKindOfClass:[NSString class]]) {
- NSNumber *num = dic[value];
- if (num) {
- if (num == (id)kCFNull) return nil;
- return num;
- }
- if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
- const char *cstring = ((NSString *)value).UTF8String;
- if (!cstring) return nil;
- double num = atof(cstring);
- if (isnan(num) || isinf(num)) return nil;
- return @(num);
- } else {
- const char *cstring = ((NSString *)value).UTF8String;
- if (!cstring) return nil;
- return @(atoll(cstring));
- }
- }
- return nil;
- }
- /// Parse string to date.
- static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
- typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
- #define kParserNum 34
- static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- {
- /*
- 2014-01-20 // Google
- */
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
- formatter.dateFormat = @"yyyy-MM-dd";
- blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
- }
-
- {
- /*
- 2014-01-20 12:24:48
- 2014-01-20T12:24:48 // Google
- 2014-01-20 12:24:48.000
- 2014-01-20T12:24:48.000
- */
- NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
- formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
- formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
-
- NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
- formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
- formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";
- NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
- formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
- formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";
- NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
- formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
- formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
-
- blocks[19] = ^(NSString *string) {
- if ([string characterAtIndex:10] == 'T') {
- return [formatter1 dateFromString:string];
- } else {
- return [formatter2 dateFromString:string];
- }
- };
- blocks[23] = ^(NSString *string) {
- if ([string characterAtIndex:10] == 'T') {
- return [formatter3 dateFromString:string];
- } else {
- return [formatter4 dateFromString:string];
- }
- };
- }
-
- {
- /*
- 2014-01-20T12:24:48Z // Github, Apple
- 2014-01-20T12:24:48+0800 // Facebook
- 2014-01-20T12:24:48+12:00 // Google
- 2014-01-20T12:24:48.000Z
- 2014-01-20T12:24:48.000+0800
- 2014-01-20T12:24:48.000+12:00
- */
- NSDateFormatter *formatter = [NSDateFormatter new];
- formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
- NSDateFormatter *formatter2 = [NSDateFormatter new];
- formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
- blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
- blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
- blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
- blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
- blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
- }
-
- {
- /*
- Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
- Fri Sep 04 00:12:21.000 +0800 2015
- */
- NSDateFormatter *formatter = [NSDateFormatter new];
- formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
- NSDateFormatter *formatter2 = [NSDateFormatter new];
- formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
- blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
- blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
- }
- });
- if (!string) return nil;
- if (string.length > kParserNum) return nil;
- YYNSDateParseBlock parser = blocks[string.length];
- if (!parser) return nil;
- return parser(string);
- #undef kParserNum
- }
- /// Get the 'NSBlock' class.
- static force_inline Class YYNSBlockClass() {
- static Class cls;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- void (^block)(void) = ^{};
- cls = ((NSObject *)block).class;
- while (class_getSuperclass(cls) != [NSObject class]) {
- cls = class_getSuperclass(cls);
- }
- });
- return cls; // current is "NSBlock"
- }
- /**
- Get the ISO date formatter.
-
- ISO8601 format example:
- 2010-07-09T16:13:30+12:00
- 2011-01-11T11:11:11+0000
- 2011-01-26T19:06:43Z
-
- length: 20/24/25
- */
- static force_inline NSDateFormatter *YYISODateFormatter() {
- static NSDateFormatter *formatter = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- formatter = [[NSDateFormatter alloc] init];
- formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
- formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
- });
- return formatter;
- }
- /// Get the value with key paths from dictionary
- /// The dic should be NSDictionary, and the keyPath should not be nil.
- static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
- id value = nil;
- for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {
- value = dic[keyPaths[i]];
- if (i + 1 < max) {
- if ([value isKindOfClass:[NSDictionary class]]) {
- dic = value;
- } else {
- return nil;
- }
- }
- }
- return value;
- }
- /// Get the value with multi key (or key path) from dictionary
- /// The dic should be NSDictionary
- 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;
- } else {
- value = YYValueForKeyPath(dic, (NSArray *)key);
- if (value) break;
- }
- }
- return value;
- }
- /// 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
- @implementation _YYModelPropertyMeta
- + (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
-
- // support pseudo generic class with protocol name
- if (!generic && propertyInfo.protocols) {
- for (NSString *protocol in propertyInfo.protocols) {
- Class cls = objc_getClass(protocol.UTF8String);
- if (cls) {
- generic = cls;
- break;
- }
- }
- }
-
- _YYModelPropertyMeta *meta = [self new];
- meta->_name = propertyInfo.name;
- meta->_type = propertyInfo.type;
- meta->_info = propertyInfo;
- meta->_genericCls = generic;
-
- if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
- meta->_nsType = YYClassGetNSType(propertyInfo.cls);
- } else {
- meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
- }
- if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
- /*
- It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
- */
- static NSSet *types = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSMutableSet *set = [NSMutableSet new];
- // 32 bit
- [set addObject:@"{CGSize=ff}"];
- [set addObject:@"{CGPoint=ff}"];
- [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
- [set addObject:@"{CGAffineTransform=ffffff}"];
- [set addObject:@"{UIEdgeInsets=ffff}"];
- [set addObject:@"{UIOffset=ff}"];
- // 64 bit
- [set addObject:@"{CGSize=dd}"];
- [set addObject:@"{CGPoint=dd}"];
- [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
- [set addObject:@"{CGAffineTransform=dddddd}"];
- [set addObject:@"{UIEdgeInsets=dddd}"];
- [set addObject:@"{UIOffset=dd}"];
- types = set;
- });
- if ([types containsObject:propertyInfo.typeEncoding]) {
- meta->_isStructAvailableForKeyedArchiver = YES;
- }
- }
- meta->_cls = propertyInfo.cls;
-
- if (generic) {
- meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
- } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
- meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
- }
-
- if (propertyInfo.getter) {
- if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
- meta->_getter = propertyInfo.getter;
- }
- }
- if (propertyInfo.setter) {
- if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
- meta->_setter = propertyInfo.setter;
- }
- }
-
- if (meta->_getter && meta->_setter) {
- /*
- KVC invalid type:
- long double
- pointer (such as SEL/CoreFoundation object)
- */
- switch (meta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeBool:
- case YYEncodingTypeInt8:
- case YYEncodingTypeUInt8:
- case YYEncodingTypeInt16:
- case YYEncodingTypeUInt16:
- case YYEncodingTypeInt32:
- case YYEncodingTypeUInt32:
- case YYEncodingTypeInt64:
- case YYEncodingTypeUInt64:
- case YYEncodingTypeFloat:
- case YYEncodingTypeDouble:
- case YYEncodingTypeObject:
- case YYEncodingTypeClass:
- case YYEncodingTypeBlock:
- case YYEncodingTypeStruct:
- case YYEncodingTypeUnion: {
- meta->_isKVCCompatible = YES;
- } break;
- default: break;
- }
- }
-
- return meta;
- }
- @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
- @implementation _YYModelMeta
- - (instancetype)initWithClass:(Class)cls {
- YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
- if (!classInfo) return nil;
- self = [super init];
-
- // Get black list
- NSSet *blacklist = nil;
- if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
- NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
- if (properties) {
- blacklist = [NSSet setWithArray:properties];
- }
- }
-
- // Get white list
- NSSet *whitelist = nil;
- if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
- NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
- if (properties) {
- whitelist = [NSSet setWithArray:properties];
- }
- }
-
- // Get container property's generic class
- NSDictionary *genericMapper = nil;
- if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
- genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
- if (genericMapper) {
- 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;
- } else if ([obj isKindOfClass:[NSString class]]) {
- Class cls = NSClassFromString(obj);
- if (cls) {
- tmp[key] = cls;
- }
- }
- }];
- genericMapper = tmp;
- }
- }
-
- // Create all property metas.
- 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) {
- if (!propertyInfo.name) continue;
- if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
- if (whitelist && ![whitelist containsObject: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;
- }
- curClassInfo = curClassInfo.superClassInfo;
- }
- if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
-
- // create mapper
- NSMutableDictionary *mapper = [NSMutableDictionary new];
- NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
- NSMutableArray *multiKeysPropertyMetas = [NSMutableArray 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 removeObjectForKey:propertyName];
-
- if ([mappedToKey isKindOfClass:[NSString class]]) {
- 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;
- mapper[mappedToKey] = propertyMeta;
-
- } else if ([mappedToKey isKindOfClass:[NSArray class]]) {
-
- 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) {
- propertyMeta->_mappedToKey = oneKey;
- propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
- }
- }
- if (!propertyMeta->_mappedToKey) return;
-
- propertyMeta->_mappedToKeyArray = mappedToKeyArray;
- [multiKeysPropertyMetas addObject:propertyMeta];
-
- propertyMeta->_next = mapper[mappedToKey] ?: nil;
- mapper[mappedToKey] = propertyMeta;
- }
- }];
- }
-
- [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
- propertyMeta->_mappedToKey = name;
- propertyMeta->_next = mapper[name] ?: nil;
- mapper[name] = propertyMeta;
- }];
-
- if (mapper.count) _mapper = mapper;
- if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
- if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
-
- _classInfo = classInfo;
- _keyMappedCount = _allPropertyMetas.count;
- _nsType = YYClassGetNSType(cls);
- _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
- _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
- _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
- _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
-
- return self;
- }
- /// Returns the cached model class meta
- + (instancetype)metaWithClass:(Class)cls {
- if (!cls) return nil;
- static CFMutableDictionaryRef cache;
- static dispatch_once_t onceToken;
- static dispatch_semaphore_t lock;
- dispatch_once(&onceToken, ^{
- cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- lock = dispatch_semaphore_create(1);
- });
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
- dispatch_semaphore_signal(lock);
- if (!meta || meta->_classInfo.needUpdate) {
- meta = [[_YYModelMeta alloc] initWithClass:cls];
- if (meta) {
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
- dispatch_semaphore_signal(lock);
- }
- }
- return meta;
- }
- @end
- /**
- Get number from property.
- @discussion Caller should hold strong reference to the parameters before this function returns.
- @param model Should not be nil.
- @param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil.
- @return A number object, or nil if failed.
- */
- static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,
- __unsafe_unretained _YYModelPropertyMeta *meta) {
- switch (meta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeBool: {
- return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeInt8: {
- return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeUInt8: {
- return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeInt16: {
- return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeUInt16: {
- return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeInt32: {
- return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeUInt32: {
- return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeInt64: {
- return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeUInt64: {
- return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
- }
- case YYEncodingTypeFloat: {
- float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
- if (isnan(num) || isinf(num)) return nil;
- return @(num);
- }
- case YYEncodingTypeDouble: {
- double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
- if (isnan(num) || isinf(num)) return nil;
- return @(num);
- }
- case YYEncodingTypeLongDouble: {
- double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
- if (isnan(num) || isinf(num)) return nil;
- return @(num);
- }
- default: return nil;
- }
- }
- /**
- Set number to property.
- @discussion Caller should hold strong reference to the parameters before this function returns.
- @param model Should not be nil.
- @param num Can be nil.
- @param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil.
- */
- static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
- __unsafe_unretained NSNumber *num,
- __unsafe_unretained _YYModelPropertyMeta *meta) {
- switch (meta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeBool: {
- ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
- } break;
- case YYEncodingTypeInt8: {
- ((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
- } break;
- case YYEncodingTypeUInt8: {
- ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);
- } break;
- case YYEncodingTypeInt16: {
- ((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);
- } break;
- case YYEncodingTypeUInt16: {
- ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);
- } break;
- case YYEncodingTypeInt32: {
- ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);
- }
- case YYEncodingTypeUInt32: {
- ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);
- } break;
- case YYEncodingTypeInt64: {
- if ([num isKindOfClass:[NSDecimalNumber class]]) {
- ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
- } else {
- ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);
- }
- } break;
- case YYEncodingTypeUInt64: {
- if ([num isKindOfClass:[NSDecimalNumber class]]) {
- ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
- } else {
- ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);
- }
- } break;
- case YYEncodingTypeFloat: {
- float f = num.floatValue;
- if (isnan(f) || isinf(f)) f = 0;
- ((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);
- } break;
- case YYEncodingTypeDouble: {
- double d = num.doubleValue;
- if (isnan(d) || isinf(d)) d = 0;
- ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);
- } break;
- case YYEncodingTypeLongDouble: {
- long double d = num.doubleValue;
- if (isnan(d) || isinf(d)) d = 0;
- ((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);
- } // break; commented for code coverage in next line
- default: break;
- }
- }
- /**
- Set value to model with a property meta.
-
- @discussion Caller should hold strong reference to the parameters before this function returns.
-
- @param model Should not be nil.
- @param value Should not be nil, but can be NSNull.
- @param meta Should not be nil, and meta->_setter should not be nil.
- */
- static void ModelSetValueForProperty(__unsafe_unretained id model,
- __unsafe_unretained id value,
- __unsafe_unretained _YYModelPropertyMeta *meta) {
- if (meta->_isCNumber) {
- NSNumber *num = YYNSNumberCreateFromID(value);
- ModelSetNumberToProperty(model, num, meta);
- if (num) [num class]; // hold the number
- } else if (meta->_nsType) {
- if (value == (id)kCFNull) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
- } else {
- switch (meta->_nsType) {
- case YYEncodingTypeNSString:
- case YYEncodingTypeNSMutableString: {
- if ([value isKindOfClass:[NSString class]]) {
- if (meta->_nsType == YYEncodingTypeNSString) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
- }
- } else if ([value isKindOfClass:[NSNumber class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- (meta->_nsType == YYEncodingTypeNSString) ?
- ((NSNumber *)value).stringValue :
- ((NSNumber *)value).stringValue.mutableCopy);
- } else if ([value isKindOfClass:[NSData class]]) {
- NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);
- } else if ([value isKindOfClass:[NSURL class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- (meta->_nsType == YYEncodingTypeNSString) ?
- ((NSURL *)value).absoluteString :
- ((NSURL *)value).absoluteString.mutableCopy);
- } else if ([value isKindOfClass:[NSAttributedString class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- (meta->_nsType == YYEncodingTypeNSString) ?
- ((NSAttributedString *)value).string :
- ((NSAttributedString *)value).string.mutableCopy);
- }
- } break;
-
- case YYEncodingTypeNSValue:
- case YYEncodingTypeNSNumber:
- case YYEncodingTypeNSDecimalNumber: {
- if (meta->_nsType == YYEncodingTypeNSNumber) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));
- } else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {
- if ([value isKindOfClass:[NSDecimalNumber class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else if ([value isKindOfClass:[NSNumber class]]) {
- NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
- } else if ([value isKindOfClass:[NSString class]]) {
- NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];
- NSDecimal dec = decNum.decimalValue;
- if (dec._length == 0 && dec._isNegative) {
- decNum = nil; // NaN
- }
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
- }
- } else { // YYEncodingTypeNSValue
- if ([value isKindOfClass:[NSValue class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- }
- }
- } break;
-
- case YYEncodingTypeNSData:
- case YYEncodingTypeNSMutableData: {
- if ([value isKindOfClass:[NSData class]]) {
- if (meta->_nsType == YYEncodingTypeNSData) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else {
- NSMutableData *data = ((NSData *)value).mutableCopy;
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
- }
- } else if ([value isKindOfClass:[NSString class]]) {
- NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
- if (meta->_nsType == YYEncodingTypeNSMutableData) {
- data = ((NSData *)data).mutableCopy;
- }
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
- }
- } break;
-
- case YYEncodingTypeNSDate: {
- if ([value isKindOfClass:[NSDate class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else if ([value isKindOfClass:[NSString class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));
- }
- } break;
-
- case YYEncodingTypeNSURL: {
- if ([value isKindOfClass:[NSURL class]]) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else if ([value isKindOfClass:[NSString class]]) {
- NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
- NSString *str = [value stringByTrimmingCharactersInSet:set];
- if (str.length == 0) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);
- } else {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);
- }
- }
- } break;
-
- case YYEncodingTypeNSArray:
- case YYEncodingTypeNSMutableArray: {
- if (meta->_genericCls) {
- 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]) {
- [objectArr addObject:one];
- } else if ([one isKindOfClass:[NSDictionary class]]) {
- Class cls = meta->_genericCls;
- if (meta->_hasCustomClassFromDictionary) {
- cls = [cls modelCustomClassForDictionary:one];
- if (!cls) cls = meta->_genericCls; // for xcode code coverage
- }
- NSObject *newOne = [cls new];
- [newOne yy_modelSetWithDictionary:one];
- if (newOne) [objectArr addObject:newOne];
- }
- }
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
- }
- } else {
- 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);
- }
- } else if ([value isKindOfClass:[NSSet class]]) {
- if (meta->_nsType == YYEncodingTypeNSArray) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);
- } else {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- ((NSSet *)value).allObjects.mutableCopy);
- }
- }
- }
- } break;
-
- case YYEncodingTypeNSDictionary:
- case YYEncodingTypeNSMutableDictionary: {
- if ([value isKindOfClass:[NSDictionary class]]) {
- if (meta->_genericCls) {
- NSMutableDictionary *dic = [NSMutableDictionary new];
- [((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {
- if ([oneValue isKindOfClass:[NSDictionary class]]) {
- Class cls = meta->_genericCls;
- if (meta->_hasCustomClassFromDictionary) {
- cls = [cls modelCustomClassForDictionary:oneValue];
- if (!cls) cls = meta->_genericCls; // for xcode code coverage
- }
- NSObject *newOne = [cls new];
- [newOne yy_modelSetWithDictionary:(id)oneValue];
- if (newOne) dic[oneKey] = newOne;
- }
- }];
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);
- } else {
- if (meta->_nsType == YYEncodingTypeNSDictionary) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
- } else {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- ((NSDictionary *)value).mutableCopy);
- }
- }
- }
- } break;
-
- case YYEncodingTypeNSSet:
- case YYEncodingTypeNSMutableSet: {
- NSSet *valueSet = nil;
- if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
- else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
-
- if (meta->_genericCls) {
- NSMutableSet *set = [NSMutableSet new];
- for (id one in valueSet) {
- if ([one isKindOfClass:meta->_genericCls]) {
- [set addObject:one];
- } else if ([one isKindOfClass:[NSDictionary class]]) {
- Class cls = meta->_genericCls;
- if (meta->_hasCustomClassFromDictionary) {
- cls = [cls modelCustomClassForDictionary:one];
- if (!cls) cls = meta->_genericCls; // for xcode code coverage
- }
- NSObject *newOne = [cls new];
- [newOne yy_modelSetWithDictionary:one];
- if (newOne) [set addObject:newOne];
- }
- }
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);
- } else {
- if (meta->_nsType == YYEncodingTypeNSSet) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);
- } else {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
- meta->_setter,
- ((NSSet *)valueSet).mutableCopy);
- }
- }
- } // break; commented for code coverage in next line
-
- default: break;
- }
- }
- } else {
- BOOL isNull = (value == (id)kCFNull);
- switch (meta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeObject: {
- Class cls = meta->_genericCls ?: meta->_cls;
- if (isNull) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
- } else if ([value isKindOfClass:cls] || !cls) {
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);
- } else if ([value isKindOfClass:[NSDictionary class]]) {
- NSObject *one = nil;
- if (meta->_getter) {
- one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
- }
- if (one) {
- [one yy_modelSetWithDictionary:value];
- } else {
- if (meta->_hasCustomClassFromDictionary) {
- cls = [cls modelCustomClassForDictionary:value] ?: cls;
- }
- one = [cls new];
- [one yy_modelSetWithDictionary:value];
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
- }
- }
- } break;
-
- case YYEncodingTypeClass: {
- if (isNull) {
- ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);
- } else {
- Class cls = nil;
- if ([value isKindOfClass:[NSString class]]) {
- cls = NSClassFromString(value);
- if (cls) {
- ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);
- }
- } else {
- cls = object_getClass(value);
- if (cls) {
- if (class_isMetaClass(cls)) {
- ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);
- }
- }
- }
- }
- } break;
-
- case YYEncodingTypeSEL: {
- if (isNull) {
- ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);
- } else if ([value isKindOfClass:[NSString class]]) {
- SEL sel = NSSelectorFromString(value);
- if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);
- }
- } break;
-
- case YYEncodingTypeBlock: {
- if (isNull) {
- ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);
- } else if ([value isKindOfClass:YYNSBlockClass()]) {
- ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);
- }
- } break;
-
- case YYEncodingTypeStruct:
- case YYEncodingTypeUnion:
- case YYEncodingTypeCArray: {
- if ([value isKindOfClass:[NSValue class]]) {
- const char *valueType = ((NSValue *)value).objCType;
- const char *metaType = meta->_info.typeEncoding.UTF8String;
- if (valueType && metaType && strcmp(valueType, metaType) == 0) {
- [model setValue:value forKey:meta->_name];
- }
- }
- } break;
-
- case YYEncodingTypePointer:
- case YYEncodingTypeCString: {
- if (isNull) {
- ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);
- } else if ([value isKindOfClass:[NSValue class]]) {
- NSValue *nsValue = value;
- if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
- ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);
- }
- }
- } // break; commented for code coverage in next line
-
- default: break;
- }
- }
- }
- typedef struct {
- void *modelMeta; ///< _YYModelMeta
- void *model; ///< id (self)
- void *dictionary; ///< NSDictionary (json)
- } ModelSetContext;
- /**
- Apply function for dictionary, to set the key-value pair to model.
-
- @param _key should not be nil, NSString.
- @param _value should not be nil.
- @param _context _context.modelMeta and _context.model should not be nil.
- */
- static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
- 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);
- while (propertyMeta) {
- if (propertyMeta->_setter) {
- ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
- }
- propertyMeta = propertyMeta->_next;
- };
- }
- /**
- Apply function for model property meta, to set dictionary to model.
-
- @param _propertyMeta should not be nil, _YYModelPropertyMeta.
- @param _context _context.model and _context.dictionary should not be nil.
- */
- static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_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);
- } else if (propertyMeta->_mappedToKeyPath) {
- value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
- } else {
- value = [dictionary objectForKey:propertyMeta->_mappedToKey];
- }
-
- if (value) {
- __unsafe_unretained id model = (__bridge id)(context->model);
- ModelSetValueForProperty(model, value, propertyMeta);
- }
- }
- /**
- Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
- or nil if an error occurs.
-
- @param model Model, can be nil.
- @return JSON object, nil if an error occurs.
- */
- 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;
- if ([model isKindOfClass:[NSDictionary class]]) {
- if ([NSJSONSerialization isValidJSONObject:model]) return model;
- NSMutableDictionary *newDic = [NSMutableDictionary new];
- [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
- NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
- if (!stringKey) return;
- id jsonObj = ModelToJSONObjectRecursive(obj);
- if (!jsonObj) jsonObj = (id)kCFNull;
- newDic[stringKey] = jsonObj;
- }];
- return newDic;
- }
- if ([model isKindOfClass:[NSSet class]]) {
- NSArray *array = ((NSSet *)model).allObjects;
- if ([NSJSONSerialization isValidJSONObject:array]) return array;
- NSMutableArray *newArray = [NSMutableArray new];
- for (id obj in array) {
- if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
- [newArray addObject:obj];
- } else {
- id jsonObj = ModelToJSONObjectRecursive(obj);
- if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
- }
- }
- return newArray;
- }
- if ([model isKindOfClass:[NSArray class]]) {
- if ([NSJSONSerialization isValidJSONObject:model]) return model;
- NSMutableArray *newArray = [NSMutableArray new];
- for (id obj in (NSArray *)model) {
- if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
- [newArray addObject:obj];
- } else {
- id jsonObj = ModelToJSONObjectRecursive(obj);
- if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
- }
- }
- return newArray;
- }
- if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
- if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
- if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
- if ([model isKindOfClass:[NSData class]]) return nil;
-
-
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
- if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
- NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
- __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->_nsType) {
- id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
- value = ModelToJSONObjectRecursive(v);
- } else {
- switch (propertyMeta->_type & 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 (propertyMeta->_mappedToKeyPath) {
- NSMutableDictionary *superDic = dic;
- NSMutableDictionary *subDic = nil;
- for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
- NSString *key = propertyMeta->_mappedToKeyPath[i];
- if (i + 1 == max) { // end
- if (!superDic[key]) superDic[key] = value;
- break;
- }
-
- subDic = superDic[key];
- if (subDic) {
- if ([subDic isKindOfClass:[NSDictionary class]]) {
- subDic = subDic.mutableCopy;
- superDic[key] = subDic;
- } else {
- break;
- }
- } else {
- subDic = [NSMutableDictionary new];
- superDic[key] = subDic;
- }
- superDic = subDic;
- subDic = nil;
- }
- } else {
- if (!dic[propertyMeta->_mappedToKey]) {
- dic[propertyMeta->_mappedToKey] = value;
- }
- }
- }];
-
- if (modelMeta->_hasCustomTransformToDictionary) {
- BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
- if (!suc) return nil;
- }
- return result;
- }
- /// Add indent to string (exclude first line)
- static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {
- for (NSUInteger i = 0, max = desc.length; i < max; i++) {
- unichar c = [desc characterAtIndex:i];
- if (c == '\n') {
- for (NSUInteger j = 0; j < indent; j++) {
- [desc insertString:@" " atIndex:i + 1];
- }
- i += indent * 4;
- max += indent * 4;
- }
- }
- return desc;
- }
- /// Generaate a description string
- static NSString *ModelDescription(NSObject *model) {
- static const int kDescMaxLength = 100;
- if (!model) return @"<nil>";
- if (model == (id)kCFNull) return @"<null>";
- if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
-
-
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class];
- switch (modelMeta->_nsType) {
- case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
- return [NSString stringWithFormat:@"\"%@\"",model];
- }
-
- case YYEncodingTypeNSValue:
- case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
- NSString *tmp = model.description;
- if (tmp.length > kDescMaxLength) {
- tmp = [tmp substringToIndex:kDescMaxLength];
- tmp = [tmp stringByAppendingString:@"..."];
- }
- return tmp;
- }
-
- case YYEncodingTypeNSNumber:
- case YYEncodingTypeNSDecimalNumber:
- case YYEncodingTypeNSDate:
- case YYEncodingTypeNSURL: {
- return [NSString stringWithFormat:@"%@",model];
- }
-
- case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: {
- model = ((NSSet *)model).allObjects;
- } // no break
-
- case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {
- NSArray *array = (id)model;
- NSMutableString *desc = [NSMutableString new];
- if (array.count == 0) {
- return [desc stringByAppendingString:@"[]"];
- } else {
- [desc appendFormat:@"[\n"];
- for (NSUInteger i = 0, max = array.count; i < max; i++) {
- NSObject *obj = array[i];
- [desc appendString:@" "];
- [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
- [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
- }
- [desc appendString:@"]"];
- return desc;
- }
- }
- case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: {
- NSDictionary *dic = (id)model;
- NSMutableString *desc = [NSMutableString new];
- if (dic.count == 0) {
- return [desc stringByAppendingString:@"{}"];
- } else {
- NSArray *keys = dic.allKeys;
-
- [desc appendFormat:@"{\n"];
- for (NSUInteger i = 0, max = keys.count; i < max; i++) {
- NSString *key = keys[i];
- NSObject *value = dic[key];
- [desc appendString:@" "];
- [desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
- [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
- }
- [desc appendString:@"}"];
- }
- return desc;
- }
-
- default: {
- NSMutableString *desc = [NSMutableString new];
- [desc appendFormat:@"<%@: %p>", model.class, model];
- if (modelMeta->_allPropertyMetas.count == 0) return desc;
-
- // sort property names
- NSArray *properties = [modelMeta->_allPropertyMetas
- sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) {
- return [p1->_name compare:p2->_name];
- }];
-
- [desc appendFormat:@" {\n"];
- for (NSUInteger i = 0, max = properties.count; i < max; i++) {
- _YYModelPropertyMeta *property = properties[i];
- NSString *propertyDesc;
- if (property->_isCNumber) {
- NSNumber *num = ModelCreateNumberFromProperty(model, property);
- propertyDesc = num.stringValue;
- } else {
- switch (property->_type & YYEncodingTypeMask) {
- case YYEncodingTypeObject: {
- id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
- propertyDesc = ModelDescription(v);
- if (!propertyDesc) propertyDesc = @"<nil>";
- } break;
- case YYEncodingTypeClass: {
- id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
- propertyDesc = ((NSObject *)v).description;
- if (!propertyDesc) propertyDesc = @"<nil>";
- } break;
- case YYEncodingTypeSEL: {
- SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
- if (sel) propertyDesc = NSStringFromSelector(sel);
- else propertyDesc = @"<NULL>";
- } break;
- case YYEncodingTypeBlock: {
- id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
- propertyDesc = block ? ((NSObject *)block).description : @"<nil>";
- } break;
- case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: {
- void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
- propertyDesc = [NSString stringWithFormat:@"%p",pointer];
- } break;
- case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
- NSValue *value = [model valueForKey:property->_name];
- propertyDesc = value ? value.description : @"{unknown}";
- } break;
- default: propertyDesc = @"<unknown>";
- }
- }
-
- propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
- [desc appendFormat:@" %@ = %@",property->_name, propertyDesc];
- [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
- }
- [desc appendFormat:@"}"];
- return desc;
- }
- }
- }
- @implementation NSObject (YYModel)
- + (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
- if (!json || json == (id)kCFNull) return nil;
- NSDictionary *dic = nil;
- NSData *jsonData = nil;
- if ([json isKindOfClass:[NSDictionary class]]) {
- dic = json;
- } else if ([json isKindOfClass:[NSString class]]) {
- jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
- } else if ([json isKindOfClass:[NSData class]]) {
- jsonData = json;
- }
- if (jsonData) {
- dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
- if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
- }
- return dic;
- }
- + (instancetype)yy_modelWithJSON:(id)json {
- NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
- return [self yy_modelWithDictionary:dic];
- }
- + (instancetype)yy_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 yy_modelSetWithDictionary:dictionary]) return one;
- return nil;
- }
- - (BOOL)yy_modelSetWithJSON:(id)json {
- NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json];
- return [self yy_modelSetWithDictionary:dic];
- }
- - (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
- if (!dic || dic == (id)kCFNull) return NO;
- if (![dic isKindOfClass:[NSDictionary class]]) return NO;
-
- _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)) {
- CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
- if (modelMeta->_keyPathPropertyMetas) {
- CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
- CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
- ModelSetWithPropertyMetaArrayFunction,
- &context);
- }
- if (modelMeta->_multiKeysPropertyMetas) {
- CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
- CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
- ModelSetWithPropertyMetaArrayFunction,
- &context);
- }
- } else {
- CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
- CFRangeMake(0, modelMeta->_keyMappedCount),
- ModelSetWithPropertyMetaArrayFunction,
- &context);
- }
-
- if (modelMeta->_hasCustomTransformFromDictionary) {
- return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
- }
- return YES;
- }
- - (id)yy_modelToJSONObject {
- /*
- Apple said:
- The top level object is an NSArray or NSDictionary.
- All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
- All dictionary keys are instances of NSString.
- Numbers are not NaN or infinity.
- */
- id jsonObject = ModelToJSONObjectRecursive(self);
- if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
- if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
- return nil;
- }
- - (NSData *)yy_modelToJSONData {
- id jsonObject = [self yy_modelToJSONObject];
- if (!jsonObject) return nil;
- return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
- }
- - (NSString *)yy_modelToJSONString {
- NSData *jsonData = [self yy_modelToJSONData];
- if (jsonData.length == 0) return nil;
- return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- }
- - (id)yy_modelCopy{
- if (self == (id)kCFNull) return self;
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
- if (modelMeta->_nsType) return [self copy];
-
- NSObject *one = [self.class new];
- for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
- if (!propertyMeta->_getter || !propertyMeta->_setter) continue;
-
- if (propertyMeta->_isCNumber) {
- switch (propertyMeta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeBool: {
- bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeInt8:
- case YYEncodingTypeUInt8: {
- uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeInt16:
- case YYEncodingTypeUInt16: {
- uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeInt32:
- case YYEncodingTypeUInt32: {
- uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeInt64:
- case YYEncodingTypeUInt64: {
- uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeFloat: {
- float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeDouble: {
- double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } break;
- case YYEncodingTypeLongDouble: {
- long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
- } // break; commented for code coverage in next line
- default: break;
- }
- } else {
- switch (propertyMeta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeObject:
- case YYEncodingTypeClass:
- case YYEncodingTypeBlock: {
- id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
- } break;
- case YYEncodingTypeSEL:
- case YYEncodingTypePointer:
- case YYEncodingTypeCString: {
- size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
- ((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
- } break;
- case YYEncodingTypeStruct:
- case YYEncodingTypeUnion: {
- @try {
- NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
- if (value) {
- [one setValue:value forKey:propertyMeta->_name];
- }
- } @catch (NSException *exception) {}
- } // break; commented for code coverage in next line
- default: break;
- }
- }
- }
- return one;
- }
- - (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder {
- if (!aCoder) return;
- if (self == (id)kCFNull) {
- [((id<NSCoding>)self)encodeWithCoder:aCoder];
- return;
- }
-
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
- if (modelMeta->_nsType) {
- [((id<NSCoding>)self)encodeWithCoder:aCoder];
- return;
- }
-
- for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
- if (!propertyMeta->_getter) return;
-
- if (propertyMeta->_isCNumber) {
- NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta);
- if (value) [aCoder encodeObject:value forKey:propertyMeta->_name];
- } else {
- switch (propertyMeta->_type & YYEncodingTypeMask) {
- case YYEncodingTypeObject: {
- id value = ((id (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
- if (value && (propertyMeta->_nsType || [value respondsToSelector:@selector(encodeWithCoder:)])) {
- if ([value isKindOfClass:[NSValue class]]) {
- if ([value isKindOfClass:[NSNumber class]]) {
- [aCoder encodeObject:value forKey:propertyMeta->_name];
- }
- } else {
- [aCoder encodeObject:value forKey:propertyMeta->_name];
- }
- }
- } break;
- case YYEncodingTypeSEL: {
- SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
- if (value) {
- NSString *str = NSStringFromSelector(value);
- [aCoder encodeObject:str forKey:propertyMeta->_name];
- }
- } break;
- case YYEncodingTypeStruct:
- case YYEncodingTypeUnion: {
- if (propertyMeta->_isKVCCompatible && propertyMeta->_isStructAvailableForKeyedArchiver) {
- @try {
- NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
- [aCoder encodeObject:value forKey:propertyMeta->_name];
- } @catch (NSException *exception) {}
- }
- } break;
-
- default:
- break;
- }
- }
- }
- }
- - (id)yy_modelInitWithCoder:(NSCoder *)aDecoder {
- if (!aDecoder) return self;
- if (self == (id)kCFNull) return self;
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
- if (modelMeta->_nsType) return self;
-
- for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
- if (!propertyMeta->_setter) continue;
-
- if (propertyMeta->_isCNumber) {
- NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
- if ([value isKindOfClass:[NSNumber class]]) {
- ModelSetNumberToProperty(self, value, propertyMeta);
- [value class];
- }
- } else {
- YYEncodingType type = propertyMeta->_type & YYEncodingTypeMask;
- switch (type) {
- case YYEncodingTypeObject: {
- id value = [aDecoder decodeObjectForKey:propertyMeta->_name];
- ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, propertyMeta->_setter, value);
- } break;
- case YYEncodingTypeSEL: {
- NSString *str = [aDecoder decodeObjectForKey:propertyMeta->_name];
- if ([str isKindOfClass:[NSString class]]) {
- SEL sel = NSSelectorFromString(str);
- ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_setter, sel);
- }
- } break;
- case YYEncodingTypeStruct:
- case YYEncodingTypeUnion: {
- if (propertyMeta->_isKVCCompatible) {
- @try {
- NSValue *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
- if (value) [self setValue:value forKey:propertyMeta->_name];
- } @catch (NSException *exception) {}
- }
- } break;
-
- default:
- break;
- }
- }
- }
- return self;
- }
- - (NSUInteger)yy_modelHash {
- if (self == (id)kCFNull) return [self hash];
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
- if (modelMeta->_nsType) return [self hash];
-
- NSUInteger value = 0;
- NSUInteger count = 0;
- for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
- if (!propertyMeta->_isKVCCompatible) continue;
- value ^= [[self valueForKey:NSStringFromSelector(propertyMeta->_getter)] hash];
- count++;
- }
- if (count == 0) value = (long)((__bridge void *)self);
- return value;
- }
- - (BOOL)yy_modelIsEqual:(id)model {
- if (self == model) return YES;
- if (![model isMemberOfClass:self.class]) return NO;
- _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
- if (modelMeta->_nsType) return [self isEqual:model];
- if ([self hash] != [model hash]) return NO;
-
- for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
- if (!propertyMeta->_isKVCCompatible) continue;
- id this = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
- id that = [model valueForKey:NSStringFromSelector(propertyMeta->_getter)];
- if (this == that) continue;
- if (this == nil || that == nil) return NO;
- if (![this isEqual:that]) return NO;
- }
- return YES;
- }
- - (NSString *)yy_modelDescription {
- return ModelDescription(self);
- }
- @end
- @implementation NSArray (YYModel)
- + (NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json {
- if (!json) return nil;
- NSArray *arr = nil;
- NSData *jsonData = nil;
- if ([json isKindOfClass:[NSArray class]]) {
- arr = json;
- } else if ([json isKindOfClass:[NSString class]]) {
- jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
- } else if ([json isKindOfClass:[NSData class]]) {
- jsonData = json;
- }
- if (jsonData) {
- arr = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
- if (![arr isKindOfClass:[NSArray class]]) arr = nil;
- }
- return [self yy_modelArrayWithClass:cls array:arr];
- }
- + (NSArray *)yy_modelArrayWithClass:(Class)cls array:(NSArray *)arr {
- if (!cls || !arr) return nil;
- NSMutableArray *result = [NSMutableArray new];
- for (NSDictionary *dic in arr) {
- if (![dic isKindOfClass:[NSDictionary class]]) continue;
- NSObject *obj = [cls yy_modelWithDictionary:dic];
- if (obj) [result addObject:obj];
- }
- return result;
- }
- @end
- @implementation NSDictionary (YYModel)
- + (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json {
- if (!json) return nil;
- NSDictionary *dic = nil;
- NSData *jsonData = nil;
- if ([json isKindOfClass:[NSDictionary class]]) {
- dic = json;
- } else if ([json isKindOfClass:[NSString class]]) {
- jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
- } else if ([json isKindOfClass:[NSData class]]) {
- jsonData = json;
- }
- if (jsonData) {
- dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
- if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
- }
- return [self yy_modelDictionaryWithClass:cls dictionary:dic];
- }
- + (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls dictionary:(NSDictionary *)dic {
- if (!cls || !dic) return nil;
- NSMutableDictionary *result = [NSMutableDictionary new];
- for (NSString *key in dic.allKeys) {
- if (![key isKindOfClass:[NSString class]]) continue;
- NSObject *obj = [cls yy_modelWithDictionary:dic[key]];
- if (obj) result[key] = obj;
- }
- return result;
- }
- @end
|