版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2018.03.30 |
前言
iOS圈內(nèi)有幾個人大家基本都知道,比如說王巍、唐巧,還有YYKit框架的作者現(xiàn)任職于滴滴的郭曜源 - ibireme等。這里有一篇唐巧對他的專訪,還有他的 GitHub - Yaoyuan 和 博客,這里貼出來框架YYKit 框架。接下來幾篇我們就一起來看一下這個框架。感興趣的可以看上面寫的幾篇。
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分類之架構分析和Font Traits(一)
10. YYKit源碼探究(十) —— UIFont分類之Create font(二)
11. YYKit源碼探究(十一) —— UIFont分類之Load and unload font(三)
12. YYKit源碼探究(十二) —— UIFont分類之Dump font data(四)
13. YYKit源碼探究(十三) —— UIImage分類之框架結構和Create image部分(一)
14. YYKit源碼探究(十四) —— UIImage分類之Image Info(二)
15. YYKit源碼探究(十五) —— UIImage分類之Modify Image(三)
16. YYKit源碼探究(十六) —— UIImage分類之Image Effect(四)
17. YYKit源碼探究(十七) —— UIImageView分類之架構和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
下面我們一起來看一下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
如果默認的模型轉換不能適合你的模型類,實現(xiàn)協(xié)議中的一個或者更多的方法來改變默認的鍵值轉換過程,不用在你的類的頭文件中添加<YYModel>。
下面我們就一起詳細的看一下這個協(xié)議。
1. + (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
如果JSON/Dictionary中的key不能匹配模型中屬性名字,實現(xiàn)這個方法并返回映射。
注意里面中的例子。
2. + (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
容器屬性的泛型類映射器。
如果屬性是一個容器對象,如NSArray / NSSet / NSDictionary,實現(xiàn)此方法并返回一個property->class映射器,告訴哪種對象將被添加到array/set/dictionary。
3. + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
如果你需要在json-> object轉換過程中創(chuàng)建不同類的實例,使用該方法根據(jù)字典數(shù)據(jù)選擇自定義類。
如果模型實現(xiàn)了這個方法,它將被調(diào)用來確定結果類在+ modelWithJSON:,+ modelWithDictionary:期間,傳遞父對象屬性的對象(通過+ modelContainerPropertyGenericClass包含單數(shù)和容器)。
4. + (nullable NSArray<NSString *> *)modelPropertyBlacklist;
在模型轉換過程中,所有黑名單中的屬性將被忽略,返回值就是一組屬性名稱。
5. + (nullable NSArray<NSString *> *)modelPropertyWhitelist;
如果屬性不在白名單中,它將在模型轉換過程中被忽略。
6. - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
該方法與- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;類似,但是是在模型轉換之前調(diào)用的。
如果模型實現(xiàn)了這個方法,它就會在+modelWithJSON:, +modelWithDictionary:, -modelSetWithJSON: and -modelSetWithDictionary:之前進行調(diào)用,如果這個方法返回nil,轉換過程將會忽略這個模型。
7. - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
如果默認的json-to-model轉換不適合你的模型對象,實現(xiàn)這個方法進行進一步的處理,你也可以使用這個方法驗證模型屬性。
如果模型實現(xiàn)了這個方法,它將會在+modelWithJSON:, +modelWithDictionary:, -modelSetWithJSON: and -modelSetWithDictionary:后進行調(diào)用,如果方法返回NO,轉換過程將會忽略這個模型。
8. - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
如果默認的model-to-json轉換不適合你的模型對象,實現(xiàn)這個方法進行進一步的處理,你也可以使用這個方法驗證模型屬性。
如果模型實現(xiàn)了這個方法,它將會在 -modelToJSONObject and -modelToJSONString后進行調(diào)用,如果方法返回NO,轉換過程將會忽略這個模型。
NSArray (YYModel)
下面我們就看一下這個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)建并返回一個數(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)
下面我們就看一下這個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)建并返回一個字典。
方法實現(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)
下面我們就看一下這個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
下面我們就詳細的看一下這個API
1. + (nullable instancetype)modelWithJSON:(id)json;
從json創(chuàng)建并返回一個實例,該方法是線程安全的。
方法實現(xiàn)
+ (instancetype)modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self modelWithDictionary:dic];
}
2. + (nullable instancetype)modelWithDictionary:(NSDictionary *)dictionary;
從字典創(chuàng)建并返回一個實例,該方法是線程安全的。
這里注意,在給對象的屬性進行賦值的時候,如果值類型和屬性類型不符合的話,該方法會按照下面規(guī)則進行適當?shù)霓D換。
`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.
方法實現(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之間的轉換是否成功。
方法實現(xiàn)
- (BOOL)modelSetWithJSON:(id)json {
NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json];
return [self modelSetWithDictionary:dic];
}
4. - (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
用給定的字典設置接受者的屬性。
字典中的key會映射到接受者的屬性名稱上,值將被設置給屬性,如果值類型和屬性類型不相符,這個方法將會按照下面規(guī)則進行轉換。
`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.
方法實現(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ù)接受者屬性生成一個json對象。
如果接受者是NSArray, NSDictionary or NSSet,那么它僅將內(nèi)部對象轉化為json對象。
方法實現(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ù)接受者屬性生成一個json data。
如果接受者是NSArray, NSDictionary or NSSet,那么它僅將內(nèi)部對象轉化為json字符串。
方法實現(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ù)接受者屬性,生成一個json字符串。
如果接受者是NSArray, NSDictionary or NSSet,那么它僅將內(nèi)部對象轉化為json字符串。
方法實現(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ù)接受者屬性,生成一個實例。
方法實現(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。
方法實現(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;
將接受者屬性進行解碼。
方法實現(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
方法實現(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;
基于屬性,比較接受者和另外一個對象是否相等。
方法實現(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)容的描述字符串。
方法實現(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的一個分類,感興趣的給個贊或者關注~~~
