YYKit的NSObject+YYModel學習



項目地址點這里......






學習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類型的賦值通過instancesRespondToSelectorrespondsToSelector來做判斷是否實現對應的方法

他們的區別參考:

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

  1. 通過該類實例_YYModeMeta類型
  2. 如果沒有鍵映射的count是0則不能實例,返回空,如果是集成NSObject的模型的話至少有一個isa的地址的鍵值映射,一般都會有
  3. 判斷是否_hasCustomWillTransformFromDictionary值,YES則執行modelCustomWillTransformFromDictionary
  4. 定義結構體ModelSetContext,其中modelMeta存_YYModelMeta實例的model,model該類實例的對象,dictionary要轉化的字典類型
  5. 給模型賦值的過程用到了CoreFoundation相關知識,不是很清楚,后面學習一下,先跳過。
  6. 轉化完成之后,_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");
}




最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容