版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.09.08 |
前言
KVC
相信大家再熟悉不過了,鍵值編碼,可以解決很多問題,包括視圖上的給UITextField
占位文字顏色大小進行設置等等,還有很多地方可以用KVC,接下來幾篇我們就深度解析一下KVC。總結一下,就是指iOS的開發中,可以允許開發者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值。而不需要調用明確的存取方法。這樣就可以在運行時動態地訪問和修改對象的屬性。而不是在編譯時確定,這也是iOS開發中的黑魔法之一。還是老規矩,由面到點,由淺到深,希望對大家有所幫助。感興趣的可以看我寫的另外幾篇文章。
1. KVC解析(一) —— 基本了解
KVC賦值
賦值的過程看似很簡單,就是找到對應的key,然后將一個已經準備好的值賦過去,但是你知道深層次的原理嗎?方法- (nullable id)valueForKey:(NSString *)key;
中的key系統內部和底層是按照什么邏輯找的呢?下面就讓我們看一下。
下面就是尋找key的內部流程。以[self setValue:@"小明" forKey:@"name"];
這句代碼作為例子進行說明。
程序優先調用
set<Key>:
屬性值方法,代碼通過setter
方法完成設置。注意,這里的<key>
是指成員變量名,首字母大小寫要符合KVC的命名規則,下同。如果沒有找到
setName:
方法,KVC機制會檢查+ (BOOL)accessInstanceVariablesDirectly
方法有沒有返回YES
,默認該方法會返回YES,如果你重寫了該方法讓其返回NO
的話,那么在這一步KVC會執行setValue:forUndefinedKey:
方法,不過一般開發者不會這么做。所以KVC機制會搜索該類里面有沒有名為_<key>
的成員變量,無論該變量是在類接口處定義,還是在類實現處定義,也無論用了什么樣的訪問修飾符,只在存在以_<key>
命名的變量,KVC都可以對該成員變量賦值。如果該類即沒有
set<key>:
方法,也沒有_<key>
成員變量,KVC機制會搜索_is<Key>
的成員變量。和上面一樣,如果該類即沒有
set<Key>:
方法,也沒有_<key>
和_is<Key>
成員變量,KVC機制再會繼續搜索<key>
和is<Key>
的成員變量。再給它們賦值。如果上面列出的方法或者成員變量都不存在,系統將會執行該對象的
setValue:forUndefinedKey:
方法,默認是拋出異常。特別需要注意的是:如果開發者想讓這個類禁用KVC里,那么重寫
+ (BOOL)accessInstanceVariablesDirectly
方法讓其返回NO
即可,這樣的話如果KVC沒有找到set<Key>:
屬性名時,會直接用setValue:forUndefinedKey:
方法。
下面結合例子進行說明
1. + (BOOL)accessInstanceVariablesDirectly 返回NO實例
#import "JJKVCSetValueVC.h"
@interface JJKVCSetValueVC ()
@end
@implementation JJKVCSetValueVC
{
NSString *name;
}
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", name);
}
+ (BOOL)accessInstanceVariablesDirectly
{
return NO;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"沒有找到該key對應的屬性會拋出異常");
}
@end
下面看輸出結果
2017-09-08 21:50:31.966 JJOC[8051:216850] 沒有找到該key對應的屬性會拋出異常
2017-09-08 21:50:31.966 JJOC[8051:216850] name = (null)
這里+ (BOOL)accessInstanceVariablesDirectly
返回了NO,也就是說找不到屬性的setter
方法,那么不會再去找實例變量,所以會輸出上面的結果。所以name
輸出結果就是null
。
2. name
看示例代碼
#import "JJKVCSetValueDemoVC.h"
@interface JJKVCSetValueDemoVC ()
@end
@implementation JJKVCSetValueDemoVC
{
NSString *name;
}
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", name);
}
@end
看輸出結果
2017-09-08 21:58:04.150 JJOC[8265:221362] name = 小明
3. _name
看示例代碼
#import "JJKVCSetValueDemoVC.h"
@interface JJKVCSetValueDemoVC ()
@end
@implementation JJKVCSetValueDemoVC
{
NSString *_name;
}
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", _name);
}
@end
看輸出結果
2017-09-08 22:00:44.908 JJOC[8417:223934] name = 小明
4. _isName
看輸出結果
#import "JJKVCSetValueDemoVC.h"
@interface JJKVCSetValueDemoVC ()
@end
@implementation JJKVCSetValueDemoVC
{
NSString *_isName;
}
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", _isName);
}
@end
看輸出結果
2017-09-08 22:03:21.038 JJOC[8581:226017] name = 小明
5. isName
看示例代碼
#import "JJKVCSetValueDemoVC.h"
@interface JJKVCSetValueDemoVC ()
@end
@implementation JJKVCSetValueDemoVC
{
NSString *isName;
}
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
[self setValue:@"小明" forKey:@"name"];
NSLog(@"name = %@", isName);
}
@end
看輸出結果
2017-09-08 22:13:24.258 JJOC[8797:231346] name = 小明
下面就是我們這個屬性和實例變量的調用順序。
self.name
_name
_isName
name
isName
上面的順序親測,是按照所列順序執行的。
后記
未完,待續~~