iOS進階_KVC(&KVC賦值取值過程分析&KVC自定義&異常處理)
KVC(Key-value coding)
鍵值編碼
基本使用
- 能夠對物件的私有成員進行取值賦值
- 對數值和結構體型的屬性進行的打包解包處理
例項:
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
ViewController.m
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
WTPerson.m
ViewController.m
-
執行程式,我們把
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、先找相關方法
get<Key>,key
- 2、若沒有相關方法,
+(BOOL)accessInstanceVariabkesDirectly
判斷是否可以直接訪問成員變數 - 3、如果是NO,直接執行KVC的
valueForUndefinedKey:
(系統丟擲一個異常,未定義key) - 4、如果是YES,繼續找相關變數
_<key>、_is<Key>、<key>、is<Key>
- 5、方法或成員都不存在,
valueForUndefineKey:
方法,預設是丟擲異常
例項驗證
WTPerson.m
ViewController.m
取值方式與賦值方式大致相同。
KVC自定義
自定義KVC程式碼實現
建立分類NSObject+KVC
NSObject+KVC.h
NSObject+KVC.m
驗證
ViewController.m
在專案中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
WTPerson.m
ViewController.m
作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群:642363427不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術, 大家一起交流學習成長!