版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.09.08 |
前言
KVC
相信大家再熟悉不過(guò)了骄呼,鍵值編碼币喧,可以解決很多問(wèn)題债沮,包括視圖上的給UITextField
占位文字顏色大小進(jìn)行設(shè)置等等,還有很多地方可以用KVC鹿鳖,接下來(lái)幾篇我們就深度解析一下KVC扁眯∽秤ǎ總結(jié)一下翅帜,就是指iOS的開(kāi)發(fā)中,可以允許開(kāi)發(fā)者通過(guò)Key名直接訪問(wèn)對(duì)象的屬性命满,或者給對(duì)象的屬性賦值涝滴。而不需要調(diào)用明確的存取方法。這樣就可以在運(yùn)行時(shí)動(dòng)態(tài)地訪問(wèn)和修改對(duì)象的屬性胶台。而不是在編譯時(shí)確定歼疮,這也是iOS開(kāi)發(fā)中的黑魔法之一。還是老規(guī)矩诈唬,由面到點(diǎn)韩脏,由淺到深,希望對(duì)大家有所幫助铸磅。感興趣的可以看我寫(xiě)的另外幾篇文章赡矢。
1. KVC解析(一) —— 基本了解
KVC賦值
賦值的過(guò)程看似很簡(jiǎn)單,就是找到對(duì)應(yīng)的key阅仔,然后將一個(gè)已經(jīng)準(zhǔn)備好的值賦過(guò)去吹散,但是你知道深層次的原理嗎?方法- (nullable id)valueForKey:(NSString *)key;
中的key系統(tǒng)內(nèi)部和底層是按照什么邏輯找的呢八酒?下面就讓我們看一下空民。
下面就是尋找key的內(nèi)部流程。以[self setValue:@"小明" forKey:@"name"];
這句代碼作為例子進(jìn)行說(shuō)明羞迷。
程序優(yōu)先調(diào)用
set<Key>:
屬性值方法界轩,代碼通過(guò)setter
方法完成設(shè)置。注意衔瓮,這里的<key>
是指成員變量名耸棒,首字母大小寫(xiě)要符合KVC的命名規(guī)則,下同报辱。如果沒(méi)有找到
setName:
方法与殃,KVC機(jī)制會(huì)檢查+ (BOOL)accessInstanceVariablesDirectly
方法有沒(méi)有返回YES
单山,默認(rèn)該方法會(huì)返回YES,如果你重寫(xiě)了該方法讓其返回NO
的話幅疼,那么在這一步KVC會(huì)執(zhí)行setValue:forUndefinedKey:
方法米奸,不過(guò)一般開(kāi)發(fā)者不會(huì)這么做。所以KVC機(jī)制會(huì)搜索該類(lèi)里面有沒(méi)有名為_<key>
的成員變量爽篷,無(wú)論該變量是在類(lèi)接口處定義悴晰,還是在類(lèi)實(shí)現(xiàn)處定義,也無(wú)論用了什么樣的訪問(wèn)修飾符逐工,只在存在以_<key>
命名的變量铡溪,KVC都可以對(duì)該成員變量賦值。如果該類(lèi)即沒(méi)有
set<key>:
方法泪喊,也沒(méi)有_<key>
成員變量棕硫,KVC機(jī)制會(huì)搜索_is<Key>
的成員變量。和上面一樣袒啼,如果該類(lèi)即沒(méi)有
set<Key>:
方法哈扮,也沒(méi)有_<key>
和_is<Key>
成員變量,KVC機(jī)制再會(huì)繼續(xù)搜索<key>
和is<Key>
的成員變量蚓再。再給它們賦值滑肉。如果上面列出的方法或者成員變量都不存在,系統(tǒng)將會(huì)執(zhí)行該對(duì)象的
setValue:forUndefinedKey:
方法摘仅,默認(rèn)是拋出異常靶庙。特別需要注意的是:如果開(kāi)發(fā)者想讓這個(gè)類(lèi)禁用KVC里,那么重寫(xiě)
+ (BOOL)accessInstanceVariablesDirectly
方法讓其返回NO
即可娃属,這樣的話如果KVC沒(méi)有找到set<Key>:
屬性名時(shí)六荒,會(huì)直接用setValue:forUndefinedKey:
方法。
下面結(jié)合例子進(jìn)行說(shuō)明
1. + (BOOL)accessInstanceVariablesDirectly 返回NO實(shí)例
#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(@"沒(méi)有找到該key對(duì)應(yīng)的屬性會(huì)拋出異常");
}
@end
下面看輸出結(jié)果
2017-09-08 21:50:31.966 JJOC[8051:216850] 沒(méi)有找到該key對(duì)應(yīng)的屬性會(huì)拋出異常
2017-09-08 21:50:31.966 JJOC[8051:216850] name = (null)
這里+ (BOOL)accessInstanceVariablesDirectly
返回了NO膳犹,也就是說(shuō)找不到屬性的setter
方法恬吕,那么不會(huì)再去找實(shí)例變量,所以會(huì)輸出上面的結(jié)果须床。所以name
輸出結(jié)果就是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
看輸出結(jié)果
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
看輸出結(jié)果
2017-09-08 22:00:44.908 JJOC[8417:223934] name = 小明
4. _isName
看輸出結(jié)果
#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
看輸出結(jié)果
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
看輸出結(jié)果
2017-09-08 22:13:24.258 JJOC[8797:231346] name = 小明
下面就是我們這個(gè)屬性和實(shí)例變量的調(diào)用順序。
self.name
_name
_isName
name
isName
上面的順序親測(cè)豺旬,是按照所列順序執(zhí)行的钠惩。
后記
未完,待續(xù)~~