iOS进阶_KVC(&KVC赋值取值过程分析&KVC自定义&异常处理)

KVC(Key-value coding)

键值编码

基本使用

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

实例:
WTPerson.h

#import <Foundation/Foundation.h>
 
@interface WTPerson : NSObject{
//    @public  //@protect默认
    NSString * _name;
}
 
/** name  **/
//@property(nonatomic,strong)NSString * name;
 
@end

ViewController.m 

#import "ViewController.h"
#import "WTPerson.h"
 
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *text;
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    WTPerson * p = [WTPerson new];
    //访问成员变量
    //p.name = @"wt";
    //NSLog(@"%@",p.name);
 
    //访问私有变量(必须要要设置为public才可访问)
    //p->_name = @"wt";
    //NSLog(@"%@",p->_name);
 
    //KVC(即使不用public修饰,也可以访问私有变量)
    [p setValue:@"wt" forKey:@"name"];
    NSLog(@"%@",[p valueForKey:@"name"]);
 
    [self.text setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
}

KVC赋值取值过程分析和自定义及异常处理

赋值过程

  • 1、先找相关方法set<Key>; _set<Key>; setIs<Key>;
  • 2、若是没有相关方法+(BOOL)accessInstanceVariablesDirectly判断是否可以直接访问成员变量
  • 3、如果判断NO,直接执行KVC的setValue:forUndefinedKey:(系统抛出一个异常,未定义key)
  • 4、如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
  • 5、方法或成员都不存在,setValue:forUndefinedKey:方法默认是抛出异常

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群642363427不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

实例验证

WTPerson.h

#import <Foundation/Foundation.h>
@interface WTPerson : NSObject{
    @public  //@protect默认
    NSString * _name;
    NSString * _isName;
    NSString * name;
    NSString * isName;
}
@end

WTPerson.m

#import "WTPerson.h"
@implementation WTPerson
 
-(void)setName:(NSString *)name{
    NSLog(@"%s",__func__);
}
 
-(void)_setName:(NSString *)name{
    NSLog(@"%s",__func__);
}
 
-(void)setIsName:(NSString *)name{
    NSLog(@"%s",__func__);
}
@end

ViewController.m

#import "ViewController.h"
#import "WTPerson.h"
 
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    WTPerson * p = [WTPerson new]; 
    //验证KVC赋值过程
    [p setValue:@"wt" forKey:@"name"];
 
    NSLog(@"name = %@",p->name);
    NSLog(@"_name = %@",p->_name);
    NSLog(@"isname = %@",p->isName);
    NSLog(@"_isname = %@",p->_isName);
}
 
@end
  • 运行程序,我们把WTPerson.m中的-(void)setName:(NSString *)name-(void)_setName:(NSString *)name-(void)setIsName:(NSString *)name三个方法依次注释,我们发现三个方法都会被依次执行。

  • 然后我们把WTPerson.h中的NSString * _name;NSString * _isName;NSString * name;NSString * isName;依次注释,我们会发现4个属性依次被赋值。

WTPerson.m中我们让accessInstanceVariablesDirectly返回NO,则程序直接崩溃。

+ (BOOL)accessInstanceVariablesDirectly{
    return NO;
}

  取值过程

  • 1、先找相关方法get<Key>,key
  • 2、若没有相关方法,+(BOOL)accessInstanceVariabkesDirectly判断是否可以直接访问成员变量
  • 3、如果是NO,直接执行KVC的valueForUndefinedKey:(系统抛出一个异常,未定义key)
  • 4、如果是YES,继续找相关变量_<key>、_is<Key>、<key>、is<Key>
  • 5、方法或成员都不存在,valueForUndefineKey:方法,默认是抛出异常

实例验证

WTPerson.m

#import "WTPerson.h"
 
@implementation WTPerson
 
//- (NSString*) getName{
//    NSLog(@"%s",__func__);
//    return @"getName";
//}
 
- (NSString*) name {
    return @"name";
}
 
//+ (BOOL)accessInstanceVariablesDirectly{
//    return NO;
//}
@end

ViewController.m

#import "ViewController.h"
#import "WTPerson.h"
 
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *text;
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    WTPerson * p = [WTPerson new];
 
    //验证KVC取值过程
    NSLog(@"name = %@",[p valueForKey:@"name"]);
}
 
@end

取值方式与赋值方式大致相同。

KVC自定义

自定义KVC代码实现

创建分类NSObject+KVC

NSObject+KVC.h

#import <Foundation/Foundation.h>
 
@interface NSObject (KVC)
 
- (void)wt_setValue:(nullable id)value forKey:(NSString *)key;
 
- (id)wt_valueForKey:(NSString *)key;
 
@end

NSObject+KVC.m

#import "NSObject+KVC.h"
#import <objc/runtime.h>
 
@implementation NSObject (KVC)
 
- (id)wt_valueForKey:(NSString *)key{
    //判断是否合法
    if (key == nil && key.length ==0) {
        return nil;
    }
 
    //Key
    NSString * Key = key.capitalizedString;
 
    //先找相关方法 get<Key>,key
    NSString * getKey = [NSString stringWithFormat:@"get%@:",Key];
 
    if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
        return [self performSelector:NSSelectorFromString(getKey)];
    }
 
    if ([self respondsToSelector:NSSelectorFromString(key)]) {
        return [self performSelector:NSSelectorFromString(key)];
    }
 
    if (![self.class accessInstanceVariablesDirectly]) {
        NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" 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]]) {
 
            return object_getIvar(self, ivars[i]);
        }
    }
 
    for (int i = 0; i < count; i++) {
        NSString *keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) {
 
            return object_getIvar(self, ivars[i]);
        }
    }
 
    for (int i = 0; i < count; i++) {
        NSString *keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) {
 
            return object_getIvar(self, ivars[i]);
        }
    }
 
    for (int i = 0; i < count; i++) {
        NSString *keyName = arr[i];
        if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) {
            return object_getIvar(self, ivars[i]);
        }
    }
    free(ivars);
    return nil;
}
 
- (void)wt_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:@"NSUnknownKeyException" 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);
}
@end

验证

ViewController.m

#import "ViewController.h"
#import "WTPerson.h"
#import "NSObject+KVC.h"
 
@interface ViewController ()
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    WTPerson * p =[WTPerson new];
    [p wt_setValue:@"wt" forKey:@"name"];
 
    NSLog(@"name-KVC = %@",[p wt_valueForKey:@"name"]);
    NSLog(@"_name = %@",p->_name);
    NSLog(@"_isName = %@",p->_isName);
    NSLog(@"name = %@",p->name);
    NSLog(@"isName = %@",p->isName);
}
@end

  在项目中

commond+shift+o 搜索setValue:forKey发现在Foundation框架下的NSKeyValueCoding文件下

 

 

 

我们查看这个文件中的方法,发现这个文件中是一些分类的集合

 

 

 

KVC异常处理及正确性验证

KVC异常处理

  • 1、赋值为空 setNilValueForKey
  • 2、Key值不存在 setValue:forUndefinedKey

正确性验证

validateValue
该方法的工作原理:

  • 1、先找一下你的类中是否实现了方法 -(BOOL)validate<Key>:error;
  • 2、如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES;如果没有实现这个方法,则系统默认返回YES

示例代码

WTPerson…h

#import <Foundation/Foundation.h>
 
@interface WTPerson : NSObject
 
/** name  **/
@property(nonatomic,strong)NSString * name;
 
/** age  **/
@property(nonatomic,assign)int age;
 
@end

WTPerson.m

#import "WTPerson.h"
 
@implementation WTPerson
 
//对非对象类型,值不能为空
- (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;
}
 
//正确性验证
- (BOOL) validateAge:(inout id  _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
    NSNumber* value = (NSNumber*)*ioValue;
    NSLog(@"%@",value);
    if ([value integerValue] >= 0 && [value integerValue] <= 200) {
        return YES;
    }
    return NO;
}
 
@end

ViewController.m

#import "ViewController.h"
#import "WTPerson.h"
#import "WTContainer.h"
 
@interface ViewController ()
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    WTPerson * p = [WTPerson new];
 
    //异常处理
    [p setValue:@18 forKey:@"name"];
    [p setValue:nil forKey:@"name"];
    NSLog(@"name = %@",p.name);
 
    [p setValue:nil forKey:@"age"];
    NSLog(@"age = %d",p.age);
 
    [p setValue:@"hello" forKey:@"name1"];
 
    NSLog(@"name = %@",[p valueForKey:@"name1"]);
 
    //万能容器
    WTContainer * container = [WTContainer new];
 
    [container setValue:@"wt" forKey:@"name"];
    [container setValue:@18 forKey:@"age"];
 
    NSLog(@"name = %@,age = %@",[container valueForKey:@"name"],[container valueForKey:@"age"]);
 
    //正确性验证
    NSNumber * value = @200;
    NSNumber * value1 = @199;
 
    if ([p validateValue:&value1 forKey:@"age" error:NULL]) {
 
        [p setValue:value1 forKey:@"age"];
    }
 
    NSLog(@"%@",[p valueForKey:@"age"]);
}
 
@end

  

  1.  
    #import <Foundation/Foundation.h>
  2.  
     
  3.  
    @interface WTPerson : NSObject{
  4.  
    // @public //@protect默认
  5.  
    NSString * _name;
  6.  
    }
  7.  
     
  8.  
    /** name **/
  9.  
    //@property(nonatomic,strong)NSString * name;
  10.  
     
  11.  
    @end
  12.  
    复制代码

ViewController.m

  1.  
    #import "ViewController.h"
  2.  
    #import "WTPerson.h"
  3.  
     
  4.  
    @interface ViewController ()
  5.  
    @property (weak, nonatomic) IBOutlet UITextField *text;
  6.  
     
  7.  
    @end
  8.  
     
  9.  
    @implementation ViewController
  10.  
     
  11.  
    - (void)viewDidLoad {
  12.  
    [super viewDidLoad];
  13.  
    WTPerson * p = [WTPerson new];
  14.  
    //访问成员变量
  15.  
    //p.name = @"wt";
  16.  
    //NSLog(@"%@",p.name);
  17.  
     
  18.  
    //访问私有变量(必须要要设置为public才可访问)
  19.  
    //p->_name = @"wt";
  20.  
    //NSLog(@"%@",p->_name);
  21.  
     
  22.  
    //KVC(即使不用public修饰,也可以访问私有变量)
  23.  
    [p setValue:@"wt" forKey:@"name"];
  24.  
    NSLog(@"%@",[p valueForKey:@"name"]);
  25.  
     
  26.  
    [self.text setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
  27.  
    }
  28.  
    复制代码

 

KVC赋值取值过程分析和自定义及异常处理

赋值过程

  • 1、先找相关方法set<Key>; _set<Key>; setIs<Key>;
  • 2、若是没有相关方法+(BOOL)accessInstanceVariablesDirectly判断是否可以直接访问成员变量
  • 3、如果判断NO,直接执行KVC的setValue:forUndefinedKey:(系统抛出一个异常,未定义key)
  • 4、如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
  • 5、方法或成员都不存在,setValue:forUndefinedKey:方法默认是抛出异常

实例验证

WTPerson.h

  1.  
    #import <Foundation/Foundation.h>
  2.  
    @interface WTPerson : NSObject{
  3.  
    @public //@protect默认
  4.  
    NSString * _name;
  5.  
    NSString * _isName;
  6.  
    NSString * name;
  7.  
    NSString * isName;
  8.  
    }
  9.  
    @end
  10.  
    复制代码

WTPerson.m

  1.  
    #import "WTPerson.h"
  2.  
    @implementation WTPerson
  3.  
     
  4.  
    -(void)setName:(NSString *)name{
  5.  
    NSLog(@"%s",__func__);
  6.  
    }
  7.  
     
  8.  
    -(void)_setName:(NSString *)name{
  9.  
    NSLog(@"%s",__func__);
  10.  
    }
  11.  
     
  12.  
    -(void)setIsName:(NSString *)name{
  13.  
    NSLog(@"%s",__func__);
  14.  
    }
  15.  
    @end
  16.  
    复制代码

ViewController.m

  1.  
    #import "ViewController.h"
  2.  
    #import "WTPerson.h"
  3.  
     
  4.  
    @interface ViewController ()
  5.  
    @end
  6.  
    @implementation ViewController
  7.  
    - (void)viewDidLoad {
  8.  
    [super viewDidLoad];
  9.  
    WTPerson * p = [WTPerson new];
  10.  
    //验证KVC赋值过程
  11.  
    [p setValue:@"wt" forKey:@"name"];
  12.  
     
  13.  
    NSLog(@"name = %@",p->name);
  14.  
    NSLog(@"_name = %@",p->_name);
  15.  
    NSLog(@"isname = %@",p->isName);
  16.  
    NSLog(@"_isname = %@",p->_isName);
  17.  
    }
  18.  
     
  19.  
    @end
  20.  
    复制代码
  • 运行程序,我们把WTPerson.m中的-(void)setName:(NSString *)name-(void)_setName:(NSString *)name-(void)setIsName:(NSString *)name三个方法依次注释,我们发现三个方法都会被依次执行。

  • 然后我们把WTPerson.h中的NSString * _name;NSString * _isName;NSString * name;NSString * isName;依次注释,我们会发现4个属性依次被赋值。

WTPerson.m中我们让accessInstanceVariablesDirectly返回NO,则程序直接崩溃。

  1.  
    + (BOOL)accessInstanceVariablesDirectly{
  2.  
    return NO;
  3.  
    }
  4.  
    复制代码

取值过程

  • 1、先找相关方法get<Key>,key
  • 2、若没有相关方法,+(BOOL)accessInstanceVariabkesDirectly判断是否可以直接访问成员变量
  • 3、如果是NO,直接执行KVC的valueForUndefinedKey:(系统抛出一个异常,未定义key)
  • 4、如果是YES,继续找相关变量_<key>、_is<Key>、<key>、is<Key>
  • 5、方法或成员都不存在,valueForUndefineKey:方法,默认是抛出异常

实例验证

WTPerson.m

  1.  
    #import "WTPerson.h"
  2.  
     
  3.  
    @implementation WTPerson
  4.  
     
  5.  
    //- (NSString*) getName{
  6.  
    // NSLog(@"%s",__func__);
  7.  
    // return @"getName";
  8.  
    //}
  9.  
     
  10.  
    - (NSString*) name {
  11.  
    return @"name";
  12.  
    }
  13.  
     
  14.  
    //+ (BOOL)accessInstanceVariablesDirectly{
  15.  
    // return NO;
  16.  
    //}
  17.  
    @end
  18.  
    复制代码

ViewController.m

  1.  
    #import "ViewController.h"
  2.  
    #import "WTPerson.h"
  3.  
     
  4.  
    @interface ViewController ()
  5.  
    @property (weak, nonatomic) IBOutlet UITextField *text;
  6.  
     
  7.  
    @end
  8.  
     
  9.  
    @implementation ViewController
  10.  
     
  11.  
    - (void)viewDidLoad {
  12.  
    [super viewDidLoad];
  13.  
    WTPerson * p = [WTPerson new];
  14.  
     
  15.  
    //验证KVC取值过程
  16.  
    NSLog(@"name = %@",[p valueForKey:@"name"]);
  17.  
    }
  18.  
     
  19.  
    @end
  20.  
    复制代码

取值方式与赋值方式大致相同。

KVC自定义

自定义KVC代码实现

创建分类NSObject+KVC

NSObject+KVC.h

  1.  
    #import <Foundation/Foundation.h>
  2.  
     
  3.  
    @interface NSObject (KVC)
  4.  
     
  5.  
    - (void)wt_setValue:(nullable id)value forKey:(NSString *)key;
  6.  
     
  7.  
    - (id)wt_valueForKey:(NSString *)key;
  8.  
     
  9.  
    @end
  10.  
    复制代码

NSObject+KVC.m

  1.  
    #import "NSObject+KVC.h"
  2.  
    #import <objc/runtime.h>
  3.  
     
  4.  
    @implementation NSObject (KVC)
  5.  
     
  6.  
    - (id)wt_valueForKey:(NSString *)key{
  7.  
    //判断是否合法
  8.  
    if (key == nil && key.length ==0) {
  9.  
    return nil;
  10.  
    }
  11.  
     
  12.  
    //Key
  13.  
    NSString * Key = key.capitalizedString;
  14.  
     
  15.  
    //先找相关方法 get<Key>,key
  16.  
    NSString * getKey = [NSString stringWithFormat:@"get%@:",Key];
  17.  
     
  18.  
    if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
  19.  
    return [self performSelector:NSSelectorFromString(getKey)];
  20.  
    }
  21.  
     
  22.  
    if ([self respondsToSelector:NSSelectorFromString(key)]) {
  23.  
    return [self performSelector:NSSelectorFromString(key)];
  24.  
    }
  25.  
     
  26.  
    if (![self.class accessInstanceVariablesDirectly]) {
  27.  
    NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil];
  28.  
    @throw exception;
  29.  
    }
  30.  
     
  31.  
    //再找相关变量
  32.  
    //获取所有的成员变量
  33.  
    unsigned int count = 0;
  34.  
    Ivar * ivars = class_copyIvarList([self class], &count);
  35.  
    NSMutableArray * arr = [[NSMutableArray alloc]init];
  36.  
    for (int i = 0; i<count; i++) {
  37.  
    Ivar var = ivars[i];
  38.  
    const char * varName = ivar_getName(var);
  39.  
    NSString *name = [NSString stringWithUTF8String:varName];
  40.  
    [arr addObject:name];
  41.  
    }
  42.  
     
  43.  
    //_<key> _is<Key> <key> is<Key>
  44.  
    for (int i = 0; i < count; i++) {
  45.  
    NSString *keyName = arr[i];
  46.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
  47.  
     
  48.  
    return object_getIvar(self, ivars[i]);
  49.  
    }
  50.  
    }
  51.  
     
  52.  
    for (int i = 0; i < count; i++) {
  53.  
    NSString *keyName = arr[i];
  54.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) {
  55.  
     
  56.  
    return object_getIvar(self, ivars[i]);
  57.  
    }
  58.  
    }
  59.  
     
  60.  
    for (int i = 0; i < count; i++) {
  61.  
    NSString *keyName = arr[i];
  62.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) {
  63.  
     
  64.  
    return object_getIvar(self, ivars[i]);
  65.  
    }
  66.  
    }
  67.  
     
  68.  
    for (int i = 0; i < count; i++) {
  69.  
    NSString *keyName = arr[i];
  70.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) {
  71.  
    return object_getIvar(self, ivars[i]);
  72.  
    }
  73.  
    }
  74.  
    free(ivars);
  75.  
    return nil;
  76.  
    }
  77.  
     
  78.  
    - (void)wt_setValue:(nullable id)value forKey:(NSString *)key{
  79.  
     
  80.  
    //判断是否合法
  81.  
    if (key == nil && key.length ==0) {
  82.  
    return;
  83.  
    }
  84.  
     
  85.  
    //Key
  86.  
    NSString * Key = key.capitalizedString;
  87.  
     
  88.  
    //先找相关方法 set<Key>; _set<Key>; setIs<Key>;
  89.  
    NSString * setKey = [NSString stringWithFormat:@"set%@:",Key];
  90.  
     
  91.  
    if ([self respondsToSelector:NSSelectorFromString(setKey)]) {
  92.  
    [self performSelector:NSSelectorFromString(setKey) withObject:value];
  93.  
    return;
  94.  
    }
  95.  
     
  96.  
    NSString * _setKey = [NSString stringWithFormat:@"_set%@:",Key];
  97.  
     
  98.  
    if ([self respondsToSelector:NSSelectorFromString(_setKey)]) {
  99.  
    [self performSelector:NSSelectorFromString(_setKey) withObject:value];
  100.  
    return;
  101.  
    }
  102.  
     
  103.  
    NSString * setIsKey = [NSString stringWithFormat:@"setIs%@:",Key];
  104.  
    if ([self respondsToSelector:NSSelectorFromString(setIsKey)]) {
  105.  
    [self performSelector:NSSelectorFromString(setIsKey) withObject:value];
  106.  
    return;
  107.  
    }
  108.  
     
  109.  
    if (![self.class accessInstanceVariablesDirectly]) {
  110.  
    NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil];
  111.  
    @throw exception;
  112.  
    }
  113.  
     
  114.  
    //再找相关变量
  115.  
    //获取所有的成员变量
  116.  
    unsigned int count = 0;
  117.  
    Ivar * ivars = class_copyIvarList([self class], &count);
  118.  
    NSMutableArray * arr = [[NSMutableArray alloc]init];
  119.  
    for (int i = 0; i<count; i++) {
  120.  
    Ivar var = ivars[i];
  121.  
    const char * varName = ivar_getName(var);
  122.  
    NSString *name = [NSString stringWithUTF8String:varName];
  123.  
    [arr addObject:name];
  124.  
    }
  125.  
     
  126.  
    //_<key> _is<Key> <key> is<Key>
  127.  
    for (int i = 0; i < count; i++) {
  128.  
    NSString *keyName = arr[i];
  129.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
  130.  
    object_setIvar(self, ivars[i], value);
  131.  
    free(ivars);
  132.  
    return;
  133.  
    }
  134.  
    }
  135.  
     
  136.  
    for (int i = 0; i < count; i++) {
  137.  
    NSString *keyName = arr[i];
  138.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) {
  139.  
    object_setIvar(self, ivars[i], value);
  140.  
    free(ivars);
  141.  
    return;
  142.  
    }
  143.  
    }
  144.  
     
  145.  
    for (int i = 0; i < count; i++) {
  146.  
    NSString *keyName = arr[i];
  147.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) {
  148.  
    object_setIvar(self, ivars[i], value);
  149.  
    free(ivars);
  150.  
    return;
  151.  
    }
  152.  
    }
  153.  
     
  154.  
    for (int i = 0; i < count; i++) {
  155.  
    NSString *keyName = arr[i];
  156.  
    if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) {
  157.  
    object_setIvar(self, ivars[i], value);
  158.  
    free(ivars);
  159.  
    return;
  160.  
    }
  161.  
    }
  162.  
     
  163.  
    [self setValue:value forUndefinedKey:Key];
  164.  
    free(ivars);
  165.  
    }
  166.  
    @end
  167.  
    复制代码

验证

ViewController.m

  1.  
    #import "ViewController.h"
  2.  
    #import "WTPerson.h"
  3.  
    #import "NSObject+KVC.h"
  4.  
     
  5.  
    @interface ViewController ()
  6.  
     
  7.  
    @end
  8.  
     
  9.  
    @implementation ViewController
  10.  
     
  11.  
    - (void)viewDidLoad {
  12.  
    [super viewDidLoad];
  13.  
     
  14.  
    WTPerson * p =[WTPerson new];
  15.  
    [p wt_setValue:@"wt" forKey:@"name"];
  16.  
     
  17.  
    NSLog(@"name-KVC = %@",[p wt_valueForKey:@"name"]);
  18.  
    NSLog(@"_name = %@",p->_name);
  19.  
    NSLog(@"_isName = %@",p->_isName);
  20.  
    NSLog(@"name = %@",p->name);
  21.  
    NSLog(@"isName = %@",p->isName);
  22.  
    }
  23.  
    @end
  24.  
    复制代码

在项目中
commond+shift+o 搜索setValue:forKey发现在Foundation框架下的NSKeyValueCoding文件下

 

 

 

我们查看这个文件中的方法,发现这个文件中是一些分类的集合

 

 

 

KVC异常处理及正确性验证

KVC异常处理

  • 1、赋值为空 setNilValueForKey
  • 2、Key值不存在 setValue:forUndefinedKey

正确性验证

validateValue
该方法的工作原理:

  • 1、先找一下你的类中是否实现了方法 -(BOOL)validate<Key>:error;
  • 2、如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES;如果没有实现这个方法,则系统默认返回YES

示例代码

WTPerson…h

  1.  
    #import <Foundation/Foundation.h>
  2.  
     
  3.  
    @interface WTPerson : NSObject
  4.  
     
  5.  
    /** name **/
  6.  
    @property(nonatomic,strong)NSString * name;
  7.  
     
  8.  
    /** age **/
  9.  
    @property(nonatomic,assign)int age;
  10.  
     
  11.  
    @end
  12.  
    复制代码

WTPerson.m

  1.  
    #import "WTPerson.h"
  2.  
     
  3.  
    @implementation WTPerson
  4.  
     
  5.  
    //对非对象类型,值不能为空
  6.  
    - (void) setNilValueForKey:(NSString *)key{
  7.  
    NSLog(@"%@ 值不能为空",key);
  8.  
    }
  9.  
     
  10.  
    //赋值的key不存在
  11.  
    - (void) setValue:(id)value forUndefinedKey:(NSString *)key{
  12.  
    NSLog(@"key = %@值不存在",key);
  13.  
    }
  14.  
     
  15.  
    //取值的key不存在
  16.  
    - (id) valueForUndefinedKey:(NSString *)key{
  17.  
    NSLog(@"key = %@值不存在",key);
  18.  
    return nil;
  19.  
    }
  20.  
     
  21.  
    //正确性验证
  22.  
    - (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
  23.  
    NSNumber* value = (NSNumber*)*ioValue;
  24.  
    NSLog(@"%@",value);
  25.  
    if ([value integerValue] >= 0 && [value integerValue] <= 200) {
  26.  
    return YES;
  27.  
    }
  28.  
    return NO;
  29.  
    }
  30.  
     
  31.  
    @end
  32.  
    复制代码

ViewController.m

  1.  
    #import "ViewController.h"
  2.  
    #import "WTPerson.h"
  3.  
    #import "WTContainer.h"
  4.  
     
  5.  
    @interface ViewController ()
  6.  
     
  7.  
    @end
  8.  
     
  9.  
    @implementation ViewController
  10.  
     
  11.  
    - (void)viewDidLoad {
  12.  
    [super viewDidLoad];
  13.  
    WTPerson * p = [WTPerson new];
  14.  
     
  15.  
    //异常处理
  16.  
    [p setValue:@18 forKey:@"name"];
  17.  
    [p setValue:nil forKey:@"name"];
  18.  
    NSLog(@"name = %@",p.name);
  19.  
     
  20.  
    [p setValue:nil forKey:@"age"];
  21.  
    NSLog(@"age = %d",p.age);
  22.  
     
  23.  
    [p setValue:@"hello" forKey:@"name1"];
  24.  
     
  25.  
    NSLog(@"name = %@",[p valueForKey:@"name1"]);
  26.  
     
  27.  
    //万能容器
  28.  
    WTContainer * container = [WTContainer new];
  29.  
     
  30.  
    [container setValue:@"wt" forKey:@"name"];
  31.  
    [container setValue:@18 forKey:@"age"];
  32.  
     
  33.  
    NSLog(@"name = %@,age = %@",[container valueForKey:@"name"],[container valueForKey:@"age"]);
  34.  
     
  35.  
    //正确性验证
  36.  
    NSNumber * value = @200;
  37.  
    NSNumber * value1 = @199;
  38.  
     
  39.  
    if ([p validateValue:&value1 forKey:@"age" error:NULL]) {
  40.  
     
  41.  
    [p setValue:value1 forKey:@"age"];
  42.  
    }
  43.  
     
  44.  
    NSLog(@"%@",[p valueForKey:@"age"]);
  45.  
    }
  46.  
     
  47.  
    @end

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群642363427不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

posted @ 2020-10-21 11:26  iOS__峰公众号iOSVNL  阅读(192)  评论(0编辑  收藏  举报