1. KVC的使用
KVC
的全稱是Key-Value Coding
缅茉,也就是鍵值編碼
,我們可以通過(guò)一個(gè)key來(lái)設(shè)置或獲取某個(gè)屬性的值。KVC
所用到的API如下:
// 通過(guò)key設(shè)置屬性值
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
// 通過(guò)key獲取屬性值
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
我們看到設(shè)置和獲取屬性值的方法都有2個(gè),一個(gè)是key
一個(gè)是keyPath
這兩個(gè)有什么區(qū)別呢刑棵?直接看下面示例吧,我們先定義如下2個(gè)類:
// Dog類
#import <Foundation/Foundation.h>
@interface Dog : NSObject
@property (nonatomic , assign) NSInteger age;
@end
// Student類
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Student : NSObject
@property (nonatomic , strong) NSString *name;
@property (nonatomic , strong) Dog *dog;
@end
如果我們要設(shè)置或獲取Student
實(shí)例對(duì)象的name
屬性值愚铡,通過(guò)key
或keyPath
方式都是一樣的:
- (void)test{
self.stu1 = [[Student alloc] init];
self.stu1.dog = [[Dog alloc] init];
[self.stu1 setValue:@"Jack" forKey:@"name"];
NSLog(@"通過(guò)key的方式--%@",[self.stu1 valueForKey:@"name"]);
[self.stu1 setValue:@"Bob" forKeyPath:@"name"];
NSLog(@"通過(guò)keyPath的方式--%@",[self.stu1 valueForKeyPath:@"name"]);
}
但是如果我們要設(shè)置或獲取Student
實(shí)例對(duì)象的dog
的age
屬性值蛉签,那就只能通過(guò)keyPath
的方式了。此時(shí)如果還是使用key
的方式設(shè)置屬性值的話就會(huì)拋出setValue:forUndefinedKey:
的異常沥寥。
- (void)test{
self.stu1 = [[Student alloc] init];
self.stu1.dog = [[Dog alloc] init];
[self.stu1 setValue:@5 forKeyPath:@"dog.age"];
NSLog(@"%@",[self.stu1 valueForKeyPath:@"dog.age"]);
}
2 KVC設(shè)置屬性值的流程
我們以setValue:forKey:
為例碍舍,KVC
設(shè)置屬性值的整個(gè)流程如下圖所示:
比如我們執(zhí)行
[self.stu1 setValue:@"Jack" forKey:@"name"];
這句代碼,其底層執(zhí)行流程如下:
- 首先找到
self.stu1
的isa
指向的類對(duì)象营曼,在類對(duì)象的方法列表中按照setName
乒验、_setName
的順序進(jìn)行查找(也就是先查找看有沒(méi)有setName
這個(gè)方法愚隧,沒(méi)有的話再查找有沒(méi)有_setName
方法)蒂阱。 - 前面如果查找到了方法,就調(diào)用方法狂塘。
- 前面如果沒(méi)有查找到方法录煤,底層就會(huì)調(diào)用
Student
類的+ (BOOL)accessInstanceVariablesDirectly;
方法。如果返回值是YES
就表示允許直接訪問(wèn)類的成員變量荞胡,返回NO
表示不允許妈踊。這個(gè)方法是需要我們?cè)?code>Student類中重寫的,由開(kāi)發(fā)者來(lái)決定是否允許訪問(wèn)成員變量泪漂,如果不重寫廊营,默認(rèn)是返回YES。 - 如果上一步返回的是
NO
萝勤,那會(huì)直接拋出異常setValue:forUndefinedKey:
露筒。 - 如果返回的是
YES
的話,那就會(huì)按照_name
敌卓、_isName
慎式、name
、isName
這樣一個(gè)順序來(lái)查找類對(duì)象中的成員屬性列表趟径,如果找到了就直接賦值瘪吏;如果沒(méi)有找到就拋出異常setValue:forUndefinedKey:
。
3 KVC獲取屬性值的流程
KVC
獲取屬性值的流程圖如下:
比如我們執(zhí)行[self.stu1 valueForKey:@"name"];
這句代碼蜗巧,其底層執(zhí)行流程如下:
- 首先找到self.stu1的isa指向的類對(duì)象掌眠,在類對(duì)象的方法列表中按照
getName
、name
幕屹、isName
蓝丙、_name
的順序進(jìn)行查找刑枝。 - 上一步如果查找到了方法,就直接調(diào)用方法迅腔。
- 如果沒(méi)有找到方法装畅,就會(huì)調(diào)用
Student
類的+ (BOOL)accessInstanceVariablesDirectly;
方法,看其返回的是YES還是NO沧烈; - 如果上一步返回的是
NO
掠兄,就直接拋出異常valueForUndefinedKey:
。 - 如果返回的是
YES
锌雀,就會(huì)按照_name
蚂夕、_isName
、name
腋逆、isName
這樣一個(gè)順序來(lái)查找類對(duì)象中的成員屬性列表婿牍,如果找到了就直接取值;如果沒(méi)有找到就拋出異常valueForUndefinedKey:
惩歉。