標籤:http io ar os 使用 sp for java strong
從JSONModel看Objective-C的反射機制前言
移動互聯時代,JSON作為一種資料轉送格式幾乎隨處可見。作為iOS開發人員,收到一串JSON字串要怎麼處理?我想多數情況下是需要將它轉成自訂的NSObject對象再使用,對於這個轉換的過程,大部分人是這麼做的:
1234567 |
NSDictionary* json = (fetch from Internet) ...User* user=[[User alloc] init];user.userId =[json objectForKey:@"userId"];user.nick= [json objectForKey:@"nick"];user.image = [json objectForKey:@"image"];user.age = [json objectForKey:@"age"];... |
這樣的代碼沒錯,但也絕對說不上優雅,model裡面的屬性越多,冗餘的代碼量也相應越多。對於這個問題,自然是有更好的解決方案,比如說這樣:
12 |
NSError* err = nil;User* user = [[User alloc] initWithDictionary:json error:&err]; |
兩行代碼足矣,當然,實現這個功能,實際上是把很多背後的工作交給JSONModel這個開源包去做了。至於其實現原理,則主要是基於Objective-C Runtime的反射機制。
關於反射
《Thinking in Java》中將反射稱解釋為運行時的類資訊,說白了就是這個類資訊在編譯的時候是未知的,需要在程式運行期間動態擷取,而這正是我們之前試圖去解決的問題。對於從網路上擷取到的一段JSON字串,在代碼編譯期間當然是無法知曉的。雖然這裡說的是Java語言,但是對於Objective-C,這種反射機制也是同樣支援的。
JSONModel中的實現
打斷點記錄了下JSONModel這個類中的方法調用順序如下:
對象屬性的擷取則主要在最後一個inspectProperties方法
以下是inspectProperties方法的程式碼片段:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
···//inspects the class, get‘s a list of the class properties-(void)__inspectProperties{ //JMLog(@"Inspect class: %@", [self class]); NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary]; //temp variables for the loops Class class = [self class]; NSScanner* scanner = nil; NSString* propertyType = nil; // inspect inherited properties up to the JSONModel class while (class != [JSONModel class]) { //JMLog(@"inspecting: %@", NSStringFromClass(class)); unsigned int propertyCount; objc_property_t *properties = class_copyPropertyList(class, &propertyCount); //loop over the class properties for (unsigned int i = 0; i < propertyCount; i++) { JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init]; //get property name objc_property_t property = properties[i]; const char *propertyName = property_getName(property); p.name = [NSString stringWithUTF8String:propertyName]; //JMLog(@"property: %@", p.name); //get property attributes const char *attrs = property_getAttributes(property); NSString* propertyAttributes = [NSString stringWithUTF8String:attrs]; if ([propertyAttributes hasPrefix:@"Tc,"]) { //mask BOOLs as structs so they can have custom convertors p.structName = @"BOOL"; } scanner = [NSScanner scannerWithString: propertyAttributes]; //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]); [scanner scanUpToString:@"T" intoString: nil]; [scanner scanString:@"T" intoString:nil];···//finally store the property index in the static property indexobjc_setAssociatedObject(self.class, &kClassPropertiesKey, [propertyIndex copy], OBJC_ASSOCIATION_RETAIN // This is atomic ); |
在這邊可以看到基本步驟如下
- 通過調用自身的class方法擷取當前類的中繼資料資訊
- 通過runtime的 class_copyPropertyList 方法取得當前類的屬性列表,以指標數組的形式返回
- 遍曆指標數組,通過property_getName擷取屬性名稱,property_getAttributes擷取屬性類型
- 使用NSScanner來掃描屬性類型字串,將類似如下的形式"[email protected]"NSNumber",&,N,V_id",處理成NSNumber,逐個屬性迴圈處理
- 將所有處理好的資料放入propertyIndex這個字典中
- 通過objc_setAssociatedObject將這些資料關聯到kClassPropertiesKey
使用時在properties方法中這樣取出屬性資料:
1234567891011121314 |
//returns a list of the model‘s properties-(NSArray*)__properties__{ //fetch the associated object NSDictionary* classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey); if (classProperties) return [classProperties allValues]; //if here, the class needs to inspect itself [self __setup__]; //return the property list classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey); return [classProperties allValues];} |
以上就是JSONModel中使用反射機制實現的類屬性擷取過程,相比常見的逐個取值賦值的方式,這種方法在代碼上的確簡潔優雅了很多,特別是對於使用的類屬性比較複雜的情況,免除了很多不必要的代碼。
Objective-C的反射機制