(20170207)開源第三方學習之JSONModel

来源:http://www.cnblogs.com/wujy/archive/2017/02/07/6373737.html
-Advertisement-
Play Games

1:倉庫地址:https://github.com/jsonmodel/jsonmodel 主要作用是把JSON字元串轉成Model實體,也可以把實體轉化成JSON字元串;還包含一些轉字典的內容;JOSN字元串通過AFNetworking轉化成字典,然後再能過字典跟實體進行轉換; 2:原理實現: 主 ...


1:倉庫地址:https://github.com/jsonmodel/jsonmodel
  主要作用是把JSON字元串轉成Model實體,也可以把實體轉化成JSON字元串;還包含一些轉字典的內容;JOSN字元串通過AFNetworking轉化成字典,然後再能過字典跟實體進行轉換;   2:原理實現:   主要介紹關於實體對象轉換地原理,其它不在這個裡面進行介紹;其主要還是運用運用時的機制,對屬性進行獲取換進行處理; 
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
    //check for nil input
    //1.為空判斷
    if (!dict) {
        if (err) *err = [JSONModelError errorInputIsNil];
        return nil;
    }

    //invalid input, just create empty instance
    //2.類型判斷
    if (![dict isKindOfClass:[NSDictionary class]]) {
        if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
        return nil;
    }

    //create a class instance
    //3.核心,初始化映射property
    self = [self init];
    if (!self) {

        //super init didn't succeed
        if (err) *err = [JSONModelError errorModelIsInvalid];
        return nil;
    }

    //check incoming data structure
    //4.檢查映射結構是否能夠從dictionary中找到相應的數據
    if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
        return nil;
    }

    //import the data from a dictionary
    //5.進行數據賦值
    if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
        return nil;
    }

    //run any custom model validation
    //6.本地數據檢查
    if (![self validate:err]) {
        return nil;
    }

    //model is valid! yay!
    return self;
}

2.1:如何獲得屬性相關修飾符?採用運行時的機制,獲取實體中的屬性,並通過屬性獲取對應的修飾符;不同的字元跟符號代表不一樣的修飾符;   代碼: 
        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 = @(propertyName);

            //JMLog(@"property: %@", p.name);

            //get property attributes
            const char *attrs = property_getAttributes(property);
            NSString* propertyAttributes = @(attrs);
            NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@",”];
            …….
}

property_getAttributes函數返回objc_property_attribute_t結構體列表,objc_property_attribute_t結構體包含name和value,常用的屬性如下:   屬性類型  name值:T  value:變化 編碼類型  name值:C(copy) &(strong) W(weak)空(assign) 等 value:無 非/原子性 name值:空(atomic) N(Nonatomic)  value:無 變數名稱  name值:V  value:變化     使用property_getAttributes獲得的描述是property_copyAttributeList能獲取到的所有的name和value的總體描述,如 T@"NSDictionary",C,N,V_dict1
  2.2 通過NSScanner用於在字元串中掃描指定的字元,尤其是把它們翻譯/轉換為數字和別的字元串。可以在創建NSScaner時指定它的string屬性,然後scanner會按照你的要求從頭到尾地掃描這個字元串的每個字元。 
NSScanner* scanner = nil;
            scanner = [NSScanner scannerWithString: propertyAttributes];

            //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
            [scanner scanUpToString:@"T" intoString: nil];
            [scanner scanString:@"T" intoString:nil];

            //check if the property is an instance of a class
            if ([scanner scanString:@"@\"" intoString: &propertyType]) {

                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
                                        intoString:&propertyType];

                //JMLog(@"type: %@", propertyClassName);
                p.type = NSClassFromString(propertyType);
                p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
                p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];

                //read through the property protocols
                while ([scanner scanString:@"<" intoString:NULL]) {

                    NSString* protocolName = nil;

                    [scanner scanUpToString:@">" intoString: &protocolName];

                    if ([protocolName isEqualToString:@"Optional"]) {
                        p.isOptional = YES;
                    } else if([protocolName isEqualToString:@"Index"]) {
                        p.isIndex = YES;
                        objc_setAssociatedObject(
                                                 self.class,
                                                 &kIndexPropertyNameKey,
                                                 p.name,
                                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                                 );
                    } else if([protocolName isEqualToString:@"ConvertOnDemand"]) {
                        p.convertsOnDemand = YES;
                    } else if([protocolName isEqualToString:@"Ignore"]) {
                        p = nil;
                    } else {
                        p.protocol = protocolName;
                    }

                    [scanner scanString:@">" intoString:NULL];
                }

            }
            //check if the property is a structure
            else if ([scanner scanString:@"{" intoString: &propertyType]) {
                [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
                                    intoString:&propertyType];

                p.isStandardJSONType = NO;
                p.structName = propertyType;

            }
            //the property must be a primitive
            else {

                //the property contains a primitive data type
                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
                                        intoString:&propertyType];

                //get the full name of the primitive type
                propertyType = valueTransformer.primitivesNames[propertyType];

                if (![allowedPrimitiveTypes containsObject:propertyType]) {

                    //type not allowed - programmer mistaked -> exception
                    @throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
                                                   reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
                                                 userInfo:nil];
                }

            }

然後對符號的字元串進行解析分析,把相應的字元串獲取到進行判斷處理;對於有增加相應的屬性進行操作,如忽略、可為空等,如是block修飾的也直接置空屬性,不對它進行轉換;代碼如下:

NSString *nsPropertyName = @(propertyName);
            if([[self class] propertyIsOptional:nsPropertyName]){
                p.isOptional = YES;
            }

            if([[self class] propertyIsIgnored:nsPropertyName]){
                p = nil;
            }

            //few cases where JSONModel will ignore properties automatically
            if ([propertyType isEqualToString:@"Block"]) {
                p = nil;
            }

2.3 JSONModel關於keyMapper修改對應的屬性對應名稱   下麵這段平時我們用於替換Model跟JSON字元串不一樣的地方,重新給它命名一個屬性新名字;   
+(JSONKeyMapper*)keyMapper
{
    return [[JSONKeyMapper alloc] initWithDictionary:@{
                                                       @"new_password": @"my_new_password",
                                                       }];
}

其實是在JSONModel初始化過程中有對keyMapper進行一個判斷處理;判斷當前的實體對象中是否有這個方法,如果有且還沒有被映射緩存過,則對它進行處理;

    id mapper = [[self class] keyMapper];
    if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
        objc_setAssociatedObject(
                                 self.class,
                                 &kMapperObjectKey,
                                 mapper,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }

其中關於&kClassPropertiesKey, &kMapperObjectKey的運用說明,是映射緩存的標識;

  //使用AssociateObject進行映射property的緩存,判斷是否映射過
    if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
        [self __inspectProperties];
    }

//同樣使用AssociateObject進行映射property的緩存
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
}

寫入標識的操作如下:

    objc_setAssociatedObject(
                             self.class,
                             &kClassPropertiesKey,
                             [propertyIndex copy],
                             OBJC_ASSOCIATION_RETAIN // This is atomic
                             );

2.4 關於健值是否缺少判斷   獲取到當前實體的屬性值,對於那些可空的就不進行判斷,也就沒有放在下麵這個NSMutableSet裡面 
-(NSMutableSet*)__requiredPropertyNames
{
    //fetch the associated property names
    NSMutableSet* classRequiredPropertyNames = objc_getAssociatedObject(self.class, &kClassRequiredPropertyNamesKey);

    if (!classRequiredPropertyNames) {
        classRequiredPropertyNames = [NSMutableSet set];
        [[self __properties__] enumerateObjectsUsingBlock:^(JSONModelClassProperty* p, NSUInteger idx, BOOL *stop) {
            if (!p.isOptional) [classRequiredPropertyNames addObject:p.name];
        }];

        //persist the list
        objc_setAssociatedObject(
                                 self.class,
                                 &kClassRequiredPropertyNamesKey,
                                 classRequiredPropertyNames,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
    return classRequiredPropertyNames;
}

這邊要相容除了dic字典裡面的屬性還要處理如果有KeyMapper的替換屬性;然後進行統一判斷;

-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{
    //check if all required properties are present
    NSArray* incomingKeysArray = [dict allKeys];
    NSMutableSet* requiredProperties = [self __requiredPropertyNames];
    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];

    //transform the key names, if neccessary
    if (keyMapper || globalKeyMapper) {

        NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
        NSString* transformedName = nil;

        //loop over the required properties list
        for (JSONModelClassProperty* property in [self __properties__]) {

            transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;

            //chek if exists and if so, add to incoming keys
            id value;
            @try {
                value = [dict valueForKeyPath:transformedName];
            }
            @catch (NSException *exception) {
                value = dict[transformedName];
            }

            if (value) {
                [transformedIncomingKeys addObject: property.name];
            }
        }

        //overwrite the raw incoming list with the mapped key names
        incomingKeys = transformedIncomingKeys;
    }

    //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.class, requiredProperties);

        if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
        return NO;
    }

    //not needed anymore
    incomingKeys= nil;
    requiredProperties= nil;

    return YES;
}

2.5 開始進行賦值操作   首先獲取到對應的屬性列表數組 
-(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];
}

然後主要進行屬性賦值

-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
    //loop over the incoming keys and set self's properties
    //迴圈遍歷映射出來的JSONModelClassProperty結構體
    for (JSONModelClassProperty* property in [self __properties__]) {

        //convert key name ot model keys, if a mapper is provided
        //keyMapper映射,獲取鎮真正的值
        NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
        //JMLog(@"keyPath: %@", jsonKeyPath);

        //general check for data type compliance
        id jsonValue;
        @try {
            jsonValue = [dict valueForKeyPath: jsonKeyPath];
        }
        @catch (NSException *exception) {
            jsonValue = dict[jsonKeyPath];
        }

        //check for Optional properties
        if (isNull(jsonValue)) {
            //skip this property, continue with next property
            if (property.isOptional || !validation) continue;

            if (err) {
                //null value for required property
                NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            return NO;
        }

        Class jsonValueClass = [jsonValue class];
        BOOL isValueOfAllowedType = NO;

        //判斷數據輸入類型是不是允許的json類型
        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) {
                NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            return NO;
        }

        //check if there's matching property in the model
        if (property) {

            // check for custom setter, than the model doesn't need to do any guessing
            // how to read the property's value from JSON
            // 使用對象相應的setter方法進行set
            if ([self __customSetValue:jsonValue forProperty:property]) {
                //skip to next JSON key
                continue;
            };

            // 0) handle primitives
            // 代表基礎類型,比如int float等,直接使用kvc賦值
            if (property.type == nil && property.structName==nil) {

                //generic setter
                if (jsonValue != [self valueForKey:property.name]) {
                    [self setValue:jsonValue forKey: property.name];
                }

                //skip directly to the next key
                continue;
            }

            // 0.5) handle nils
            if (isNull(jsonValue)) {
                if ([self valueForKey:property.name] != nil) {
                    [self setValue:nil forKey: property.name];
                }
                continue;
            }


            // 1) check if property is itself a JSONModel
            // 判斷子結構是否是一個JSONModel結構,進行遞歸遍歷,先將子結構遍歷完並賦值完成
            if ([self __isJSONModelSubClass:property.type]) {

                //initialize the property's model, store it
                JSONModelError* initErr = nil;
                id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];

                if (!value) {
                    //skip this property, continue with next property
                    if (property.isOptional || !validation) continue;

                    // Propagate the error, including the property name as the key-path component
                    if((err != nil) && (initErr != nil))
                    {
                        *err = [initErr errorByPrependingKeyPathComponent:property.name];
                    }
                    return NO;
                }
                if (![value isEqual:[self valueForKey:property.name]]) {
                    [self setValue:value forKey: property.name];
                }

                //for clarity, does the same without continue
                continue;

            } else {

                // 2) check if there's a protocol to the property
                //  ) might or not be the case there's a built in transform for it
                // 是否包含protocol的欄位,該欄位主要用來表明array或者dictionary中的對象類型
                if (property.protocol) {

                    //JMLog(@"proto: %@", p.protocol);
                    //迴圈遍歷子內容,將對應的類型賦給相應的array或者dictionary
                    jsonValue = [self __transform:jsonValue forProperty:property error:err];
                    if (!jsonValue) {
                        if ((err != nil) && (*err == nil)) {
                            NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property];
                            JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                            *err = [dataErr errorByPrependingKeyPathComponent:property.name];
                        }
                        return NO;
                    }
                }

                // 3.1) handle matching standard JSON types
                // 判斷標準的json類型,比如nsstring等
                if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {

                    //mutable properties
                    if (property.isMutable) {
                        jsonValue = [jsonValue mutableCopy];
                    }

                    //set the property value
                    if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                        [self setValue:jsonValue forKey: property.name];
                    }
                    continue;
                }

                // 3.3) handle values to transform
                // 其他處理情況,主要是一些類型轉換的情況,比如nsstring轉換為nsurl等
                if (
                    (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
                    ||
                    //the property is mutable
                    property.isMutable
                    ||
                    //custom struct property
                    property.structName
                    ) {

                    // searched around the web how to do this better
                    // but did not find any solution, maybe that's the best idea? (hardly)
                    // 獲取真實的json數據類型
                    Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];

                    //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);

                    //build a method selector for the property and json object classes
                    // 通過property類型和json數據類型進行轉換的判斷
                    NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
                                              (property.structName? property.structName : property.type), //target name
                                              sourceClass]; //source name
                    SEL selector = NSSelectorFromString(selectorName);

                    //check for custom transformer
                    //是否有本地轉換的方法
                    BOOL foundCustomTransformer = NO;
                    if ([valueTransformer respondsToSelector:selector]) {
                        foundCustomTransformer = YES;
                    } else {
                        //try for hidden custom transformer
                        selectorName = [NSString stringWithFormat:@"__%@",selectorName];
                        selector = NSSelectorFromString(selectorName);
                        if ([valueTransformer respondsToSelector:selector]) {
                            foundCustomTransformer = YES;
                        }
                    }

                    //check if there's a transformer with that name
                    if (foundCustomTransformer) {

                        //it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                        //transform the value
                        // 通過 JSONValueTransformer 進行類型轉換
                        jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
#pragma clang diagnostic pop

                        if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                            [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 mistake -> exception
                        @throw [NSException exceptionWithName:@"Type not allowed"
                                                       reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]
                                                     userInfo:nil];
                        return NO;
                    }

                } else {
                    // 3.4) handle "all other" cases (if any)
                    if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                        [self setValue:jsonValue forKey: property.name];
                    }
                }
            }
        }
    }

    return YES;
}

其中獲取到值對應的類型是否在支持的轉化類型中

allowedJSONTypes = @[
                [NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class], //immutable JSON classes
                [NSMutableString class], [NSMutableArray class], [NSMutableDictionary class] //mutable JSON classes
            ];

            allowedPrimitiveTypes = @[
                @"BOOL", @"float", @"int", @"long", @"double", @"short",
                //and some famous aliases
                @"NSInteger", @"NSUInteger",
                @"Block"
            ];

然後上面的代碼用於下麵進行判斷

Class jsonValueClass = [jsonValue class];
        BOOL isValueOfAllowedType = NO;

        for (Class allowedType in allowedJSONTypes) {
            if ( [jsonValueClass isSubclassOfClass: allowedType] ) {
                isValueOfAllowedType = YES;
                break;
            }
        }

2.6 定義空的協議用於區分一些屬性標識

@protocol Ignore
@end

/**
* Protocol for defining optional properties in a JSON Model class. Use like below to define
* model properties that are not required to have values in the JSON input:
*
* @property (strong, nonatomic) NSString&lt;Optional&gt;* propertyName;
*
*/
@protocol Optional
@end

/**
* Protocol for defining index properties in a JSON Model class. Use like below to define
* model properties that are considered the Model's identifier (id).
*
* @property (strong, nonatomic) NSString&lt;Index&gt;* propertyName;
*
*/
@protocol Index
@end

//使所有對象可選相容,避免編譯器警告
@interface NSObject(JSONModelPropertyCompatibility)<Optional, Index, Ignore>
@end

特別是Optional為可空的標識例如我們給一個屬性設置可空時 @property (nonatomic, copy) NSString<Optional> *current_status;,然後在上面獲取屬性的字元串中關於是否存在這個標識字元串,做一些屬性的配置;它是被包含在<>裡面;

while ([scanner scanString:@"<" intoString:NULL]) {

                    NSString* protocolName = nil;

                    [scanner scanUpToString:@">" intoString: &protocolName];

                    if ([protocolName isEqualToString:@"Optional"]) {
                        p.isOptional = YES;
                    } else if([protocolName isEqualToString:@"Index"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
                        p.isIndex = YES;
#pragma GCC diagnostic pop

                        objc_setAssociatedObject(
                                                 self.class,
                                                 &kIndexPropertyNameKey,
                                                 p.name,
                                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                                 );
                    } else if([protocolName isEqualToString:@"Ignore"]) {
                        p = nil;
                    } else {
                        p.protocol = protocolName;
                    }

                    [scanner scanString:@">" intoString:NULL];
                }

            }

 3:關於屬性類型的獲取實例

定義一個類:

@interface MPAllModel : JSONModel

@property(nonatomic,copy)NSString *name;
@property(nonatomic,strong)NSString *userName;
@property(nonatomic)int age;
@property(nonatomic)float price;
@property(nonatomic,strong)NSNumber *isShow;
@property(nonatomic,assign)BOOL isError;
@property(nonatomic,strong)NSArray *itemArray;
@property(nonatomic,strong)NSArray<Optional> *dataArray;
@end

然後利用運行時的代碼,獲得到這個類對應屬性的字元串內容:

    unsigned int outCount;
    int i;
    objc_property_t *property = class_copyPropertyList([MPAllModel class], &outCount);
    for (i = outCount -1; i >= 0 ; i--) {
        NSString *getPropertyName = [NSString stringWithCString:property_getName(property[i]) encoding:NSUTF8StringEncoding];
        NSLog(@"getPropertyName:        %@",getPropertyName);
        NSString *getPropertyNameString = [NSString stringWithCString:property_getAttributes(property[i]) encoding:NSUTF8StringEncoding];
        NSLog(@"getPropertyNameString:  %@",getPropertyNameString);
    }

得到下麵對應的類型:

getPropertyName:        dataArray
getPropertyNameString:  T@"NSArray<Optional>",&,N,V_dataArray

getPropertyName:        itemArray
getPropertyNameString:  T@"NSArray",&,N,V_itemArray

getPropertyName:        isError
getPropertyNameString:  TB,N,V_isError

getPropertyName:        isShow
getPropertyNameString:  T@"NSNumber",&,N,V_isShow

getPropertyName:        price
getPropertyNameString:  Tf,N,V_price

getPropertyName:        age
getPropertyNameString:  Ti,N,V_age

getPropertyName:        userName
getPropertyNameString:  T@"NSString",&,N,V_userName

getPropertyName:        name
getPropertyNameString:  T@"NSString",C,N,V_name

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Jcrop 是一個功能強大的 jQuery 圖像裁剪插件,結合後端程式(例如:PHP)可以快速的實現圖片裁剪的功能。 版本: jQuery v1.5.1+ jQuery Jcrop v0.9.12 github 線上實例 實例預覽 jQuery Jcrop 圖像裁剪插件 Hello World 基礎 ...
  • 今天整理筆記,發現在學習javaScript的過程中,遇到過一個在當時看來很棘手的問題,現在特地總結一下,也希望能幫助到曾像我一樣迷惘的初學者。 我還是以一個案例來說明問題,html代碼如下: css代碼如下: js代碼如下: 稍微懂點js的人都知道當我點擊p時,基於事件冒泡機制,會觸發父元素div ...
  • 很久以前,我們在"細說gulp"隨筆中,以壓縮JavaScript為例,詳細地講解瞭如何利用gulp來完成前端自動化。再來短暫回顧下,當時除了藉助gulp之外,我們還利用了第三方gulp插件”gulp-uglify”,來達到壓縮JavaScript文件的目的。今兒,我們的重點就是,自己也來實現一個g... ...
  • 1、 冒泡排序,將數組從小到大排序,即從數組左邊開始相鄰兩個元素比較大小和交換位置。 <script> // 冒泡排序,從小到大。 var arr = [41, 21, 12, 52, 67, 32, 23]; var outer = 0, inner = 0; for (var j = 0; j ...
  • 這個問題很傻很天真,如何正確的添加圖像資源 我有一個名為 back.png 的圖片,直接拷貝到 Finder 的 project 目錄下了。 用如下的語句: roleImageView.image = UIImage(named: "back") 顯示不了圖片。 其實不是語句有問題,是添加圖像的方法 ...
  • 當你在一個Xcode版本上編輯Storyboard並儲存後(比如 8.1)在另一個版本上(比如8.2.1)打開想繼續編輯的時候,有時候會無法打開Storyboard。 所以兩人合作編寫一個 Xcode project 的時候,一定要使用相同的版本。不然以後麻煩多多,會出現很多莫名其妙的問題。 有兩個 ...
  • AToolsActivity.java shake.xml AddressDao.java ...
  • 科技的仿生學無處不在,給予我們啟發。為了延長電池是使用壽命,google從蛇的冬眠中得到體會,那就是在某種情況下也讓手機進入類冬眠的情況,從而引入了今天的主題,Doze模式,Doze中文是打盹兒,打盹當然比活動節約能量了。 手機打盹兒的時候會怎樣呢? 按照google的官方說法,Walklocks, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...