JSONModel源碼閱讀筆記

來源:互聯網
上載者:User

標籤:blog   http   io   ar   os   使用   sp   for   on   

JSONModel是一個解析伺服器返回的Json資料的庫。

 

http://blog.csdn.net/dyllove98/article/details/9050905

通常伺服器傳回的json資料要通過寫一個資料轉換模組將NSDictionary轉換為Model,將NSString資料轉換為Model中property的資料類型。

這樣伺服器如果要做修改,可能需要改兩三個檔案。

JSONModel的出現就是為了將這種解析工作在設計層面完成。

使用方法:參考串連

對其源碼的核心部分JSONModel.m做了源碼閱讀,筆記如下:

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
函數中完成所有解析工作:如果有任何失誤或者錯誤直接返回nil。

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err{    //1、做有效性判斷(dict是不是空啊,dict是不是真是一個NSDictionary)    //check for nil input    if (!dict) {        if (err) *err = [JSONModelError errorInputIsNil];        return nil;    }    //invalid input, just create empty instance    if (![dict isKindOfClass:[NSDictionary class]]) {        if (err) *err = [JSONModelError errorInvalidData];        return nil;    }    //create a class instance    self = [super init];    if (!self) {                //super init didn‘t succeed        if (err) *err = [JSONModelError errorModelIsInvalid];        return nil;    }        //__setup__中通過調用__restrospectProperties建立類屬性的映射表,並且存放在全域變數classProperties裡面    //->__restrospectProperties中利用runtime function搞出屬性列表:    //    ->獲得屬性列表class_copyPropertyList(得到objc_property_t數組)->對於每一個objc_property_t調用property_getName獲得名稱,property_getAttributes獲得屬性的描述(字串)->通過解析字串獲得屬性的類型、是否是Mutable、是否是基本的JSON類型等等    //    ->調用[class superclass]獲得父類繼續擷取列表    //    ->列表儲存在classProperties中備用    //->調用+keyMapper獲得key轉換列表,產生JSONKeyMapper對象存入keyMapper。    //do initial class setup, retrospec properties    [self __setup__];        //看看必傳參數中是否在輸入參數中都有。    //check if all required properties are present    NSArray* incomingKeysArray = [dict allKeys];    NSMutableSet* requiredProperties = [self __requiredPropertyNames];    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];        //get the key mapper    JSONKeyMapper* keyMapper = keyMappers[__className_];        //transform the key names, if neccessary    if (keyMapper) {        //對比dict輸入的keyName匯入NSSet與keyMapper中JSONKeyMapper對象做keyName的轉換。統一轉換為對象的propertyname。        NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];        NSString* transformedName = nil;        //loop over the required properties list        for (NSString* requiredPropertyName in requiredProperties) {            //get the mapped key path            transformedName = keyMapper.modelToJSONKeyBlock(requiredPropertyName);                        //chek if exists and if so, add to incoming keys            if ([dict valueForKeyPath:transformedName]) {                [transformedIncomingKeys addObject: requiredPropertyName];            }        }                //overwrite the raw incoming list with the mapped key names        incomingKeys = transformedIncomingKeys;    }        //利用NSSet的isSubsetOfSet:將必傳參數表與輸入的keyName表對比。如果不是內含項目關聯性說明參數傳的不夠。    //check for missing input keys    if (![requiredProperties isSubsetOfSet:incomingKeys]) {        //get a list of the missing properties        [requiredProperties minusSet:incomingKeys];        //not all required properties are in - invalid input        JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self._className_, requiredProperties);                if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];        return nil;    }        //not needed anymore    incomingKeys= nil;    requiredProperties= nil;        //從對象的classProperties列表中迴圈到dict中取值:(賦值使用KVO操作的setValue:forKey:來做的,這樣會直接調用setter函數賦值)    //loop over the incoming keys and set self‘s properties    for (JSONModelClassProperty* property in [self __properties__]) {        //對於每一個對象的property,通過keyMapper的轉換找到對應dict property的dictKeyPath,找到值jsonValue。如果沒有值,並且這個屬性是Optional的就進行下一項property對比。        //convert key name ot model keys, if a mapper is provided        NSString* jsonKeyPath = property.name;                if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );        //JMLog(@"keyPath: %@", jsonKeyPath);                //general check for data type compliance        id jsonValue = [dict valueForKeyPath: jsonKeyPath];                //check for Optional properties        if (jsonValue==nil && property.isOptional==YES) {            //skip this property, continue with next property            continue;        }                //對找到的值做類型判斷,如果不是JSON應該返回的資料類型就報錯。(注意:NSNull是可以作為參數回傳的)        Class jsonValueClass = [jsonValue class];        BOOL isValueOfAllowedType = NO;                for (Class allowedType in allowedJSONTypes) {            if ( [jsonValueClass isSubclassOfClass: allowedType] ) {                isValueOfAllowedType = YES;                break;            }        }                if (isValueOfAllowedType==NO) {            //type not allowed            JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));            if (err) *err = [JSONModelError errorInvalidData];            return nil;        }                        //check if there‘s matching property in the model        //JSONModelClassProperty* property = classProperties[self.className][key];                //接著對property的屬性與jsonValue進行類型匹配:        if (property) {                        //如果是基本類型(int/float等)直接值拷貝;            // 0) handle primitives            if (property.type == nil && property.structName==nil) {                                //just copy the value                [self setValue:jsonValue forKey: property.name];                                //skip directly to the next key                continue;            }                        //如果是NSNull直接賦空值;            // 0.5) handle nils            if (isNull(jsonValue)) {                [self setValue:nil forKey: property.name];                continue;            }            //如果是值也是一個JsonModel,遞迴搞JsonModel            // 1) check if property is itself a JSONModel            if ([[property.type class] isSubclassOfClass:[JSONModel class]]) {                                //initialize the property‘s model, store it                NSError* initError = nil;                id value = [[property.type alloc] initWithDictionary: jsonValue error:&initError];                if (!value) {                    if (initError && err) *err = [JSONModelError errorInvalidData];                    return nil;                }                [self setValue:value forKey: property.name];                                //for clarity, does the same without continue                continue;                            } else {                                //如果property中有protocol解析將jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass                // 2) check if there‘s a protocol to the property                //  ) might or not be the case there‘s a built in transofrm for it                if (property.protocol) {                                        //JMLog(@"proto: %@", p.protocol);                                        //__transform:forProperty:函數功能:                    //    ->先判斷下protocolClass是否在運行環境中存在,如不存在並且property是NSArray類型,直接報錯。否則,直接返回。                    //    ->如果protocalClass是JsonModel的子類,                    //    ->如果property.type是NSArray                    //        ->判斷一下是否是使用時轉換                    //        ->如果為使用時轉換則輸出一個JSONModelArray(NSArray)的子類                    //        ->如果不是使用時轉換則輸出一個NSArray,其中的對象全部轉換為protocalClass所對應對象                    //    ->如果property.type是NSDictionary                    //        ->將value轉換為protocalClass所對應對象                    //        ->根據key儲存到一個NSDictionary中輸出                    jsonValue = [self __transform:jsonValue forProperty:property];                    if (!jsonValue) {                        if (err) *err = [JSONModelError errorInvalidData];                        return nil;                    }                }                                //如果是基本JSON類型(NSString/NSNumber)                // 3.1) handle matching standard JSON types                if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {                                        //如果是mutable的,做一份MutableCopy                    //mutable properties                    if (property.isMutable) {                        jsonValue = [jsonValue mutableCopy];                    }                                        //set the property value                    [self setValue:jsonValue forKey: property.name];                    continue;                }                                //如果property.type是NSArray                // 3.3) handle values to transform                if (                    //如果(類型沒有匹配,並且jsonValue不為空白)或者是Mutable的property(說明是特殊類型轉換)                    (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))                    ||                    //the property is mutable                    property.isMutable                    ) {                                        //利用JSONValueTransformer找到源類型                    // searched around the web how to do this better                    // but did not find any solution, maybe that‘s the best idea? (hardly)                    Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];                                        //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);                                        //用字串拼出轉換函式的名稱字串,到JSONValueTransformer中去搜尋@SEL執行出正確類型                    //build a method selector for the property and json object classes                    NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",                                              (property.structName? property.structName : property.type), //target name                                              sourceClass]; //source name                    SEL selector = NSSelectorFromString(selectorName);                                        //check if there‘s a transformer with that name                    if ([valueTransformer respondsToSelector:selector]) {                                                //it‘s OK, believe me...#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"                        //transform the value                        jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];#pragma clang diagnostic pop                                                [self setValue:jsonValue forKey: property.name];                                            } else {                                                // it‘s not a JSON data type, and there‘s no transformer for it                        // if property type is not supported - that‘s a programmer mistaked -> exception                        @throw [NSException exceptionWithName:@"Type not allowed"                                                       reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]                                                     userInfo:nil];                        return nil;                    }                                    } else {                    //哪兒都不是的直接存起來                    // 3.4) handle "all other" cases (if any)                    [self setValue:jsonValue forKey: property.name];                }            }        }    }        //最後調用validate:看看結果是不是有效,沒問題就返回了。    //run any custom model validation    NSError* validationError = nil;    BOOL doesModelDataValidate = [self validate:&validationError];        if (doesModelDataValidate == NO) {        if (err) *err = validationError;        return nil;    }        //model is valid! yay!    return self;}

程式亮點:
1、為了提高效率通過static的NSArray和NSDictionary進行解耦。
2、JSONValueTransformer實現了一個可複用的類型轉換模板。
3、通過runtime function解析出property列表,通過property相關函數解析出名稱,和Attributes的資訊。
4、NSScanner的使用
5、NSSet的內含項目關聯性判斷兩個集合的交集
6、利用[NSObject setValue:forKey:]的KVO操作賦值,可以直接調用setter函數,並且可以賦nil到property中
7、適時給子類一個函數可以修改父類的一些行為

JSONModel源碼閱讀筆記

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.