美文网首页
KVC的使用

KVC的使用

作者: heart_领 | 来源:发表于2018-09-17 16:02 被阅读11次

    一、作用
    1.能够对对象的私有成员进行取值赋值
    2.对数值和结构体型的属性进行的打包解包处理
    二、赋值
    赋值过程:
    1.先找相关方法 set<Key>:, _set<Key>:, setIs<Key>:

    1. 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly,判断是否可以直接方法成员变量
    2. 如果是判断是NO,直接执行KVC的setValue:forUndefinedKey:(系统抛出一个异
      常,未定义key)
    3. 如果是YES,继续找相关变量_<key> 􏱞_is<Key>􏱞 <key> 􏱞is<Key>
    4. 方法或成员都不存在,setValue:
      forUndefinedKey:方法,默认是抛出异常
      自定义KVC存值
    - (void)tz_setValue:(nullable id)value forKey:(NSString *)key {
        // 判断是否合法
        if (key == nil && key.length == 0) {
            return;
        }
        // Key
        NSString* Key = key.capitalizedString;//首字母大写
        /// 先找相关方法
        //set<Key>:, _set<Key>:, setIs<Key>:
        NSString* setKey = [NSString stringWithFormat:@"set%@:", Key];
        if ([self respondsToSelector:NSSelectorFromString(setKey)]) {
         //   [self performSelector:NSSelectorFromString(setKey) withObject:value];
           //  获取方法
            Method method = class_getInstanceMethod([self class], NSSelectorFromString(setKey));//实例方法
    //        class_getClassMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>)//类方法
            // 获取参数类型  objc_msgSend(id sel arg)
            char *type = method_copyArgumentType(method,2);
            // v@:i  short long
            // 判断类型
            if (strcmp(type, "i") == 0) {
                NSNumber* num = (NSNumber*)value;
                objc_msgSend(self, NSSelectorFromString(setKey), num.intValue);
            } else {
                objc_msgSend(self, NSSelectorFromString(setKey), value);
            }
    
            free(type);
            return;
        }
        
        NSString* _setKey = [NSString stringWithFormat:@"_set%@:", Key];
        if ([self respondsToSelector:NSSelectorFromString(_setKey)]) {
            [self performSelector:NSSelectorFromString(_setKey) withObject:value];
            return;
        }
        
        NSString* setIsKey = [NSString stringWithFormat:@"setIs%@:", Key];
        if ([self respondsToSelector:NSSelectorFromString(setIsKey)]) {
            [self performSelector:NSSelectorFromString(setIsKey) withObject:value];
            return;
        }
        
        if (![self.class accessInstanceVariablesDirectly]) {
            NSException* exception = [NSException exceptionWithName:@"NSUnkonwnKeyException" reason:@"setValue:forUndefineKey" userInfo:nil];
            @throw exception;
        }
        
        /// 再找相关变量
        /// 获取所以成员变量
        unsigned int count = 0;
        Ivar* ivars = class_copyIvarList([self class], &count);
        
        NSMutableArray* arr = [[NSMutableArray alloc] init];
        
        for (int i = 0; i < count; i++) {
            Ivar var = ivars[i];
            const char* varName = ivar_getName(var);
            NSString* name = [NSString stringWithUTF8String:varName];
            [arr addObject:name];
        }
        
        // _<key> _is<Key> <key> is<Key>
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@", key]]) {
                object_setIvar(self, ivars[i], value);
                free(ivars);
                return;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@", Key]]) {
                object_setIvar(self, ivars[i], value);
                free(ivars);
                return;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"%@", key]]) {
                object_setIvar(self, ivars[i], value);
                free(ivars);
                return;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@", Key]]) {
                object_setIvar(self, ivars[i], value);
                free(ivars);
                return;
            }
        }
        
        [self setValue:value forUndefinedKey:key];
        free(ivars);
        
    }
    

    三、取值
    取值过程:
    1.先找相关方法 get<Key>, key

    1. 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly,判断是否可以直接方法成员变量
    2. 如果是判断是NO,直接执行KVC的valueForUndefinedKey:(系统抛出一个异
      常,未定义key)
    3. 如果是YES,继续找相关变量_<key> 􏱞_is<Key>􏱞 <key> 􏱞is<Key>
    4. 方法或成员都不存在,valueForUndefinedKey:方法,默认是抛出异常
    - (nullable id)tz_valueForKey:(NSString *)key {
        // 判断是否合法
        if (key == nil && key.length == 0) {
            return nil;
        }
        // Key
        NSString* Key = key.capitalizedString;
        
        /// 再找相关变量
        /// 获取所以成员变量
        unsigned int count = 0;
        Ivar* ivars = class_copyIvarList([self class], &count);
        
        NSMutableArray* arr = [[NSMutableArray alloc] init];
        
        for (int i = 0; i < count; i++) {
            Ivar var = ivars[i];
            const char* varName = ivar_getName(var);
            NSString* name = [NSString stringWithUTF8String:varName];
            [arr addObject:name];
        }
        
        // _<key> _is<Key> <key> is<Key>
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@", key]]) {
    //           id value = object_getIvar(self, ivars[i]);
                const char * type = ivar_getTypeEncoding(ivars[i]);
                if (strcmp(type, "i")==0) {
                    int v = ((int (*)(id, Ivar))object_getIvar)(self, ivars[i]);
                    free(ivars);
                    return [NSNumber numberWithInt:v];
                }
                id value = object_getIvar(self, ivars[i]);
                free(ivars);
                return value;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@", Key]]) {
                id value = object_getIvar(self, ivars[i]);
                free(ivars);
                return value;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"%@", key]]) {
                id value = object_getIvar(self, ivars[i]);
                free(ivars);
                return value;
            }
        }
        
        for (int i = 0; i < count; i++) {
            NSString* keyName = arr[i];
            if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@", Key]]) {
                id value = object_getIvar(self, ivars[i]);
                free(ivars);
                return value;
            }
        }
        
        [self valueForUndefinedKey:key];
        free(ivars);
        
        return nil;
    }
    

    四、KVC异常处理及正确性验证
    异常处理:
    1.赋值为空 setNilValueForKey:
    2.Key值不存在 setValue:forUndefinedKey
    异常处理

    // 对非对象类型,值不能为空
    - (void) setNilValueForKey:(NSString *)key {
        NSLog(@"%@ 值不能为空", key);
    }
    // 赋值key值不存在
    - (void) setValue:(id)value forUndefinedKey:(NSString *)key {
        NSLog(@"key = %@值不存在 ", key);
        
    }
    // 取值key值不存在
    - (id) valueForUndefinedKey:(NSString *)key {
        NSLog(@"key=%@不存在", key);
        return nil;
    }
    

    正确性验证:
    validateValue
    该方法的工作原理:

    1. 先找一下你的类中是否实现了方法
      -(BOOL)validate<Key>:error:
    2. 如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES,如果没有实现这个方法,则系统默认返回就是YES
    TZPerson* p = [TZPerson new];
        NSNumber* value = @200;
        if ([p validateValue:&value forKey:@"age" error:NULL]) {
            [p setValue:value forKey:@"age"];
        }
    @interface TZPerson : NSObject//.h
    
    @end
    
    @implementation TZPerson//.m
    - (BOOL) validateAge:(inout id  _Nullable __autoreleasing *)ioValue  error:(out NSError * _Nullable __autoreleasing *)outError {
        NSNumber* value = (NSNumber*)*ioValue;
        NSLog(@"%@", value);
    //    if (value <= 0 || value >= 200) {
    //        return NO;
    //    }
        return YES;
    }
    
    @end
    
    

    五、用法

    1. KVC与字典
    /// KVC字典操作
    - (void) dictionaryTest {
        TZPerson* p = [TZPerson new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@18,
                               @"nick":@"Cat",
                               @"height":@180,
                               @"dd":@"helo"
                               };
        
        [p setValuesForKeysWithDictionary:dict];//字典转模型
        NSLog(@"p.name = %@, p.age = %d, p.nick =%@, p.height = %f", p.name, p.age, p.nick, p.height);
        NSArray* keys = @[@"name", @"age"];
        NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
        NSLog(@"%@", dict1);
        /**
         输出:
         {
         age = 18;
         name = Tom;
         }
         */
    }
    
    1. KVC的消息传递
    /// KVC消息传递  array
    - (void) arrayKVCTest {
        NSArray* arr = @[@"Monday", @"Tuesday", @"Wednesday"];
        NSArray* lengthArr = [arr valueForKey:@"length"];
        NSLog(@"%@", lengthArr);
        NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"];
        NSLog(@"%@", lowercaseArr);
    }
    
    1. KVC容器操作
      /// 聚合操作符 @avg、@count、@max、@min、@sum
    - (void) contrainerTest {
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSLog(@"%@", [students valueForKey:@"height"]);//拿到6个模型中的高
        /**
         输出:
         (
         "1.67",
         "1.75",
         "1.67",
         "1.75",
         "1.65",
         "1.75"
         )
         */
        /// 平均身高
        float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
        NSLog(@"%f", avg);//输出:1.706667
    }
    

    /// 数组操作符 @distinctUnionOfObjects @unionOfObjects

    - (void) contrainerArrayTest {
        
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        NSLog(@"%@", [students valueForKey:@"height"]);
        
        NSArray* arr = [students valueForKeyPath:@"@distinctUnionOfObjects.height"];//去重
        NSLog(@"arr = %@", arr);
        
        NSArray* arr1 = [students valueForKeyPath:@"@unionOfObjects.height"];
        NSLog(@"arr1 = %@", arr1);//不去重
    }
    

    /// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets

    - (void) containerNestingTest {
        
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSMutableArray* students1 = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students1 addObject:student];
        }
        
        // 嵌套数组
        NSArray* nestArr = @[students, students1];
        
    //    从嵌套数组中取值。二维数组
        NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.height"];//去重
        NSLog(@"arr = %@", arr);
        
        /** 输出:
         arr = (
         "1.65",
         "1.67",
         "1.73",
         "1.75",
         "1.69"
         )
         */
        
        NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
        NSLog(@"arr1 = %@", arr1);
        /**
         输出:
         arr1 = (
         "1.69",
         "1.65",
         "1.65",
         "1.73",
         "1.75",
         "1.75",
         "1.69",
         "1.65",
         "1.69",
         "1.73",
         "1.73",
         "1.67"
         )
         */
    }
    
    //NSMutableSet
    - (void) containerNestingTest1 {
        NSMutableSet* students = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
    //    set使用valueForKey去重,array使用valueForKey不去重
        NSLog(@"students = %@", [students valueForKey:@"height"]);//去重
        
        NSMutableSet* students1 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students1 addObject:student];
        }
        
         NSLog(@"students1 = %@", [students1 valueForKey:@"height"]);//去重
        
        NSSet* nestSet = [NSSet setWithObjects:students, students1, nil];
        
        NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.height"];//去重
        NSLog(@"arr1 = %@", arr1);
    }
    
    1. KVC集合代理对象
    TZPerson* p = [TZPerson new];
        p.countjhl = 5;
        NSLog(@"books = %@", [p valueForKey:@"books"]);
        
        p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
        NSSet* set = [p valueForKey:@"pens"];
        NSLog(@"pens = %@", set);
        
        NSEnumerator* enumerator = [set objectEnumerator];
        NSString* str = nil;
        while (str = [enumerator nextObject]) {
            NSLog(@"%@", str);
        }
    
    
    
    @interface TZPerson : NSObject
    @property (nonatomic, strong) NSString* name;
    @property (nonatomic, assign) int age;
    @property (nonatomic, strong) NSString* nick;
    @property (nonatomic, assign) float height;
    @property (nonatomic, assign) NSUInteger countjhl;
    @property (nonatomic, strong) NSMutableArray *penArr;
    /**  */
    @property (nonatomic,copy) NSString *jiahl;
    @end
    
    
    @implementation TZPerson
    
    //Books:.h中未定义的属性
    - (NSUInteger) countOfBooks {
        return self.countjhl;
    }
    
    - (id) objectInBooksAtIndex:(NSUInteger)index {
        return [NSString stringWithFormat:@"book %lu", index];
    }
    //Pens:.h中未定义的属性
    // 个数
    - (NSUInteger) countOfPens {
        return [self.penArr count];
    }
    
    // 是否包含这个成员对象
    - (id) memberOfPens:(id)object {
        return [self.penArr containsObject:object] ? object : nil;
    }
    // 迭代器
    - (id) enumeratorOfPens {
        return [self.penArr objectEnumerator];
    }
    
    //jiahl:.h中定义的属性
    //-(NSUInteger)countOfJiahl
    //{
    //    return self.countjhl;
    //}
    //
    //-(id)objectInJiahlAtIndex:(NSUInteger)index{
    //    return [NSString stringWithFormat:@"bookjhl %lu", index];
    //}
    @end
    
    

    相关文章

      网友评论

          本文标题:KVC的使用

          本文链接:https://www.haomeiwen.com/subject/tsblnftx.html