拥有一个自己的模型基类。
在进行数据获取的时候会将数据使用模型进行存储,我常规的方法都是给一两个遍历初始化方法,在在里面对字典进行取值赋给相应的模型属性。
这样的好处也有,就是代码直观,快速。由于模型的属性一般是根据网络或者数据库获得的json进行设定的,所以可以考虑使用KVC,而且如果一个模型有很多个属性的话,这样写也比较麻烦。
而使用runtime可以不用考虑属性的个数(有点儿像KVC,但是这只是对runtime的一次练习使用,所以就用了runtime),这样的做法是可以得到一个自己的模型基类
,重用性大大提升.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #pragma mark - convenience
- (instancetype) initWithDict:(NSDictionary *)dict{
self = [super init]; if (self) { [self assginToPropertyWithDictionary:dict]; [self assginToRuntimePropertyWithDictionary:dict]; } return self; } + (instancetype) itemWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict]; }
|
下面有两种方法进行模型属性的赋值,第一种是使用runtime得到模型的所有属性,然后还用KVC进行赋值,第二种是在cocoaChina上看到的一个方法,是得到对应属性的setter方法,然后对属性进行赋值,两种都来感受一下,后面会说两种方法的不同。
runtime
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| - (void) assginToRuntimePropertyWithDictionary:(NSDictionary *)dict{ if (dict == nil) { return; } NSArray * allKeys = dict.allKeys; unsigned int outCount; objc_property_t * propertys = class_copyPropertyList(self.class, &outCount); for (int index = 0; index < outCount; index ++) { objc_property_t property = propertys[index]; NSString * propertyKey = [NSString stringWithUTF8String:property_getName(property)]; if ([allKeys containsObject:propertyKey]) { id propertyValue = [dict objectForKey:propertyKey]; [self setValue:propertyValue forKey:propertyKey]; } } free(propertys); }
|
cocoaChina
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| - (void) assginToPropertyWithDictionary:(NSDictionary *)dict{
if (dict == nil) { return; } NSArray * allKeys = dict.allKeys; for (int index = 0; index < allKeys.count; index ++) { SEL setSEL = [self creatSetterWithPropertyName:allKeys[index]]; if ([self respondsToSelector:setSEL]) { NSString * value = [dict objectForKey:allKeys[index]]; [self performSelectorOnMainThread:setSEL withObject:value waitUntilDone:[NSThread isMainThread]]; } } }
- (SEL) creatSetterWithPropertyName:(NSString *)propertyName{ propertyName = propertyName.capitalizedString; NSString * setterName = [NSString stringWithFormat:@"set%@:",propertyName]; return NSSelectorFromString(setterName); }
|
在模型的属性为一般的常规属性(id)的时候,比如,NSString,NSArray,NSDictionary等的时候两个方法都可以正确的完成属性赋值。
但是在基本数据类型的属性(非id)的时候,比如,NSInterger,BOOL,int等的时候,由于第二个就必须要进行解包和封包,因此导致不能正确的进行属性赋值,而第一个不论是否进行封包都能够正确进行属性赋值。
因此还是推荐使用runtime进行属性赋值。
一个不可控制的情况是有的时候还是会遇到字典的key和模型的属性不同,这时候的解决办法是需要在类中建立key和属性的映射关系。
在模型基类中写如下方法
1 2 3 4 5 6 7 8
|
-(NSDictionary *) propertyMapDic { return nil; }
|
该方法在有需要的子类中进行重写,设置映射规则
1 2 3 4 5
| -(NSDictionary *) propertyMapDic{ return @{@"firstName":@"key1", @"address":@"key3"}; }
|
然后在模型基类中实现如下方法,将有映射的字典对原字典进行重新设值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
- (void) dictionaryMapPropertyWithAllKeys:(NSMutableArray *)allKeys toNewDictionary:(NSMutableDictionary *)newDict{ NSDictionary * mapDictionary = [self propertyMapDic]; if (mapDictionary) { [mapDictionary enumerateKeysAndObjectsUsingBlock:^(NSString * mapProperty, NSString * mapKey, BOOL *stop) { if ([allKeys containsObject:mapKey]) {
[newDict setValue:[newDict objectForKey:mapKey] forKey:mapProperty]; [newDict removeObjectForKey:mapKey]; NSUInteger mapIndex = [allKeys indexOfObject:mapKey]; [allKeys replaceObjectAtIndex:mapIndex withObject:mapProperty]; } }]; } }
|
此时的runtimeAssginToPropertyWithDictionary:方法如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| - (void) runtimeAssginToPropertyWithDictionary:(NSDictionary *)dict{ NSMutableDictionary * newDict = [NSMutableDictionary dictionaryWithDictionary:dict]; for (int index = 0; index < outCount; index ++) { [self dictionaryMapPropertyWithAllKeys:allKeys toNewDictionary:newDict]; if ([allKeys containsObject:propertyKey]) { } } free(propertys); }
|
这里是自己练习的一个App,作用是仿照快起
做的一个快速启动手机上的软件。里面有简单的对控制器的数据源封装。