版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.03.30 |
前言
iOS圈內(nèi)有幾個(gè)人大家基本都知道,比如說(shuō)王巍、唐巧毫缆,還有YYKit框架的作者現(xiàn)任職于滴滴的郭曜源 - ibireme等杏节。這里有一篇唐巧對(duì)他的專訪唬渗,還有他的 GitHub - Yaoyuan 和 博客,這里貼出來(lái)框架YYKit 框架奋渔。接下來(lái)幾篇我們就一起來(lái)看一下這個(gè)框架镊逝。感興趣的可以看上面寫的幾篇。
1. YYKit源碼探究(一) —— 基本概覽
2. YYKit源碼探究(二) —— NSString分類之Hash(一)
3. YYKit源碼探究(三) —— NSString分類之Encode and decode(二)
4. YYKit源碼探究(四) —— NSString分類之Drawing(三)
5. YYKit源碼探究(五) —— NSString分類之Regular Expression(四)
6. YYKit源碼探究(六) —— NSString分類之NSNumber Compatible(五)
7. YYKit源碼探究(七) —— NSString分類之Utilities(六)
8. YYKit源碼探究(八) —— NSNumber分類(一)
9. YYKit源碼探究(九) —— UIFont分類之架構(gòu)分析和Font Traits(一)
10. YYKit源碼探究(十) —— UIFont分類之Create font(二)
11. YYKit源碼探究(十一) —— UIFont分類之Load and unload font(三)
12. YYKit源碼探究(十二) —— UIFont分類之Dump font data(四)
13. YYKit源碼探究(十三) —— UIImage分類之框架結(jié)構(gòu)和Create image部分(一)
14. YYKit源碼探究(十四) —— UIImage分類之Image Info(二)
15. YYKit源碼探究(十五) —— UIImage分類之Modify Image(三)
16. YYKit源碼探究(十六) —— UIImage分類之Image Effect(四)
17. YYKit源碼探究(十七) —— UIImageView分類之架構(gòu)和image部分(一)
18. YYKit源碼探究(十八) —— UIImageView分類之highlight image部分(二)
19. YYKit源碼探究(十九) —— UIScreen分類(一)
20. YYKit源碼探究(二十) —— UIScrollView分類(一)
21. YYKit源碼探究(二十一) —— UITableView分類(一)
22. YYKit源碼探究(二十二) —— UITextField分類(一)
23. YYKit源碼探究(二十三) —— UIView分類(一)
24. YYKit源碼探究(二十四) —— UIPasteboard分類(一)
25. YYKit源碼探究(二十五) —— UIGestureRecognizer分類(一)
26. YYKit源碼探究(二十六) —— UIDevice分類框架及Device Information(一)
27. YYKit源碼探究(二十七) —— UIDevice分類之Network Information(二)
28. YYKit源碼探究(二十八) —— UIDevice分類之Disk Space(三)
29. YYKit源碼探究(二十九) —— UIDevice分類之Memory Information(四)
30. YYKit源碼探究(三十) —— UIDevice分類之CPU Information(五)
31. YYKit源碼探究(三十一) —— UIControl分類(一)
32. YYKit源碼探究(三十二) —— UIColor分類之Create a UIColor Object(一)
33. YYKit源碼探究(三十三) —— UIColor分類之Get color's description(二)
34. YYKit源碼探究(三十四) —— UIColor分類之Retrieving Color Information(三)
35. YYKit源碼探究(三十五) —— UIButton分類之image(一)
36. YYKit源碼探究(三十六) —— UIButton分類之background image(二)
37. YYKit源碼探究(三十七) —— UIBezierPath分類(一)
38. YYKit源碼探究(三十八) —— UIBarButtonItem分類(一)
39. YYKit源碼探究(三十九) —— UIApplication分類(一)
40. YYKit源碼探究(四十) —— NSTimer分類(一)
41. YYKit源碼探究(四十一) —— NSParagraphStyle分類(一)
回顧
上一篇主要介紹了NSParagraphStyle
分類卒稳,這一篇主要看一下NSObject
分類YYModel
部分蹋半。
API
下面我們一起來(lái)看一下API文檔。
YYModel協(xié)議
/**
If the default model transform does not fit to your model class, implement one or
more method in this protocol to change the default key-value transform process.
There's no need to add '<YYModel>' to your class header.
*/
@protocol YYModel <NSObject>
@optional
/**
Custom property mapper.
@discussion If the key in JSON/Dictionary does not match to the model's property name,
implements this method and returns the additional mapper.
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
@return A custom mapper for properties.
*/
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
/**
The generic class mapper for container properties.
@discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
implements this method and returns a property->class mapper, tells which kind of
object will be add to the array/set/dictionary.
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
@return A class mapper.
*/
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
/**
If you need to create instances of different classes during json->object transform,
use the method to choose custom class based on dictionary data.
@discussion If the model implements this method, it will be called to determine resulting class
during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
(both singular and containers via `+modelContainerPropertyGenericClass`).
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
@param dictionary The json/kv dictionary.
@return Class to create from this dictionary, `nil` to use current class.
*/
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
/**
All the properties in blacklist will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
/**
If a property is not in the whitelist, it will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
/**
This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
but be called before the model transform.
@discussion If the model implements this method, it will be called before
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns nil, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns the modified dictionary, or nil to ignore this model.
*/
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
/**
If the default json-to-model transform does not fit to your model object, implement
this method to do additional process. You can also use this method to validate the
model's properties.
@discussion If the model implements this method, it will be called at the end of
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns NO, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
/**
If the default model-to-json transform does not fit to your model class, implement
this method to do additional process. You can also use this method to validate the
json dictionary.
@discussion If the model implements this method, it will be called at the end of
`-modelToJSONObject` and `-modelToJSONString`.
If this method returns NO, the transform process will ignore this json dictionary.
@param dic The json dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
@end
如果默認(rèn)的模型轉(zhuǎn)換不能適合你的模型類充坑,實(shí)現(xiàn)協(xié)議中的一個(gè)或者更多的方法來(lái)改變默認(rèn)的鍵值轉(zhuǎn)換過(guò)程减江,不用在你的類的頭文件中添加<YYModel>
。
下面我們就一起詳細(xì)的看一下這個(gè)協(xié)議捻爷。
1. + (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
如果JSON/Dictionary中的key不能匹配模型中屬性名字辈灼,實(shí)現(xiàn)這個(gè)方法并返回映射。
注意里面中的例子也榄。
2. + (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
容器屬性的泛型類映射器巡莹。
如果屬性是一個(gè)容器對(duì)象司志,如NSArray / NSSet / NSDictionary
,實(shí)現(xiàn)此方法并返回一個(gè)property->class
映射器降宅,告訴哪種對(duì)象將被添加到array/set/dictionary
骂远。
3. + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
如果你需要在json-> object
轉(zhuǎn)換過(guò)程中創(chuàng)建不同類的實(shí)例,使用該方法根據(jù)字典數(shù)據(jù)選擇自定義類腰根。
如果模型實(shí)現(xiàn)了這個(gè)方法激才,它將被調(diào)用來(lái)確定結(jié)果類在+ modelWithJSON:
,+ modelWithDictionary:
期間额嘿,傳遞父對(duì)象屬性的對(duì)象(通過(guò)+ modelContainerPropertyGenericClass
包含單數(shù)和容器)瘸恼。
4. + (nullable NSArray<NSString *> *)modelPropertyBlacklist;
在模型轉(zhuǎn)換過(guò)程中,所有黑名單中的屬性將被忽略册养,返回值就是一組屬性名稱东帅。
5. + (nullable NSArray<NSString *> *)modelPropertyWhitelist;
如果屬性不在白名單中,它將在模型轉(zhuǎn)換過(guò)程中被忽略球拦。
6. - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
該方法與- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
類似靠闭,但是是在模型轉(zhuǎn)換之前調(diào)用的。
如果模型實(shí)現(xiàn)了這個(gè)方法刘莹,它就會(huì)在+modelWithJSON:
, +modelWithDictionary:
, -modelSetWithJSON:
and -modelSetWithDictionary:
之前進(jìn)行調(diào)用阎毅,如果這個(gè)方法返回nil,轉(zhuǎn)換過(guò)程將會(huì)忽略這個(gè)模型点弯。
7. - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
如果默認(rèn)的json-to-model
轉(zhuǎn)換不適合你的模型對(duì)象扇调,實(shí)現(xiàn)這個(gè)方法進(jìn)行進(jìn)一步的處理,你也可以使用這個(gè)方法驗(yàn)證模型屬性抢肛。
如果模型實(shí)現(xiàn)了這個(gè)方法狼钮,它將會(huì)在+modelWithJSON:
, +modelWithDictionary:
, -modelSetWithJSON:
and -modelSetWithDictionary:
后進(jìn)行調(diào)用,如果方法返回NO捡絮,轉(zhuǎn)換過(guò)程將會(huì)忽略這個(gè)模型熬芜。
8. - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
如果默認(rèn)的model-to-json
轉(zhuǎn)換不適合你的模型對(duì)象,實(shí)現(xiàn)這個(gè)方法進(jìn)行進(jìn)一步的處理福稳,你也可以使用這個(gè)方法驗(yàn)證模型屬性涎拉。
如果模型實(shí)現(xiàn)了這個(gè)方法,它將會(huì)在 -modelToJSONObject
and -modelToJSONString
后進(jìn)行調(diào)用的圆,如果方法返回NO鼓拧,轉(zhuǎn)換過(guò)程將會(huì)忽略這個(gè)模型。
NSArray (YYModel)
下面我們就看一下這個(gè)API
/**
Provide some data-model method for NSArray.
*/
@interface NSArray (YYModel)
/**
Creates and returns an array from a json-array.
This method is thread-safe.
@param cls The instance's class in array.
@param json A json array of `NSArray`, `NSString` or `NSData`.
Example: [{"name","Mary"},{name:"Joe"}]
@return A array, or nil if an error occurs.
*/
+ (nullable NSArray *)modelArrayWithClass:(Class)cls json:(id)json;
@end
1. + (nullable NSArray *)modelArrayWithClass:(Class)cls json:(id)json;
該方法的作用就是從json-array
創(chuàng)建并返回一個(gè)數(shù)組越妈。
方法實(shí)現(xiàn)
+ (NSArray *)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 modelArrayWithClass:cls array:arr];
}
+ (NSArray *)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 modelWithDictionary:dic];
if (obj) [result addObject:obj];
}
return result;
}
NSDictionary (YYModel)
下面我們就看一下這個(gè)API
/**
Provide some data-model method for NSDictionary.
*/
@interface NSDictionary (YYModel)
/**
Creates and returns a dictionary from a json.
This method is thread-safe.
@param cls The value instance's class in dictionary.
@param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
@return A dictionary, or nil if an error occurs.
*/
+ (nullable NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json;
@end
1. + (nullable NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json;
該方法的作用就是從json
創(chuàng)建并返回一個(gè)字典季俩。
方法實(shí)現(xiàn)
+ (NSDictionary *)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 modelDictionaryWithClass:cls dictionary:dic];
}
+ (NSDictionary *)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 modelWithDictionary:dic[key]];
if (obj) result[key] = obj;
}
return result;
}
NSObject (YYModel)
下面我們就看一下這個(gè)API
@interface NSObject (YYModel)
/**
Creates and returns a new instance of the receiver from a json.
This method is thread-safe.
@param json A json object in `NSDictionary`, `NSString` or `NSData`.
@return A new instance created from the json, or nil if an error occurs.
*/
+ (nullable instancetype)modelWithJSON:(id)json;
/**
Creates and returns a new instance of the receiver from a key-value dictionary.
This method is thread-safe.
@param dictionary A key-value dictionary mapped to the instance's properties.
Any invalid key-value pair in dictionary will be ignored.
@return A new instance created from the dictionary, or nil if an error occurs.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type does not match the
property, this method will try to convert the value based on these rules:
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
*/
+ (nullable instancetype)modelWithDictionary:(NSDictionary *)dictionary;
/**
Set the receiver's properties with a json object.
@discussion Any invalid data in json will be ignored.
@param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
receiver's properties.
@return Whether succeed.
*/
- (BOOL)modelSetWithJSON:(id)json;
/**
Set the receiver's properties with a key-value dictionary.
@param dic A key-value dictionary mapped to the receiver's properties.
Any invalid key-value pair in dictionary will be ignored.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type doesn't match the
property, this method will try to convert the value based on these rules:
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
@return Whether succeed.
*/
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
/**
Generate a json object from the receiver's properties.
@return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
See [NSJSONSerialization isValidJSONObject] for more information.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
the inner object to json object.
*/
- (nullable id)modelToJSONObject;
/**
Generate a json string's data from the receiver's properties.
@return A json string's data, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSData *)modelToJSONData;
/**
Generate a json string from the receiver's properties.
@return A json string, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSString *)modelToJSONString;
/**
Copy a instance with the receiver's properties.
@return A copied instance, or nil if an error occurs.
*/
- (nullable id)modelCopy;
/**
Encode the receiver's properties to a coder.
@param aCoder An archiver object.
*/
- (void)modelEncodeWithCoder:(NSCoder *)aCoder;
/**
Decode the receiver's properties from a decoder.
@param aDecoder An archiver object.
@return self
*/
- (id)modelInitWithCoder:(NSCoder *)aDecoder;
/**
Get a hash code with the receiver's properties.
@return Hash code.
*/
- (NSUInteger)modelHash;
/**
Compares the receiver with another object for equality, based on properties.
@param model Another object.
@return `YES` if the reciever is equal to the object, otherwise `NO`.
*/
- (BOOL)modelIsEqual:(id)model;
/**
Description method for debugging purposes based on properties.
@return A string that describes the contents of the receiver.
*/
- (NSString *)modelDescription;
@end
下面我們就詳細(xì)的看一下這個(gè)API
1. + (nullable instancetype)modelWithJSON:(id)json;
從json創(chuàng)建并返回一個(gè)實(shí)例,該方法是線程安全的梅掠。
方法實(shí)現(xiàn)
+ (instancetype)modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self modelWithDictionary:dic];
}
2. + (nullable instancetype)modelWithDictionary:(NSDictionary *)dictionary;
從字典創(chuàng)建并返回一個(gè)實(shí)例酌住,該方法是線程安全的店归。
這里注意,在給對(duì)象的屬性進(jìn)行賦值的時(shí)候酪我,如果值類型和屬性類型不符合的話消痛,該方法會(huì)按照下面規(guī)則進(jìn)行適當(dāng)?shù)霓D(zhuǎn)換。
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
方法實(shí)現(xiàn)
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
if ([one modelSetWithDictionary:dictionary]) return one;
return nil;
}
3. - (BOOL)modelSetWithJSON:(id)json;
判斷model和JSON之間的轉(zhuǎn)換是否成功都哭。
方法實(shí)現(xiàn)
- (BOOL)modelSetWithJSON:(id)json {
NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json];
return [self modelSetWithDictionary:dic];
}
4. - (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
用給定的字典設(shè)置接受者的屬性肄满。
字典中的key會(huì)映射到接受者的屬性名稱上,值將被設(shè)置給屬性质涛,如果值類型和屬性類型不相符,這個(gè)方法將會(huì)按照下面規(guī)則進(jìn)行轉(zhuǎn)換掰担。
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
方法實(shí)現(xiàn)
- (BOOL)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;
}
5. - (nullable id)modelToJSONObject;
根據(jù)接受者屬性生成一個(gè)json對(duì)象也糊。
如果接受者是NSArray
, NSDictionary
or NSSet
仅政,那么它僅將內(nèi)部對(duì)象轉(zhuǎn)化為json對(duì)象。
方法實(shí)現(xiàn)
- (id)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;
}
6. - (nullable NSData *)modelToJSONData;
根據(jù)接受者屬性生成一個(gè)json data。
如果接受者是NSArray
, NSDictionary
or NSSet
舀奶,那么它僅將內(nèi)部對(duì)象轉(zhuǎn)化為json字符串。
方法實(shí)現(xiàn)
- (NSData *)modelToJSONData {
id jsonObject = [self modelToJSONObject];
if (!jsonObject) return nil;
return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
}
7. - (nullable NSString *)modelToJSONString;
根據(jù)接受者屬性碌宴,生成一個(gè)json字符串页慷。
如果接受者是NSArray
, NSDictionary
or NSSet
,那么它僅將內(nèi)部對(duì)象轉(zhuǎn)化為json字符串执庐。
方法實(shí)現(xiàn)
- (NSString *)modelToJSONString {
NSData *jsonData = [self modelToJSONData];
if (jsonData.length == 0) return nil;
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
8. - (nullable id)modelCopy;
根據(jù)接受者屬性酪耕,生成一個(gè)實(shí)例。
方法實(shí)現(xiàn)
- (id)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;
}
9. - (void)modelEncodeWithCoder:(NSCoder *)aCoder;
將接受者屬性編碼到coder轨淌。
方法實(shí)現(xiàn)
- (void)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;
}
}
}
}
10. - (id)modelInitWithCoder:(NSCoder *)aDecoder;
將接受者屬性進(jìn)行解碼迂烁。
方法實(shí)現(xiàn)
- (id)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;
}
11. - (NSUInteger)modelHash;
獲取接收者屬性的hash code
方法實(shí)現(xiàn)
- (NSUInteger)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;
}
12. - (BOOL)modelIsEqual:(id)model;
基于屬性,比較接受者和另外一個(gè)對(duì)象是否相等递鹉。
方法實(shí)現(xiàn)
- (BOOL)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;
}
13. - (NSString *)modelDescription;
返回接受者內(nèi)容的描述字符串盟步。
方法實(shí)現(xiàn)
- (NSString *)modelDescription {
return ModelDescription(self);
}
/// 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;
}
}
}
后記
本篇主要介紹了NSObject的一個(gè)分類,感興趣的給個(gè)贊或者關(guān)注~~~