一、思路:
1、通过模型类型获得所有的属性和其类型
2、对获得的json进行处理。类型处理
3、考虑字典键值和模型属性名不一致的情况
4、添加code用于归档
5、补充JSON转字典、字典转JSON、字典转模型等接口
6、对处理过的properties做缓存
 
二、设计模式思考:
设计模式的选择---------继承、接口、抽象基类的选择。
在使用方便、高效率、低耦合之间抉择。
 
三、细节及实现
先把任务分解,实现各个部分细节,然后进行组合,当然我们要思考好,采用何种设计模式组装。先来看看各个部分的实现细节。
 
1.通过模型类型获得所有的属性和其类型,

 unsigned int outCount = 0;
//获得Class c所有属性这里的c是[Model class]
objc_property_t *properties = class_copyPropertyList(c, &outCount); for (int i = 0; i < outCount; i++) {
objc_property_t propert = properties[i];
//获得属性名
NSString *key = @(property_getName(propert));
//获得属性类型,如CGFloat、nonatomic、copy等信息
NSString *type = @(property_getAttributes(propert));
NSLog(@"key = %@ , type = %@", key, type);
}

 Model模型如下

//属性}
typedef void(^block)();
@interface Model : NSObject
@property (nonatomic, copy) NSString *q_NSString;
@property (nonatomic, assign) CGFloat q_CGFloat;
@property (nonatomic, assign) CGRect q_CGRect;
@property (nonatomic, assign) double q_double;
@property (nonatomic, assign) int q_int;
@property (nonatomic, assign) BOOL q_bool;
@property (nonatomic, assign) float q_float;
@property (nonatomic, assign) short q_short;
@property (nonatomic, assign) long q_long;
@property (nonatomic, assign) long long q_longlong;
@property (nonatomic, assign) Point q_point; @property (nonatomic, strong) id q_id;
@property (nonatomic, weak) id<NSObject> q_delegate;
@property (nonatomic, copy) block q_block;
@property (nonatomic, strong) Model1 *q_model1; @property SEL q_SEL;
@property Class q_Class;
@property Ivar q_Ivar;
@property Method q_Method;

输出结果为

key = q_NSString , type = T@"NSString",C,N,V_q_NSString
key = q_CGFloat , type = Td,N,V_q_CGFloat
key = q_CGRect , type = T{CGRect={CGPoint=dd}{CGSize=dd}},N,V_q_CGRect
key = q_double , type = Td,N,V_q_double
key = q_int , type = Ti,N,V_q_int
key = q_bool , type = TB,N,V_q_bool
key = q_float , type = Tf,N,V_q_float
key = q_short , type = Ts,N,V_q_short
key = q_long , type = Tq,N,V_q_long
key = q_longlong , type = Tq,N,V_q_longlong
key = q_point , type = T{Point=ss},N,V_q_point
key = q_id , type = T@,&,N,V_q_id
key = q_delegate , type = T@"<NSObject>",W,N,V_q_delegate
key = q_block , type = T@?,C,N,V_q_block
key = q_model1 , type = T@"Model1",&,N,V_q_model1
key = q_SEL , type = T:,V_q_SEL
key = q_Class , type = T#,&,V_q_Class
key = q_Ivar , type = T^{objc_ivar=},V_q_Ivar
key = q_Method , type = T^{objc_method=},V_q_Method
将type用”,”分开,以T@"NSNumber",N,R,Vname  为例
在类中的声明为 let name: NSNumber =  NSNumber()
  • T@“NSNumber” 标记了属于什么类型
  • N            线程安全 相当与Objective-C中的nonmatic
  • R            不可变,R相当与Objective-C中的readonly,C相当于copy
  • Vname        去掉V,name就是变量名
通过对type进行处理就可以获得属性的类型。从而进行下一步处理。
注意点:class_copyPropertyList返回的仅仅是对象类的属性,class_copyIvarList返回类的所有属性和变量,在swift中如let a: Int? 是无法通过class_copyPropertyList返回的。
 
2.对type的处理使用方法可能不同,但是目的是一样的,就是为了从type字符串中区分出不同的类型
主要分为这几种:对象、协议、block、基本类型、结构体、自定义类型、Class、Ivar、Method、SEL、
 
MJExtension采用这样区分,在MJExtensionConst中这样定义
NSString *const MJPropertyTypeInt = @"i";
NSString *const MJPropertyTypeShort = @"s";
NSString *const MJPropertyTypeFloat = @"f";
NSString *const MJPropertyTypeDouble = @"d";
NSString *const MJPropertyTypeLong = @"l";
NSString *const MJPropertyTypeLongLong = @"q";
NSString *const MJPropertyTypeChar = @"c";
NSString *const MJPropertyTypeBOOL1 = @"c";
NSString *const MJPropertyTypeBOOL2 = @"b";
NSString *const MJPropertyTypePointer = @"*"; NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}";
NSString *const MJPropertyTypeMethod = @"^{objc_method=}";
NSString *const MJPropertyTypeBlock = @"@?";
NSString *const MJPropertyTypeClass = @"#";
NSString *const MJPropertyTypeSEL = @":";
NSString *const MJPropertyTypeId = @"@";
MJExtension采用对type进行字符串处理就能够区分出具体的类型
而在JSONModel采用 NSScanner,对类型进行处理。
比较下,个人觉得采用定义MJExtensionConst这样一个类来存放区分的值显得更加清晰。对于阅读源代码的人来说相对比较好。当然也可以结合起来使用
 
3.对获得的JSON进行类型处理。

  • 在JSON中为NSNumer,而propertytype为NSString,这种情况很常见。我们就需要处理一下,当propertytype为NSString,而在JSON中为NSNumber,就把NSNumber转化为NSString。
  • Readonly不需要赋值
  • nil处理
  • 可变和不可变处理
  • 模型就需要递归处理
  • NSString -> NSURL
  • 字符串转BOOL
  • 还有一些其他处理,以上的处理中也不是每个第三方都进行处理了
截取MJEextension中的一部分代码

  if ([value isKindOfClass:[NSStringclass]]) {
if (propertyClass == [NSURL class]) {
// NSString -> NSURL
// 字符串转码
value = [value mj_url];
} else if (type.isNumberType) {
NSString *oldValue = value; // NSString -> NSNumber
value = [numberFormatter_ numberFromString:oldValue]; // 如果是BOOL
if (type.isBoolType) {
// 字符串转BOOL(字符串没有charValue方法)
// 系统会调用字符串的charValue转为BOOL类型
NSString *lower = [oldValue lowercaseString];
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
value = @YES;
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
value = @NO;
}
}
}
}
4.采用KVC赋值
setValue:forKey:
 就不多说了
 
5.考虑字典键值和模型属性名不一致的情况
比如id、descripition不能作为属性名,当服务器使用时我们就需要另外构造名字;还有服务器一般使用user_name的命名方式,而OC中一般使用驼峰命名法即userName,那么我们就需要建立一对一的对应关系。
 
我想到有三种方法处理:
  • 采用继承,定义model基类,子类重写父类方法。
  • block设置block回调。
  • 可以采用抽象基类。
      这个部分就需要我们好好考虑下了,因为涉及到我们如何提供接口,使用者如何使用的问题。需要考虑到使用是否方便、效率高低、低耦合是否高等问题。
     先来说第一种,采用继承的方法,无疑的会带来高耦合的后果,当然在设计合理的情况下,大部分情况下使用起来更方便。
     第二种采用block的方法。解决了继承带来的高耦合的成本。使用起来也很方便。MJEextension就是用这种方法。但是这种方法往往让我们在调用转化方法时调用block,些一大串的不同键值的转化。个人觉得不同键值的转化写在model里面比较好,而且在封装时,就有意识地引导使用者这么做
  第三种方法使用抽象基类,定义一个协议,协议提供一个或者几个键值转化的方法。当model遵守并实现了此协议,就找到处理方法进行处理。这种作法避免了继承的高耦合的成本,也是使用者在model遵守协议,实现此方法。键值转化的方法写在model里面,避免转化的地方些太多东西。有时候还要写几遍。当然这样也有些缺点,就是每次有需要转化时(而且键值不同的情况很常见)需要引入,然后遵守协议。连续建几个model时,复制起来都不方便。
 
6.其他方面
    (未测试) 对于SEL、Method 、Ivar不能使用KVC,如果考虑这些情况,需要进行判断。
    在赋值时,设置可以忽略的属性
 
7.添加code用于归档
  添加方法
- (id)initWithCoder:(NSCoder *)decoder
- (void)encodeWithCoder:(NSCoder *)encoder
 
一般是给NSObject添加分类,因为我们可以得到类的所有属性和对应的值。在两个方法中遍历属性,进行编码和解码。
方便做些缓存,很多的页面缓存就可以这样做。使用比较方便。
 
8.补充JSON转字典
+ (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
  if (jsonString == nil) {
    return nil;
  }
  NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
  NSError *error;
  NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
   options:NSJSONReadingMutableContainers
   error:&error];
  if(error) {
    NSLog(@"json解析失败:%@",error);
    return nil;
  }
  return dic;
}
 
此方法稍作变换,也可输出数组,主要看json格式。
 
9.字典转JSON
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic
                                 options:NSJSONWritingPrettyPrinted
                                 error:&error];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
10.对处理过的properties一般需要做缓存
     定义一个字典缓存,缓存处理过的properties,重复使用是不需要重复计算,用空间换时间。例如在归档的时候需要获得属性名列表,然后遍历归档、解挡。其他中间操作也需要用到properties。
 
水平有限,错误之处,欢迎大家指正。互相学习。转载请注明出处

最新文章

  1. Elasticsearch+Logstash+Kibana教程
  2. DBA-mysql-字符集
  3. Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
  4. Pandas中DateFrame修改列名
  5. Oracle命令:授权-收回权限-角色
  6. [转]DOS命令
  7. chfn,chsh,last,login,mail ,mesg ,talk,wall,write,nice ,pstree ,renice,skill ,expr ,reset,tset,compress ,lpd ,lpq ,lpr ,lprm,fdformat ,mformat ,mkdosf
  8. “margin塌陷” 嵌套盒子外边距合并现象
  9. python 组合样例
  10. H5利用pattern属性和oninvalid属性验证表单
  11. seafile数据的备份与恢复
  12. IoC之AutoFac(一)——简单使用和组件注册
  13. 什么是pytorch(4.数据集加载和处理)(翻译)
  14. poj 1236(强连通分量分解模板题)
  15. Rpgmakermv(38)MOG_Theatrhythm
  16. Codeforces Round #439 (Div. 2) Problem B (Codeforces 869B)
  17. Hydra密码破译工具
  18. Android Studio之代码提示快捷键冲突设置
  19. 探索MVP(Model-View-Presenter)设计模式在SharePoint平台下的实现
  20. com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/NOTICE

热门文章

  1. 高级 JsRender 模板功能
  2. nodejs个人配置
  3. 用lambda表达式替代 for循环进行批量操作。
  4. 一个oracle存储过程
  5. cf Perfect Pair
  6. Codeforces 573B Bear and Blocks
  7. 深入理解7816(1)---- 关于F/D和etu
  8. STARTUP.A51详解
  9. Delphi事件列表赏析(38个事件,必须要对这些事件非常熟悉,才能如臂使指,才能正确发布到新控件!)
  10. STLtoSVG,and SVG to Bmp