美文网首页
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详解

    KVC 目录结构KVC定义KVC取值和设置KVC使用keyPathKVC处理字典KVC作用 参考:iOS KVC和...

  • iOS原理篇(二): KVC实现原理

    KVC实现原理 什么是 KVC KVC基本使用 KVC 原理 总结 一 、 什么是KVC KVC的全称是Key-V...

  • 数据存储

    1.1.3.使用通知传值 1.2.Segue使用 2. KVC&&KVO2.1.什么是KVC KVC - Key ...

  • OC语法:KVC的底层实现

    一、KVC是什么二、怎么使用KVC三、KVC的底层实现四、KVC常见面试题 一、KVC是什么 KVC全称Key-V...

  • KVC

    方法交换 KVC的使用 KVC的原理 KVC自定义 参考资料 DIS_KVC_KVO[https://github...

  • KVC的简单使用

    KVC字典转模型 KVC 中经常使用的就是字典转模型 KVC的大招 KVC设置对象属性及取值 KVC间接设置对象属...

  • 02-03、图片的拉伸、KVC和KVO的使用

    1、图片拉伸 2、KVC的使用 KVC: Key Value Coding(键值编码) 3、KVO的使用 KVO:...

  • KVC,KVO

    主要分成一下几个部分1.KVC1.1 KVC简介1.2 KVC使用2.KVO2.1 KVO简介2.2 KVO使用 ...

  • KVO和KVC的使用及原理解析

    一 KVO基本使用 二 KVO本质原理讲解及代码验证 三 KVC基本使用 四 KVC设值原理 五 KVC取值原理 ...

  • 03--KVC/KVO本质04--KVC 注意事项

    总结 KVC 使用的一些注意事项 KVC 注意事项 KVC 自动转换类型使用string存基本类型,系统会存为 _...

网友评论

      本文标题:KVC的使用

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