項目地址點這里......
學習NSObject+YYModel之前要先了學一下他YYClassInfo類.
如果runtime有了解過,就會發現yyclassinfo 是對ivar,method,objc_property,class這三個做了整理歸納定義,對應YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo,YYClassInfo更容易看懂他們每個類所擁有的特性,也更方便的直接的使用。他們的實現也是基于Runtime,具體可以看YYKit
的源碼.
參考:http://www.lxweimin.com/p/36273f598731
開始學習NSObject+YYModel
#define force_inline __inline__ __attribute__((always_inline))
定義內聯函數
獲取這個屬性的類型信息
static force_inline YYEncodingNSType YYClassGetNSType(Class cls){}
是否是number型,基本類型
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type)
從id中解析nsnumber類型 不是很懂
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value){}
字符串轉化為date類型
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {}
定義NSBlock類
static force_inline Class YYNSBlockClass(){}
獲取國際標準化組織的日期格式
static force_inline NSDateFormatter *YYISODateFormatter() {}
獲取字典里面一序列鍵值對應的值
static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {}
獲取字典里面一序列鍵值對應的值 感覺跟上面一個方法沒什么差
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {}
通過屬性獲取number
static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model, __unsafe_unretained _YYModelPropertyMeta *meta) {}
設置number到屬性
static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
__unsafe_unretained NSNumber *num,
__unsafe_unretained _YYModelPropertyMeta *meta) {}
設置值與屬性的元模型。
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {}
應用函數字典,設置鍵-值對模型。
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {}
應用屬性元函數模型,設置字典模型。
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {}
將模型轉化成json串
static id ModelToJSONObjectRecursive(NSObject *model) {}
增加縮進字符串(排除第一行)
static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {}
模型生成一個字符串描述
static NSString *ModelDescription(NSObject *model) {}
NSObject+YYModel
1.+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {}
將傳進來的json類型轉化為字典
2.+ (instancetype)modelWithJSON:(id)json {}
通過傳進來的json實例該類對象,通過 modelWithDictionary轉化
3.+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {}
- 通過私有類_YYModelMeta(模型元件)將該類中的屬性,變量,方法,映射信息都提取出來 在其中_YYModelMeta中:
類的基本信息
YYClassInfo *_classInfo;
屬性信息和_YYModelPropertyMeta屬性類的映射
NSDictionary *_mapper;
所有屬性對象的數組,每個元素是_YYModelPropertyMeta類型
NSArray *_allPropertyMetas;
所有屬性對應的keypath的映射的數組,每個屬性類型也都是_YYModelPropertyMeta類型
NSArray *_keyPathPropertyMetas;
有多個key映射
NSArray *_multiKeysPropertyMetas;
映射的數量
NSUInteger _keyMappedCount;
類編碼類型
YYEncodingNSType _nsType;
是否執行 modelCustomWillTransformFromDictionary 自定義對象將要轉化為模型的時候,如果有實現該方法,他將先于 `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` 和 `-modelSetWithDictionary:`這些方法執行,返回是一個字典類型.
BOOL _hasCustomWillTransformFromDictionary;
是否執行 modelCustomTransformFromDictionary, 返回的是一個布爾值,如果是默認的json-to-model轉化的時候某個字段適合你的自定義的模型,可以去實現這個額外的方法,你也可以使用這個方法來校驗你屬性的類型,返回YES這個模型是可用的,NO則不可用,具體看例子實現
BOOL _hasCustomTransformFromDictionary;
是否執行 modelCustomTransformToDictionary,如果是默認的json-to-model轉化的時候某個字段適合你的自定義的模型,返回YES這個模型是可用的,NO則不可用,具體看例子實現
BOOL _hasCustomTransformToDictionary;
是否執行 modelCustomClassForDictionary,在json-to-model轉化的時候,通過該方法去實例不同的類類型
BOOL _hasCustomClassFromDictionary;
在YYModeleMeta中的司機BOOL類型的賦值通過instancesRespondToSelector
和respondsToSelector
來做判斷是否實現對應的方法
他們的區別參考:
http://www.lxweimin.com/p/ae494b5eeb86
http://www.lxweimin.com/p/c37f5d97b07e
http://blog.csdn.net/yxwlzsh/article/details/49755823
instancesRespondToSelector是類方法,用于判斷實例后的對象是否綁定某個方法,只能用來判斷實例方法。作用于類
類名 + 實例方法
respondsToSelector是實例方法,用于判斷實例后的對象是否綁定某個方法,和類是否實現某個類方法,作用于對象和類
類名 + 類方法
實例之后的對象 + 實例方法
所有在上面前三個BOOL是直接通過類名來判斷實例方法所以使用instancesRespondToSelector
,最后一個是直接判斷類方法所以使用respondsToSelector
回到YYKit的model
- 通過_hasCustomClassFromDictionary判斷是否有自定義類,YES則執行modelCustomClassForDictionary
modelCustomClassForDictionary
是用戶自己在外部實現的方法,傳遞自己要實例的類類型。
- 通過確定的class類new一個對象,通過modelSetWithDictionary方法實現dic-to-model
4.- (BOOL)modelSetWithJSON:(id)json {}
判斷這個json是否可轉化成對象
5.- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {}
實現Dictionary-To-Model
- 通過該類實例_YYModeMeta類型
- 如果沒有鍵映射的count是0則不能實例,返回空,如果是集成NSObject的模型的話至少有一個isa的地址的鍵值映射,一般都會有
- 判斷是否
_hasCustomWillTransformFromDictionary
值,YES則執行modelCustomWillTransformFromDictionary - 定義結構體ModelSetContext,其中modelMeta存_YYModelMeta實例的model,model該類實例的對象,dictionary要轉化的字典類型
- 給模型賦值的過程用到了CoreFoundation相關知識,不是很清楚,后面學習一下,先跳過。
- 轉化完成之后,_hasCustomTransformFromDictionary判斷是否要自動已轉化從字典中,YES執行modelCustomTransformFromDictionary
6.- (id)modelToJSONObject {}
將模型轉化為NSArray或者NSDictionary類型,其中里面所有的對象都是通過 NSString, NSNumber, NSArray, NSDictionary, or NSNull.實例的,可直接轉化為json數據
7.- (NSData *)modelToJSONData {}
將對象轉化成Data數據
8.- (NSString *)modelToJSONString {}
將對象轉化成json字符串
9.- (id)modelCopy{}
拷貝復制對象
10.- (void)modelEncodeWithCoder:(NSCoder *)aCoder {}
和- (id)modelInitWithCoder:(NSCoder *)aDecoder {}
實現NSCoding編碼解碼,使用的時候:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[self modelEncodeWithCoder:aCoder];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
return [self modelInitWithCoder:aDecoder];
}
一句編碼解碼
11.- (NSUInteger)modelHash {}
model哈希
12.- (BOOL)modelIsEqual:(id)model {}
兩個model是否一樣,
如果地址指針是否一樣或者里面的所有的屬性信息值都是否一樣
13.- (NSString *)modelDescription {}
該類的字符串描述
例子(具體可以看YYKit
里面的YYModelExample
):
NSArray+YYModel
+ (NSArray *)modelArrayWithClass:(Class)cls json:(id)json {}
如果json串可轉化成數組類型,將這個json數組中的每個元素轉化成cls類型元素,重新轉成變成數組返回,數組中的元素都是cls類型。
NSDictionary+YYModel
+ (NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json {}
跟數組的YYModel一樣,key對應的value轉化成cls類型value
@protocol YYModel
該協議不需要去集成添加,是為了方法的擴展。為了轉模型的時候更好的定義。
1.+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
自定義屬性的映射。如果JSON或者Dic的key在model中找不到對應的關聯屬性,可以用這個方法添加映射.
Example:
json:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowling."
},
"ID" : 100010
}
model:
@interface YYBook : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation YYBook
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID": @[@"id", @"ID", @"book_id"]};
}
@end
屬性name
的值對應的是Json中的n
的key的鍵值,
屬性page
的值對應的是Json中的p
的key的鍵值,
屬性desc
的值對應的是Json中的ext.desc
的key的鍵值,其中ext.desc是路徑的key,多層的話 ext.s1.s2.s3...
屬性bookID
的值對應的是Json中的id
、ID
,book_id
的key的鍵值,多個key的值都是對應的bookID的屬性,可以用數組的方法傳值。很貼心啊。
2.+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
屬性內包含的對象自定義,如果一個屬性是一個容器的對象,比如是NSArray/NSSet/NSDictionary,實現這個方法返回對應屬性的映射,知道哪一個類將被添加到容器中,容器中的對象是哪一種類類型。
Example:
@class YYShadow, YYBorder, YYAttachment;
@interface YYAttributes
@property NSString *name;
@property NSArray *shadows;
@property NSSet *borders;
@property NSDictionary *attachments;
@end
@implementation YYAttributes
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [YYShadow class],
@"borders" : YYBorder.class,
@"attachments" : @"YYAttachment" };
}
@end
其中這個類中有三個是容器的屬性類型。shadows中是YYShadow元素類型,borders是YYBorder元素類型,NSDictionary是YYAttachment元素類型。自定義類型寫法的時候可以有這個三種寫法:[YYShadow class]
,YYBorder.class
,@"YYAttachment"
.
3.+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
上面講過,根據dic來實例不通的類類型
Example:
@class YYCircle, YYRectangle, YYLine;
@implementation YYShape
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
if (dictionary[@"radius"] != nil) {
return [YYCircle class];
} else if (dictionary[@"width"] != nil) {
return [YYRectangle class];
} else if (dictionary[@"y2"] != nil) {
return [YYLine class];
} else {
return [self class];
}
}
@end
4.+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
哪些屬性在模型轉化的過程中將被忽略,黑名單
5.+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
不在這個屬性列表中的屬性模型轉化過程中將被忽略,白名單
例子
- 普通對象實例
YHBook * book = [YHBook modelWithJSON:@" \
{ \
\"name\": \"Harry Potter\", \
\"pages\": 512, \
\"publishDate\": \"2010-01-01\" \
}"];
NSLog(@"name = %@__date = %@",book.name,book.publishDate);
NSLog(@"BOOk : %@",[book modelToJSONString]);
如果是直接使用modewithJson的時候沒有實現其他的自定義方法的時候YHBook中定義的屬性的名字需和json串中每個字段對應的key的名字一樣。
- 內嵌對象實例
YYRepo * repo = [YYRepo modelWithJSON:@" \
{ \
\"rid\": 123456789, \
\"name\": \"YYKit\", \
\"createTime\" : \"2011-06-09T06:24:26Z\", \
\"owner\": { \
\"uid\" : 989898, \
\"name\" : \"ibireme\" \
} \
}"];
NSLog(@"owner name %@",repo.owner.name);
NSLog(@"Repo: %@", [repo modelToJSONString]);
json串中在ownerkey這邊有一個新的一級字典數據,外面一層通過YYRepo來實例,owner這邊通過YYUser來實例。
- 有屬性是容器類型的屬性,定義該屬性中元素類類型的實例
@interface YYPhoto : NSObject
@property (nonatomic, copy) NSString *url;
@property (nonatomic, copy) NSString *desc;
@end
@implementation YYPhoto
@end
@interface YYAlbum : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSArray *photos; // Array<YYPhoto>
@property (nonatomic, strong) NSDictionary *likedUsers; // Key:name(NSString) Value:user(YYUser)
@property (nonatomic, strong) NSSet *likedUserIds; // Set<NSNumber>
@end
@implementation YYAlbum
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"photos" : YYPhoto.class,
@"likedUsers" : YYUser.class,
@"likedUserIds" : NSNumber.class};
}
@end
static void ContainerObjectExample() {
YYAlbum *album = [YYAlbum modelWithJSON:@" \
{ \
\"name\" : \"Happy Birthday\", \
\"photos\" : [ \
{ \
\"url\":\"http://example.com/1.png\", \
\"desc\":\"Happy~\" \
}, \
{ \
\"url\":\"http://example.com/2.png\", \
\"desc\":\"Yeah!\" \
} \
], \
\"likedUsers\" : { \
\"Jony\" : {\"uid\":10001,\"name\":\"Jony\"}, \
\"Anna\" : {\"uid\":10002,\"name\":\"Anna\"} \
}, \
\"likedUserIds\" : [10001,10002] \
}"];
NSString *albumJSON = [album modelToJSONString];
NSLog(@"Album: %@", albumJSON);
}
通過+ (NSDictionary *)modelContainerPropertyGenericClass{}
來定義photos、photos、likedUserIds這三個屬性中其元素的類類型.
- NSCoding編碼、解碼, NSCopying拷貝,哈希轉化
@interface YYShadow :NSObject <NSCoding, NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGSize size;
@property (nonatomic, strong) UIColor *color;
@end
@implementation YYShadow
- (void)encodeWithCoder:(NSCoder *)aCoder { [self modelEncodeWithCoder:aCoder]; }
- (id)initWithCoder:(NSCoder *)aDecoder { return [self modelInitWithCoder:aDecoder]; }
- (id)copyWithZone:(NSZone *)zone { return [self modelCopy]; }
- (NSUInteger)hash { return [self modelHash]; }
- (BOOL)isEqual:(id)object { return [self modelIsEqual:object]; }
@end
static void CodingCopyingHashEqualExample() {
YYShadow *shadow = [YYShadow new];
shadow.name = @"Test";
shadow.size = CGSizeMake(10, 0);
shadow.color = [UIColor blueColor];
YYShadow *shadow2 = [shadow deepCopy]; // Archive and Unachive
// shadow2.name = @"TT";
BOOL equal = [shadow isEqual:shadow2];
NSLog(@"shadow equals: %@",equal ? @"YES" : @"NO");
}