一、思路:
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。
水平有限,錯誤之處,歡迎大家指正。互相學習。轉載請注明出處