iOS 开发继续加油将来跳槽用iOS面试题+基础知识

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

2019-06-29  本文已影响8人  struggle3g

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>
  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
上一篇下一篇

猜你喜欢

热点阅读