iOS基础全面分析之一(KVC全面分析)

iOS基础全面分析之一(KVC全面分析)
iOS基础全面分析之二(RunLoop全面分析)
iOS基础全面分析之三(KVO全面分析)

KVC全面分析

基本使用

  1. 能够对对象的私有成员进行取值赋值
  2. 对数值和结构体的属性进行的打包解包处理 (NSNumber、NSValue)

基本赋值过程

  1. 先找相关方法: set<Key>:, _set<Key>:, setIs<Key>:
  2. 若没有相关方法,会调用+ (BOOL)accessInstanceVariablesDirectly方法
  3. 如果return是NO,直接执行KVC的[object setValue:forUndefinedKey:]:(系统抛出一个异常,未定义的key)
  4. + (BOOL)accessInstanceVariablesDirectly如果return是YES,继续找相关的变量:
    • 相关变量赋值顺序,

      //_<key>,_is<Key>,<key>,is<Key>
      //    NSString * name;         //3
      //    NSString * _name;        //1
      //    NSString * isName;       //4
      //    NSString * _isName;      //2
      
  5. 如果方法或成员变量都不存在,[object setValue:forUndefinedKey:]:(系统抛出一个异常,未定义的key)

基本取值过程

  1. 先找相关方法 get<Key>,<key>,<isKey>,_<key>
  2. 若没有相关方法,会调用+ (BOOL)accessInstanceVariablesDirectly方法
  3. 如果return是NO,执行KVC的[object valueForUndefinedKey:]:(系统抛出一个异常,未定义的key)
  4. + (BOOL)accessInstanceVariablesDirectly如果return是YES,继续找相关的变量:
    • 相关变量取值顺序

      //_<key>,_is<Key>,<key>,is<Key>
      //    NSString * name;         //3
      //    NSString * _name;        //1
      //    NSString * isName;       //4
      //    NSString * _isName;      //2
      
  5. 方法根成员变量都不存在,执行KVC的[object valueForUndefinedKey:]:(系统抛出一个异常,未定义的key)

KVC自定义

set方法自定义

- (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];
       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);
   
}

get方法自定义

- (NSString *)GG_getValueforKey:(NSString *)key{
    
    // 判断是否合法
    if (key == nil && key.length == 0) {
        NSException* exception = [NSException exceptionWithName:@"崩溃" reason:@"value不合法" userInfo:nil];
        @throw exception;
    }
    
    // Key
    NSString* CapitalKey = key.capitalizedString;
    
    //get<key>,<key>
    NSString* getKey = [NSString stringWithFormat:@"get%@", CapitalKey];
    if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
        
        return [self performSelector:NSSelectorFromString(getKey)];
    }
    
    NSString* Key = [NSString stringWithFormat:@"%@", key];
    if ([self respondsToSelector:NSSelectorFromString(Key)]) {
        
        return [self performSelector:NSSelectorFromString(Key)];
    }
    
    
    if (![self.class accessInstanceVariablesDirectly]) {
        NSException* exception = [NSException exceptionWithName:@"NSUnkonwnKeyException" reason:@"valueForUndefinedKey:" userInfo:nil];
        @throw exception;
    }
    
#pragma mark -- 获取所以成员变量
    /// 再找相关变量
    /// 获取所以成员变量
    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];
        NSLog(@"%@",name);
        [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]]) {
            NSString * var = object_getIvar(self, ivars[i]);
            free(ivars);
            return var;
        }
    }
    
    for (int i = 0; i < count; i++) {
        NSString* keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@", CapitalKey]]) {
            NSString * var = object_getIvar(self, ivars[i]);
            free(ivars);
            return var;
        }
    }
    
    for (int i = 0; i < count; i++) {
        NSString* keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"%@", key]]) {
            NSString * var = object_getIvar(self, ivars[i]);
            free(ivars);
            return var;
        }
    }
    
    for (int i = 0; i < count; i++) {
        NSString* keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@", CapitalKey]]) {
            NSString * var = object_getIvar(self, ivars[i]);
            free(ivars);
            return var;
        }
    }
    
    NSString *RNstr = [self valueForUndefinedKey:key];
    
    free(ivars);
    return  RNstr;

}

KVC异常处理及正确性验证

非对象类型,KVC的异常处理

@interface Person : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@end
Person *per = [[Person alloc]init];
//这里会崩溃,因为下面是一个值类型,而值类型不能为nil,所以会崩溃
/*
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<Person 0x600003865380> setNilValueForKey]: could not set nil as the value for the key age.'
*/
[per setValue:nil forKey:@"age"];

上述是值类型用KVC赋值为nil崩溃,怎样才能不崩溃呐?
需要在Person重写setNilValueForKey方法

//非对象类型,不能为nil
- (void)setNilValueForKey:(NSString *)key{
    NSLog(@"%@,值不能为空",key);
}

取值和赋值key不存在的异常捕获

//赋值异常捕获
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"key == %@ 值不存在",key);
}
//取值的异常捕获
- (id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"key == %@ 值不存在",key);
    return @"";
}

扩展:万能容器

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    
    if(!key || [key isEqualToString:@""]){
        Persondict = [NSMutableDictionary dictionary];
    }
    [Persondict setValue:value forUndefinedKey:key];
}

- (id)valueForUndefinedKey:(NSString *)key{
    if(!key || [key isEqualToString:@""]){
        return nil;
    }
    return [Persondict objectForKey:key];
}

value正确性验证

- (BOOL)validateValue: forKey: error:,- (BOOL)validateValue: forKeyPath: error:,这两个方法是KVC提供的验证值的正确性的方法

  1. 值所对应的验证方法是否实现,- (BOOL)validate<Key>: forKey: error:
  2. 如果实现了就会根据实现方法里面的自定义逻辑返回YES和NO,如果没有实现这个方法,则默认系统会返回YES

例子:存储一个Int类型age,这个值必须0<age<200,如果不符合逻辑返回NO,不赋值给person对象。如果符合就赋值,代码如下:

@implementation Person
- (BOOL)validateAge:(inout id  _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
    
    NSInteger value = [(NSNumber *) * ioValue integerValue];
    if(value <=0 || value>=200){
        return  NO;
    }
    return YES;
}
@end

//正确性验证
NSNumber *value = @200;
Person *per = [[Person alloc]init];
if([per validateValue:&value forKey:@"age" error:NULL]){
    [per setValue:value forKey:@"age"];
}

KVC进阶使用

KVC与字典

字典转模型

通过一个字典,以字典Key为属性名称直接赋值给model,一种字典转模型的模式

@interface Person : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@property(nonatomic,copy)NSString *nick;
@property(nonatomic,assign)float height;
@end

NSDictionary *dict = @{
                       @"name":@"struggle3g",
                       @"age":@18,
                       @"nick":@"cat",
                       @"height":@170
                       };
    
Person *per = [[Person alloc]init];
[per setValuesForKeysWithDictionary:dict];
    NSLog(@"per.name = %@, per.age = %d, per.nick =%@, per.height = %f", per.name, per.age, per.nick, per.height);

通过一组key获取字典

NSArray* keys = @[@"name", @"age"];
NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
    
NSLog(@"%@", dict1);

NSArray使用valueForKey:向所有的成员发送消息 (KVC消息传递)

- (void)arrayKVCtext{
    
//向这个数组里面的所有方法发送消息
NSArray *arr = @[@"Mondy",@"Tuesday",@"Wednesday"];
NSArray *lengtharr = [arr valueForKey:@"length"];
NSLog(@"%@",lengtharr);
}

KVC容器操作

聚合操作

/*聚合操作*/
    
NSMutableArray *students = [[NSMutableArray alloc]init];
for (int i = 0; i<6; i++) {
    Person *per = [Person new];
    NSDictionary *dict = @{
                           @"name":@"struggle3g",
                           @"age":@(18+i),
                           @"nick":@"cat",
                           @"height":@(1.70+0.02*arc4random_uniform(6))
                           };
    [per setValuesForKeysWithDictionary:dict];
    [students addObject:per];
}
    
NSLog(@"%@",[students valueForKey:@"height"]);
//平均身高
   
/*
 //聚合的公式,这个在下面官网有注解 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/CollectionOperators.html#//apple_ref/doc/uid/20002176-SW5
 
 */
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
NSLog(@"%f",avg);

数组操作

- (void)arrayKVCtext{
    
    //向这个数组里面的所有方法发送消息
    NSArray *arr = @[@"Mondy",@"Tuesday",@"Wednesday"];
    NSArray *lengtharr = [arr valueForKey:@"length"];
    NSLog(@"%@",lengtharr);
}

嵌套操作(array&set)操作

嵌套操作array
/// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
- (void) containerNestingTest {
    
    NSMutableArray* students = [NSMutableArray array];
    for (int i = 0; i < 6; i++) {
        Person* student = [Person 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++) {
        Person* student = [Person 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);
    
    NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
    NSLog(@"arr1 = %@", arr1);
    
}
嵌套操作set
//无序的嵌套集合
- (void) containerNestingTest1{
    
    NSMutableSet* students = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        Person* student = [Person new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                               };
        [student setValuesForKeysWithDictionary:dict];
        [students addObject:student];
    }
    
    
    
    NSMutableSet* students1 = [NSMutableSet set];
    for (int i = 0; i < 6; i++) {
        Person* student = [Person new];
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@(18+i),
                               @"nick":@"Cat",
                               @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                               };
        [student setValuesForKeysWithDictionary:dict];
        [students1 addObject:student];
    }
    
    //set这个无序集合,向每一个成员发送消息,返回的值是默认是去重的
    NSLog(@"students = %@",[students valueForKey:@"height"]);
    NSLog(@"students1 = %@",[students1 valueForKey:@"height"]);
    
    
    NSSet *set = [NSSet setWithObjects:students,students1, nil];
    NSArray *arr1 = [set valueForKeyPath:@"@distinctUnionOfSets.height"];
    NSLog(@"%@",arr1);

}

集合代理对象

Array的集合代理对象

@interface Person : NSObject
@property (nonatomic,assign)NSUInteger count;
@end

@implementation Person
#pragma  mark -- 集合代理对象

- (NSUInteger)countOfBooks{
    
    return self.count;
}


-(id)objectInBooksAtIndex:(NSUInteger)index{
    
    return [NSString stringWithFormat:@"book %lu",index];
    
}
@end

/*array集合代理对象  一对多的关系  array是2个方法*/
Person *p = [[Person alloc]init];
p.count = 5;
NSLog(@"books = %@",[p valueForKey:@"books"]);

Set的集合代理对象


@interface Person : NSObject
@property (nonatomic,strong)NSMutableArray *penArr;
@end


@implementation Person
#pragma  mark -- 集合代理对象

//个数
- (NSUInteger)countOfPens{
    return [self.penArr count];
}

//是否包含这个成员对象
- (id)memberOfPens:(id)object{
    return [self.penArr containsObject:object]?object:nil;
}
//迭代器
-(id)enumeratorOfPens{
    return [self.penArr reverseObjectEnumerator];
}

@end

/*set集合代理对象  set是3个方法*/
p.penArr = [NSMutableArray arrayWithObjects:@"pen0",@"pen1",@"pen2", nil];
NSSet *set = [p valueForKey:@"pens"];
NSLog(@"%@",set);

总结

KVC的思维导图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • KVC(Key-valuecoding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS...
    榕樹頭阅读 711评论 0 2
  • 什么是KVC? KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实是指iOS的开...
    祀梦_阅读 932评论 0 7
  • 什么是KVC? KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实是指iOS的开...
    萨缪阅读 4,638评论 1 13
  • 什么是KVC? KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实是指iOS的开...
    萨缪阅读 814评论 0 5
  • 1. Basic methods KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允...
    木小易Ying阅读 190评论 0 4