SDWebImage學(xué)習(xí)筆記之KVC

KVC概述

KVC全稱是Key-Value-Coding,NSObject類及其子類和內(nèi)建基本數(shù)據(jù)類型都可以通過KVC的方法賦值和取值氓鄙,不需要通過get和set璧坟。


KVC使用

以一個(gè)保存多個(gè)字典的數(shù)組的取值為例:

NSArray *jjArray = @[@{@"name": @"aa", @"age": @(15)},
                    @{@"name": @"bb", @"age": @(16)},
                    @{@"name": @"cc", @"age": @(17)}];
// 遍歷數(shù)組獲取key為"name"時(shí)對應(yīng)的value
NSMutableArray *nameArray1 = [NSMutableArray array];
for (NSDictionary *dict in jjArray) {
    [nameArray1 addObject:dict[@"name"]];
}
NSLog(@"nameArray1: %@", nameArray1);
// KVC方法篩選key為"name"時(shí)對應(yīng)的value,返回一個(gè)數(shù)組
NSArray *nameArray2 = [jjArray valueForKey:@"name"];
NSLog(@"nameArray2: %@", nameArray2);

日志輸出:

nameArray1: (
    aa,
    bb,
    cc
)
nameArray2: (
    aa,
    bb,
    cc
)

遍歷數(shù)組和KVC的方式得到了相同的結(jié)果讶隐,但明顯KVC方式的代碼量要遠(yuǎn)小于遍歷的方式起胰。

對于復(fù)雜的數(shù)據(jù)結(jié)構(gòu),例如自定義類巫延,KVC也可以快速的取值效五。

// 定義一個(gè)Teacher類
@interface Teacher : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// 定義一個(gè)Student類
@interface Student : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// ViewController.m
NSMutableArray *aArray = [[NSMutableArray alloc] initWithCapacity:0];
Teacher *teacher = [[Teacher alloc] init];
// 屬性賦值
teacher.name = @"teacher";
teacher.age = 30;
// KVC賦值
//    [teacher setValue:@"teacher" forKey:@"name"];
//    [teacher setValue:@30 forKey:@"age"];
[aArray addObject:teacher];
for (int i = 0; i < 3; i++) {
    Student *student = [[Student alloc] init];
    student.name = [NSString stringWithFormat:@"student%d", i];
    student.age = i;
    [aArray addObject:student];
}
// KVC方法篩選每個(gè)對象中key值為"name"的value
NSArray *teacherArray = [aArray valueForKey:@"name"];
// aArray保存了1個(gè)Teacher對象和3個(gè)Student對象,每個(gè)對象中都有name屬性烈评,所以teacherArray數(shù)組會(huì)有4個(gè)值火俄。
NSLog(@"teacherArray: %@", teacherArray);

日志輸出:

nameArray: (
    teacher,
    student0,
    student1,
    student2
)

對于自定義類中含有自定義類的情況,valueForKey: 方法已經(jīng)無法獲取到正確的值了讲冠,需要調(diào)用valueForKeyPath: 方法傳入屬性的路徑來獲取(xxx.xxx.xxx)瓜客。

// Teacher.h
@interface Teacher : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
// 在Teacher類中增加一個(gè)Student類型的數(shù)據(jù)屬性students
@property (nonatomic, strong) NSMutableArray<Student *> *students;

@end

// ViewController.m
NSMutableArray *aArray = [[NSMutableArray alloc] initWithCapacity:0];
Teacher *teacher = [[Teacher alloc] init];
teacher.name = @"teacher";
teacher.age = 30;
teacher.students = [NSMutableArray array];
[aArray addObject:teacher];
for (int i = 0; i < 3; i++) {
    Student *student = [[Student alloc] init];
    student.name = [NSString stringWithFormat:@"student%d", i];
    student.age = i;
    [teacher.students addObject:student];
}
NSArray *nameArray = [aArray valueForKey:@"name"];
NSArray *studentNameArray = [aArray valueForKeyPath:@"students.name"];
NSLog(@"nameArray: %@, studentNameArray: %@", nameArray, studentNameArray);

日志輸出:

nameArray: (
    teacher
), 
studentNameArray: (
        (
        student0,
        student1,
        student2
    )
)

在這段代碼中,[aArray valueForKey:@"name"]方法只獲取到了teacher對象name屬性的值竿开,而[aArray valueForKeyPath:@"students.name"]才能獲取到student對象name屬性的值谱仪。


KVC的實(shí)現(xiàn)

在網(wǎng)上查了一些資料,對KVC的鍵值查找方式否彩、KVC的實(shí)現(xiàn)原理和內(nèi)部機(jī)制做了詳細(xì)的闡述疯攒,本文直接引用過來。

KVC鍵值查找

setValue:forKey:搜索方式

1列荔、首先搜索setKey:方法敬尺。(key指成員變量名枚尼,首字母大寫)

2、上面的setter方法沒找到砂吞,如果類方法accessInstanceVariablesDirectly返回YES署恍。那么按 _key,_isKey蜻直,key盯质,iskey的順序搜索成員名。(NSKeyValueCodingCatogery中實(shí)現(xiàn)的類方法概而,默認(rèn)實(shí)現(xiàn)為返回YES)

3呼巷、如果沒有找到成員變量,調(diào)用setValue:forUnderfinedKey:

valueForKey:的搜索方式

1赎瑰、首先按getKey王悍,key,isKey的順序查找getter方法乡范,找到直接調(diào)用配名。如果是BOOL、int等內(nèi)建值類型晋辆,會(huì)做NSNumber的轉(zhuǎn)換渠脉。

2、上面的getter沒找到瓶佳,查找countOfKey芋膘、objectInKeyAtindex、KeyAtindexes格式的方法霸饲。如果countOfKey和另外兩個(gè)方法中的一個(gè)找到为朋,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合的NSArray消息方法。

3厚脉、還沒找到习寸,查找countOfKey、enumeratorOfKey傻工、memberOfKey格式的方法霞溪。如果這三個(gè)方法都找到,那么就返回一個(gè)可以響應(yīng)NSSet所有方法的代理集合中捆。
4鸯匹、還是沒找到,如果類方法accessInstanceVariablesDirectly返回YES泄伪。那么按 _key殴蓬,_isKey,key蟋滴,iskey的順序搜索成員名染厅。

5痘绎、再?zèng)]找到,調(diào)用valueForUndefinedKey肖粮。
KVC實(shí)現(xiàn)原理

KVC運(yùn)用了isa-swizzing技術(shù)简逮。isa-swizzing就是類型混合指針機(jī)制。KVC通過isa-swizzing實(shí)現(xiàn)其內(nèi)部查找定位尿赚。isa指針(is kind of 的意思)指向維護(hù)分發(fā)表的對象的類,該分發(fā)表實(shí)際上包含了指向?qū)崿F(xiàn)類中的方法的指針和其他數(shù)據(jù)蕉堰。

比如說如下的一行KVC代碼:

[site setValue:@"sitename" forKey:@"name"];

//會(huì)被編譯器處理成

SEL sel = sel_get_uid(setValue:forKey);
IMP method = objc_msg_loopup(site->isa,sel);
method(site,sel,@"sitename",@"name");

每個(gè)類都有一張方法表凌净,是一個(gè)hash表,值是還書指針I(yè)MP屋讶,SEL的名稱就是查表時(shí)所用的鍵冰寻。
SEL數(shù)據(jù)類型:查找方法表時(shí)所用的鍵。定義成char*皿渗,實(shí)質(zhì)上可以理解成int值斩芭。
IMP數(shù)據(jù)類型:他其實(shí)就是一個(gè)編譯器內(nèi)部實(shí)現(xiàn)時(shí)候的函數(shù)指針。當(dāng)Objective-C編譯器去處理實(shí)現(xiàn)一個(gè)方法的時(shí)候乐疆,就會(huì)指向一個(gè)IMP對象划乖,這個(gè)對象是C語言表述的類型。

KVC內(nèi)部機(jī)制

一個(gè)對象在調(diào)用setValue的時(shí)候進(jìn)行了如下操作:

  1. 根據(jù)方法名找到運(yùn)行方法的時(shí)候需要的環(huán)境參數(shù)
  2. 他會(huì)從自己的isa指針結(jié)合環(huán)境參數(shù)挤土,找到具體的方法實(shí)現(xiàn)接口
  3. 再直接查找得來的具體的實(shí)現(xiàn)方法

引用地址:http://www.cnblogs.com/zy1987/p/4616063.html


KVC在SDWebImage中的應(yīng)用

SDWebImage庫Downloader模塊中的SDWebImageDownloaderOperation類負(fù)責(zé)執(zhí)行下載任務(wù)琴庵,它定義了一個(gè)屬性callbackBlocks

typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;

@property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;

callbackBlocks是一個(gè)可變素組,其中每個(gè)元素是SDCallbacksDictionary類型的字典仰美,用鍵值對的方式保存每個(gè)下載任務(wù)的progressBlock和completedBlock迷殿。progressBlock和completedBlock由外部傳入,負(fù)責(zé)下載過程中和下載完成時(shí)或下載異常情況的處理咖杂。

假如想取到其中所有的progressBlock或completedBlock庆寺,一種方法是遍歷callbackBlocks數(shù)組,根據(jù)key來獲取value诉字,保存到一個(gè)新的數(shù)組中懦尝。另一種快速的方式就是KVC,源代碼如下:

- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    LOCK(self.callbacksLock);
    [self.callbackBlocks addObject:callbacks];
    UNLOCK(self.callbacksLock);
    return callbacks;
}

- (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
    LOCK(self.callbacksLock);
    NSMutableArray<id> *callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
    UNLOCK(self.callbacksLock);
    // We need to remove [NSNull null] because there might not always be a progress block for each callback
    [callbacks removeObjectIdenticalTo:[NSNull null]];
    return [callbacks copy]; // strip mutability here
}

callbacksLock屬性是一個(gè)信號量鎖奏窑,初始值為1导披,表示同時(shí)期只能有一個(gè)線程來訪問callbacks,保證callbacks數(shù)組在賦值和取值過程中的線程安全埃唯。

@property (strong, nonatomic, nonnull) dispatch_semaphore_t callbacksLock;
// 設(shè)置信號量為1 
_callbacksLock = dispatch_semaphore_create(1);

總結(jié)

雖然KVC在效率上要優(yōu)于遍歷的方式撩匕,但是濫用KVC會(huì)導(dǎo)致異常問題的出現(xiàn),假如valueForKey: 或valueForKeyPath:傳入了不存在的key值墨叛,那么就會(huì)導(dǎo)致程序崩潰止毕。所以在沒有把握的情況下模蜡,慎用KVC!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扁凛,一起剝皮案震驚了整個(gè)濱河市忍疾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谨朝,老刑警劉巖卤妒,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異字币,居然都是意外死亡则披,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門洗出,熙熙樓的掌柜王于貴愁眉苦臉地迎上來士复,“玉大人,你說我怎么就攤上這事翩活≮搴椋” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵菠镇,是天一觀的道長冗荸。 經(jīng)常有香客問我,道長利耍,這世上最難降的妖魔是什么俏竞? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮堂竟,結(jié)果婚禮上魂毁,老公的妹妹穿的比我還像新娘。我一直安慰自己出嘹,他們只是感情好席楚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著税稼,像睡著了一般烦秩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郎仆,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天只祠,我揣著相機(jī)與錄音,去河邊找鬼扰肌。 笑死抛寝,一個(gè)胖子當(dāng)著我的面吹牛耽装,可吹牛的內(nèi)容都是我干的卷扮。 我是一名探鬼主播涯贞,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扎谎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钻趋?” 一聲冷哼從身側(cè)響起川陆,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛮位,沒想到半個(gè)月后较沪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡失仁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年购对,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陶因。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垂蜗,靈堂內(nèi)的尸體忽然破棺而出楷扬,到底是詐尸還是另有隱情,我是刑警寧澤贴见,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布烘苹,位于F島的核電站,受9級特大地震影響片部,放射性物質(zhì)發(fā)生泄漏镣衡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一档悠、第九天 我趴在偏房一處隱蔽的房頂上張望廊鸥。 院中可真熱鬧,春花似錦辖所、人聲如沸惰说。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吆视。三九已至,卻和暖如春酥宴,著一層夾襖步出監(jiān)牢的瞬間啦吧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工拙寡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留授滓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像褒墨,于是被迫代替她去往敵國和親炫刷。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容