1. 程式人生 > 實用技巧 >iOS進階_KVC(&KVC賦值取值過程分析&KVC自定義&異常處理)

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. @interface WTPerson : NSObject{
  3. // @public //@protect預設
  4. NSString * _name;
  5. }
  6. /** name **/
  7. //@property(nonatomic,strong)NSString * name;
  8. @end
  9. 複製程式碼

ViewController.m

  1. #import "ViewController.h"
  2. #import "WTPerson.h"
  3. @interface ViewController ()
  4. @property (weak, nonatomic) IBOutlet UITextField *text;
  5. @end
  6. @implementation ViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. WTPerson * p = [WTPerson new];
  10. //訪問成員變數
  11. //p.name = @"wt";
  12. //NSLog(@"%@",p.name);
  13. //訪問私有變數(必須要要設定為public才可訪問)
  14. //p->_name = @"wt";
  15. //NSLog(@"%@",p->_name);
  16. //KVC(即使不用public修飾,也可以訪問私有變數)
  17. [p setValue:@"wt" forKey:@"name"];
  18. NSLog(@"%@",[p valueForKey:@"name"]);
  19. [self.text setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
  20. }
  21. 複製程式碼

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. -(void)setName:(NSString *)name{
  4. NSLog(@"%s",__func__);
  5. }
  6. -(void)_setName:(NSString *)name{
  7. NSLog(@"%s",__func__);
  8. }
  9. -(void)setIsName:(NSString *)name{
  10. NSLog(@"%s",__func__);
  11. }
  12. @end
  13. 複製程式碼

ViewController.m

  1. #import "ViewController.h"
  2. #import "WTPerson.h"
  3. @interface ViewController ()
  4. @end
  5. @implementation ViewController
  6. - (void)viewDidLoad {
  7. [super viewDidLoad];
  8. WTPerson * p = [WTPerson new];
  9. //驗證KVC賦值過程
  10. [p setValue:@"wt" forKey:@"name"];
  11. NSLog(@"name = %@",p->name);
  12. NSLog(@"_name = %@",p->_name);
  13. NSLog(@"isname = %@",p->isName);
  14. NSLog(@"_isname = %@",p->_isName);
  15. }
  16. @end
  17. 複製程式碼
  • 執行程式,我們把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. @implementation WTPerson
  3. //- (NSString*) getName{
  4. // NSLog(@"%s",__func__);
  5. // return @"getName";
  6. //}
  7. - (NSString*) name {
  8. return @"name";
  9. }
  10. //+ (BOOL)accessInstanceVariablesDirectly{
  11. // return NO;
  12. //}
  13. @end
  14. 複製程式碼

ViewController.m

  1. #import "ViewController.h"
  2. #import "WTPerson.h"
  3. @interface ViewController ()
  4. @property (weak, nonatomic) IBOutlet UITextField *text;
  5. @end
  6. @implementation ViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. WTPerson * p = [WTPerson new];
  10. //驗證KVC取值過程
  11. NSLog(@"name = %@",[p valueForKey:@"name"]);
  12. }
  13. @end
  14. 複製程式碼

取值方式與賦值方式大致相同。

KVC自定義

自定義KVC程式碼實現

建立分類NSObject+KVC

NSObject+KVC.h

  1. #import <Foundation/Foundation.h>
  2. @interface NSObject (KVC)
  3. - (void)wt_setValue:(nullable id)value forKey:(NSString *)key;
  4. - (id)wt_valueForKey:(NSString *)key;
  5. @end
  6. 複製程式碼

NSObject+KVC.m

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

驗證

ViewController.m

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

在專案中
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. @interface WTPerson : NSObject
  3. /** name **/
  4. @property(nonatomic,strong)NSString * name;
  5. /** age **/
  6. @property(nonatomic,assign)int age;
  7. @end
  8. 複製程式碼

WTPerson.m

  1. #import "WTPerson.h"
  2. @implementation WTPerson
  3. //對非物件型別,值不能為空
  4. - (void) setNilValueForKey:(NSString *)key{
  5. NSLog(@"%@ 值不能為空",key);
  6. }
  7. //賦值的key不存在
  8. - (void) setValue:(id)value forUndefinedKey:(NSString *)key{
  9. NSLog(@"key = %@值不存在",key);
  10. }
  11. //取值的key不存在
  12. - (id) valueForUndefinedKey:(NSString *)key{
  13. NSLog(@"key = %@值不存在",key);
  14. return nil;
  15. }
  16. //正確性驗證
  17. - (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
  18. NSNumber* value = (NSNumber*)*ioValue;
  19. NSLog(@"%@",value);
  20. if ([value integerValue] >= 0 && [value integerValue] <= 200) {
  21. return YES;
  22. }
  23. return NO;
  24. }
  25. @end
  26. 複製程式碼

ViewController.m

  1. #import "ViewController.h"
  2. #import "WTPerson.h"
  3. #import "WTContainer.h"
  4. @interface ViewController ()
  5. @end
  6. @implementation ViewController
  7. - (void)viewDidLoad {
  8. [super viewDidLoad];
  9. WTPerson * p = [WTPerson new];
  10. //異常處理
  11. [p setValue:@18 forKey:@"name"];
  12. [p setValue:nil forKey:@"name"];
  13. NSLog(@"name = %@",p.name);
  14. [p setValue:nil forKey:@"age"];
  15. NSLog(@"age = %d",p.age);
  16. [p setValue:@"hello" forKey:@"name1"];
  17. NSLog(@"name = %@",[p valueForKey:@"name1"]);
  18. //萬能容器
  19. WTContainer * container = [WTContainer new];
  20. [container setValue:@"wt" forKey:@"name"];
  21. [container setValue:@18 forKey:@"age"];
  22. NSLog(@"name = %@,age = %@",[container valueForKey:@"name"],[container valueForKey:@"age"]);
  23. //正確性驗證
  24. NSNumber * value = @200;
  25. NSNumber * value1 = @199;
  26. if ([p validateValue:&value1 forKey:@"age" error:NULL]) {
  27. [p setValue:value1 forKey:@"age"];
  28. }
  29. NSLog(@"%@",[p valueForKey:@"age"]);
  30. }
  31. @end

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群642363427不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術, 大家一起交流學習成長!