Prechádzať zdrojové kódy

Merge pull request #5 from digal/custom-model-class

Ability to choose custom model (sub-)class based on dictionary Data
Yaoyuan 9 rokov pred
rodič
commit
6cd58464ae
2 zmenil súbory, kde vykonal 75 pridanie a 6 odobranie
  1. 34 0
      YYModel/NSObject+YYModel.h
  2. 41 6
      YYModel/NSObject+YYModel.m

+ 34 - 0
YYModel/NSObject+YYModel.h

@@ -319,6 +319,40 @@
  */
  */
 + (NSDictionary *)modelContainerPropertyGenericClass;
 + (NSDictionary *)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.
+
+ */
++ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary;
+
 /**
 /**
  All the properties in blacklist will be ignored in model transform process.
  All the properties in blacklist will be ignored in model transform process.
  Returns nil to ignore this feature.
  Returns nil to ignore this feature.

+ 41 - 6
YYModel/NSObject+YYModel.m

@@ -292,7 +292,7 @@ static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic,
     Class _genericCls;           ///< container's generic class, or nil if threr's no generic class
     Class _genericCls;           ///< container's generic class, or nil if threr's no generic class
     SEL _getter;                 ///< getter, or nil if the instances cannot respond
     SEL _getter;                 ///< getter, or nil if the instances cannot respond
     SEL _setter;                 ///< setter, or nil if the instances cannot respond
     SEL _setter;                 ///< setter, or nil if the instances cannot respond
-    
+    BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
     NSString *_mappedToKey;      ///< the key mapped to
     NSString *_mappedToKey;      ///< the key mapped to
     NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
     NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
     _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
     _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
@@ -311,6 +311,14 @@ static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic,
         meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
         meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
     }
     }
     meta->_cls = propertyInfo.cls;
     meta->_cls = propertyInfo.cls;
+
+    if (generic) {
+        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
+    } else if (meta->_cls && meta->_type == YYEncodingTypeNSUnknown) {
+        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
+    }
+
+    
     if (propertyInfo.getter) {
     if (propertyInfo.getter) {
         SEL sel = NSSelectorFromString(propertyInfo.getter);
         SEL sel = NSSelectorFromString(propertyInfo.getter);
         if ([classInfo.cls instancesRespondToSelector:sel]) {
         if ([classInfo.cls instancesRespondToSelector:sel]) {
@@ -323,6 +331,7 @@ static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic,
             meta->_setter = sel;
             meta->_setter = sel;
         }
         }
     }
     }
+    
     return meta;
     return meta;
 }
 }
 @end
 @end
@@ -344,6 +353,7 @@ static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic,
     
     
     BOOL _hasCustomTransformFromDictionary;
     BOOL _hasCustomTransformFromDictionary;
     BOOL _hasCustomTransformToDictionary;
     BOOL _hasCustomTransformToDictionary;
+    BOOL _hasCustomClassFromDictionary;
 }
 }
 @end
 @end
 
 
@@ -452,6 +462,7 @@ static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic,
     _nsType = YYClassGetNSType(cls);
     _nsType = YYClassGetNSType(cls);
     _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
     _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
     _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
     _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
+    _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
     
     
     return self;
     return self;
 }
 }
@@ -739,7 +750,12 @@ static void ModelSetValueForProperty(__unsafe_unretained id model,
                                 if ([one isKindOfClass:meta->_genericCls]) {
                                 if ([one isKindOfClass:meta->_genericCls]) {
                                     [objectArr addObject:one];
                                     [objectArr addObject:one];
                                 } else if ([one isKindOfClass:[NSDictionary class]]) {
                                 } else if ([one isKindOfClass:[NSDictionary class]]) {
-                                    NSObject *newOne = [meta->_genericCls new];
+                                    Class clazz = meta->_genericCls;
+                                    if (meta->_hasCustomClassFromDictionary) {
+                                        clazz = [clazz modelCustomClassForDictionary:one] ?: clazz;
+                                    }
+                                    
+                                    NSObject *newOne = [clazz new];
                                     [newOne yy_modelSetWithDictionary:one];
                                     [newOne yy_modelSetWithDictionary:one];
                                     if (newOne) [objectArr addObject:newOne];
                                     if (newOne) [objectArr addObject:newOne];
                                 }
                                 }
@@ -778,7 +794,12 @@ static void ModelSetValueForProperty(__unsafe_unretained id model,
                             NSMutableDictionary *dic = [NSMutableDictionary new];
                             NSMutableDictionary *dic = [NSMutableDictionary new];
                             [((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, NSObject *oneValue, BOOL *stop) {
                             [((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, NSObject *oneValue, BOOL *stop) {
                                 if ([oneValue isKindOfClass:[NSDictionary class]]) {
                                 if ([oneValue isKindOfClass:[NSDictionary class]]) {
-                                    NSObject *o = [meta->_genericCls new];
+                                    Class clazz = meta->_genericCls;
+                                    if (meta->_hasCustomClassFromDictionary) {
+                                        clazz = [clazz modelCustomClassForDictionary:oneValue] ?: clazz;
+                                    }
+
+                                    NSObject *o = [clazz new];
                                     [o yy_modelSetWithDictionary:(id)oneValue];
                                     [o yy_modelSetWithDictionary:(id)oneValue];
                                     if (o) dic[oneKey] = o;
                                     if (o) dic[oneKey] = o;
                                 }
                                 }
@@ -810,7 +831,11 @@ static void ModelSetValueForProperty(__unsafe_unretained id model,
                             if ([one isKindOfClass:meta->_genericCls]) {
                             if ([one isKindOfClass:meta->_genericCls]) {
                                 [set addObject:one];
                                 [set addObject:one];
                             } else if ([one isKindOfClass:[NSDictionary class]]) {
                             } else if ([one isKindOfClass:[NSDictionary class]]) {
-                                NSObject *newOne = [meta->_genericCls new];
+                                Class clazz = meta->_genericCls;
+                                if (meta->_hasCustomClassFromDictionary) {
+                                    clazz = [clazz modelCustomClassForDictionary:one] ?: clazz;
+                                }
+                                NSObject *newOne = [clazz new];
                                 [newOne yy_modelSetWithDictionary:one];
                                 [newOne yy_modelSetWithDictionary:one];
                                 if (newOne) [set addObject:newOne];
                                 if (newOne) [set addObject:newOne];
                             }
                             }
@@ -848,7 +873,11 @@ static void ModelSetValueForProperty(__unsafe_unretained id model,
                     if (one) {
                     if (one) {
                         [one yy_modelSetWithDictionary:value];
                         [one yy_modelSetWithDictionary:value];
                     } else {
                     } else {
-                        one = [meta->_cls new];
+                        Class clazz = meta->_cls;
+                        if (meta->_hasCustomClassFromDictionary) {
+                            clazz = [clazz modelCustomClassForDictionary:value] ?: clazz;
+                        }
+                        one = [clazz new];
                         [one yy_modelSetWithDictionary:value];
                         [one yy_modelSetWithDictionary:value];
                         ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
                         ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
                     }
                     }
@@ -1100,7 +1129,13 @@ static id ModelToJSONObjectRecursive(NSObject *model) {
 + (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
 + (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
     if (!dictionary || dictionary == (id)kCFNull) return nil;
     if (!dictionary || dictionary == (id)kCFNull) return nil;
     if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
     if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
-    NSObject *one = [self new];
+    Class clazz = [self class];
+    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:clazz];
+    if (modelMeta->_hasCustomClassFromDictionary) {
+        clazz = [clazz modelCustomClassForDictionary:dictionary] ?: clazz;
+    }
+
+    NSObject *one = [clazz new];
     [one yy_modelSetWithDictionary:dictionary];
     [one yy_modelSetWithDictionary:dictionary];
     return one;
     return one;
 }
 }