代碼:數組
ViewController.mbash
person.name = @"LG_Cooci"; person.age = 18; person->myName = @"cooci"; NSLog(@"%@ - %d - %@",person.name,person.age,person->myName); 複製代碼
打印結果:markdown
Janice - 18 - ty
複製代碼
代碼:atom
ViewController.mspa
[person setValue:@"Janice" forKey:@"name"]; [person setValue:@19 forKey:@"age"]; [person setValue:@"ty" forKey:@"myName"]; NSLog(@"%@ - %@ - %@",[person valueForKey:@"name"],[person valueForKey:@"age"],[person valueForKey:@"myName"]); 複製代碼
打印結果:指針
Janice - 19 - ty
複製代碼
代碼:code
ViewController.morm
//不可變數組 person.array = @[@"1",@"2",@"3"]; NSArray *array = [person valueForKey:@"array"]; // 用 array 的值建立一個新的數組 array = @[@"100",@"2",@"3"]; [person setValue:array forKey:@"array"]; NSLog(@"%@",[person valueForKey:@"array"]); //可變數組 NSMutableArray *ma = [person mutableArrayValueForKey:@"array"]; ma[0] = @"200"; NSLog(@"%@",[person valueForKey:@"array"]); 複製代碼
打印結果:對象
(
100,
2,
3
)
(
200,
2,
3
)
複製代碼
代碼:three
LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN typedef struct { float x, y, z; } ThreeFloats; @interface LGPerson @property (nonatomic) ThreeFloats threeFloats; @end 複製代碼
ViewController.m
ThreeFloats floats = {1., 2., 3.}; NSValue *value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)]; [person setValue:value forKey:@"threeFloats"]; NSValue *reslut = [person valueForKey:@"threeFloats"]; NSLog(@"%@",reslut); ThreeFloats th; [reslut getValue:&th] ; NSLog(@"%f - %f - %f",th.x,th.y,th.z); 複製代碼
打印結果:
{length = 12, bytes = 0x0000803f0000004000004040}
1.000000 - 2.000000 - 3.000000
複製代碼
LGStudent *student = [[LGStudent alloc] init]; student.subject = @"語文"; person.student = student; [person setValue:@"數學" forKeyPath:@"student.subject"]; NSLog(@"%@",[person valueForKeyPath:@"student.subject"]); 複製代碼
打印結果:
數學
複製代碼
這個過程當中主要用成員變量來探索,爲何要使用成員變量呢,由於屬性在賦值的過程當中原本就會生成getter 和 setter 方法了,因此再用屬性探索KVC賦值,會很差分辯。
根據官方文檔可知 KVC setter 過程當中查找賦值的方法以下:
一、set<Key>: 或則 _set<key>
二、若是找不到第一步,查看 accessInstanceVariablesDirectly 返回是否爲YES,若是返回YES,就繼續查找一下方法:_<key>, _is<key>,<key>,或則 is<key>
依次來驗證一下:
一、先找setter
代碼:
LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public NSString *_name; NSString *_isName; NSString *name; NSString *isName; } @end 複製代碼
LGPerson.m
#import "LGPerson.h" @implementation LGPerson #pragma mark - 關閉或開啓實例變量賦值 + (BOOL)accessInstanceVariablesDirectly{ return YES; } //MARK: - setKey. 的流程分析 - (void)setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)_setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } @end 複製代碼
ViewController.m
- (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 設置值的過程 [person setValue:@"Janice" forKey:@"name"]; NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 複製代碼
運行結果:
二、 當 accessInstanceVariablesDirectly 爲 YES
LGPerson.m 中代碼稍微作一點改動,以下:
//MARK: - setKey. 的流程分析 //註釋掉 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} - (void)_setName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } 複製代碼
打印結果:
而後代碼再作一點改動,以下
//MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} - (void)setIsName:(NSString *)name{ NSLog(@"%s - %@",__func__,name); } 複製代碼
打印結果:
二、 當 accessInstanceVariablesDirectly 爲 NO 時,作以下改動
#pragma mark - 關閉或開啓實例變量賦值 + (BOOL)accessInstanceVariablesDirectly{ return NO; } //MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)setIsName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} 複製代碼
打印結果:
以上,打印估計有注意到了我下面打印的值所有都爲 null,這是由於在重寫方法時,並無複製的哈,如_name = name;
下面將重寫方法全都註釋掉,以下,再來打印成員變量的值,再將 accessInstanceVariablesDirectly 而後YES。
代碼:
LGPerson.m
#pragma mark - 關閉或開啓實例變量賦值 + (BOOL)accessInstanceVariablesDirectly{ return YES; } //MARK: - setKey. 的流程分析 //- (void)setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)_setName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} //- (void)setIsName:(NSString *)name{ // NSLog(@"%s - %@",__func__,name); //} 複製代碼
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 設置值的過程 [person setValue:@"Janice" forKey:@"name"]; NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 複製代碼
打印結果:
打印: LGPerson.h
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public // NSString *_name; NSString *_isName; NSString *name; NSString *isName; } @end 複製代碼
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 設置值的過程 [person setValue:@"Janice" forKey:@"name"]; // NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); NSLog(@"%@-%@",person->name,person->isName); NSLog(@"%@",person->isName); } 複製代碼
打印結果:
再驗證最後一個
#import <Foundation/Foundation.h> #import "LGStudent.h" NS_ASSUME_NONNULL_BEGIN @interface LGPerson : NSObject{ @public // NSString *_name; // NSString *_isName; // NSString *name; NSString *isName; } @end 複製代碼
ViewController.m
#import "ViewController.h" #import "LGPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 1: KVC - 設置值的過程 [person setValue:@"Janice" forKey:@"name"]; // NSLog(@"%@-%@-%@-%@",person->_name,person->_isName,person->name,person->isName); // NSLog(@"%@-%@-%@",person->_isName,person->name,person->isName); // NSLog(@"%@-%@",person->name,person->isName); // NSLog(@"%@",person->isName); } 複製代碼
打印結果:
官方文檔的查找流程以下,get<key>, <key>, is<key>,_<key>
代碼:
LGPerson.h
@interface LGPerson : NSObject{
@public
NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
複製代碼
LGPerson.m
- (NSString *)getName{ return NSStringFromSelector(_cmd); } - (NSString *)name{ return NSStringFromSelector(_cmd); } - (NSString *)isName{ return NSStringFromSelector(_cmd); } - (NSString *)_name{ return NSStringFromSelector(_cmd); } 複製代碼
ViewController.m
- (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [[LGPerson alloc] init]; // 2: KVC - 取值的過程 //先賦值不一樣的值 person->_name = @"_name"; person->_isName = @"_isName"; person->name = @"name"; person->isName = @"isName"; //取值 NSLog(@"取值:%@",[person valueForKey:@"name"]); } 複製代碼
打印結果:
再驗證一個,就將getName註釋掉,走下一個方法
LGPerson.m
//MARK: - valueForKey 流程分析 - get<Key>, <key>, is<Key>, or _<key>, //- (NSString *)getName{ // return NSStringFromSelector(_cmd); //} - (NSString *)name{ return NSStringFromSelector(_cmd); } - (NSString *)isName{ return NSStringFromSelector(_cmd); } - (NSString *)_name{ return NSStringFromSelector(_cmd); } 複製代碼
打印結果:
KVC設置與取值總結:
KVC 設置過程:
一、判斷是否存在'set<key>' 或者 '_set<key>'(帶下劃線的屬性)'setls<key>'
二、若是沒有條件 1,(簡單訪問方式)
2.一、判斷 'accessInstanceVariablesDirectly' 是否存在,返回 'YES'
2.二、判斷 '_<key>','_is<key>','<key>','is<key>'等實例變量
2.三、直接給這些實例變量設置
三、'setValue:forUndefinedKey:'報錯!
KVC 取值過程:
一、若是找到'get<key>',<key>,'is<key>','_<key>'這幾個方法就跳到 '第五步'
二、若是沒有條件 1 ,開始是不是 NSArray 判斷
三、是不是 NSSet 判斷
四、非集合類型
4.一、'accessInstanceVariablesDirectly' 返回 YES
4.二、'_<key>,_is<key>,,is<key>'這些實例屬性
4.三、若是找到,直接獲取實例變量的值,而後繼續執行步驟 5
五、細節處理
5.一、若是檢索到的屬性值是對象指針,則只需返回結果
5.二、若是該值是 NSNumber 支持的標量類型,則將其存儲在 NSNumber 實例中並返回它
5.三、若是結果是NSNumber 不支持的標量類型,則轉換爲 NSValue 對象並返回
六、'valueForUndefineKey'報錯!!!
七、集合類型的還須要操做!
一、KVC 自動轉換類型
如,
[person setValue:@"20" forKey:@"age"]; 複製代碼
age 爲 int 類型,可是給了一個 NSString 類型,因而,便會自動轉換類型爲 __NSCFNumber
二、能夠對int,NSValue 類型設置空值,對NSString設空值變不會走設置的任何方法
當找不到時能夠用一下方法能夠檢測到
- (void)setNilValueForKey:(NSString *)key; 複製代碼
三、找不到Key
當找不到時能夠用一下方法能夠檢測到
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 複製代碼
四、取值時 - 找不到 key
當找不到時能夠用一下方法能夠檢測到
- (nullable id)valueForUndefinedKey:(NSString *)key;
複製代碼
五、鍵值驗證
在此方法能夠作一些容錯處理
- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError; 複製代碼