最近發(fā)現(xiàn)之前看的東西沒一會(huì)就忘記了唤崭,所以寫來好一些颗味,也建議大家吧學(xué)到了記錄下來這樣加深印象扯俱,也可以幫助到別人书蚪。
首先附上參考鏈接1,參考鏈接2迅栅,參考鏈接3--KVC詳解
1.KVC--鍵值編碼(Key-value coding):
在某種程度上跟map(C++的函數(shù)殊校,所以多學(xué)吧。读存。为流。)的關(guān)系匪淺。它提供了一種使用字符串而不是訪問器方法去訪問一個(gè)對象實(shí)例變量的機(jī)制让簿。
實(shí)現(xiàn)方法:
KVC運(yùn)用了一個(gè)isa-swizzling(isa指針混寫)技術(shù)敬察,任何對象都有isa指針。KVC主要通過isa-swizzling尔当,來實(shí)現(xiàn)其內(nèi)部查找定位的:
(1).實(shí)例方法調(diào)用時(shí)莲祸,通過對象的 isa 在類中獲取方法的實(shí)現(xiàn)
(2).類方法調(diào)用時(shí),通過類的 isa 在元類中獲取方法的實(shí)現(xiàn)
實(shí)現(xiàn)原理:
1.當(dāng)調(diào)用setValue: forKey:@"name:的方法時(shí)椭迎,底層的執(zhí)行機(jī)制如下:
注意锐帜,這里的<key>是指成員變量名,首字母大清寫要符合KVC的全名規(guī)則
程序優(yōu)先調(diào)用set<Key>:屬性值方法畜号,代碼通過setter方法完成設(shè)置缴阎。
如果沒有找到setName:方法,KVC機(jī)制會(huì)檢查+ (BOOL)accessInstanceVariablesDirectly方法有沒有返回YES简软,默認(rèn)該方法會(huì)返回YES蛮拔,如果你重寫了該方法讓其返回NO的話,那么在這一步KVC會(huì)執(zhí)行setValue:forUNdefinedKey:方法痹升,不過一般開發(fā)者不會(huì)這么做建炫。所以KVC機(jī)制會(huì)搜索該類里面有沒有名為<key>的成員變量,無論該變量是在類接口部分定義视卢,還是在類實(shí)現(xiàn)部分定義踱卵,也無論用了什么樣的訪問修飾符,只在存在以<key>命名的變量据过,KVC都可以對該成員變量賦值。
如果該類即沒有set<Key>:方法妒挎,也沒有_<key>成員變量绳锅,KVC機(jī)制會(huì)搜索_is<Key>的成員變量
和上面一樣,如果該類即沒有set<Key>:方法酝掩,也沒有_<key>和_is<Key>成員變量鳞芙,KVC機(jī)制再會(huì)繼續(xù)搜索<key>和is<Key>的成員變量。再給它們賦值。
如果上面列出的方法或者成員變量都不存在原朝,系統(tǒng)將會(huì)執(zhí)行該對象的setValue:forUNdefinedKey:方法驯嘱,默認(rèn)是拋出異常。
如果開發(fā)者想讓這個(gè)類禁用KVC里喳坠,那么重寫+ (BOOL)accessInstanceVariablesDirectly
方法讓其返回NO即可鞠评,這樣的話如果KVC沒有找到set<Key>:屬性名時(shí),會(huì)直接用setValue:forUNdefinedKey:方法壕鹉。
2.當(dāng)調(diào)用ValueforKey:@”name“的代碼時(shí)剃幌,KVC對key的搜索方式不同于setValue:屬性值 forKey:@”name“,其搜索方式如下
首先按get<Key>,<key>,is<Key>的順序方法查找getter方法晾浴,找到的話會(huì)直接調(diào)用负乡。如果是BOOL或者int等值類型, 會(huì)做NSNumber轉(zhuǎn)換
如果上面的getter沒有找到脊凰,KVC則會(huì)查找countOf<Key>,objectIn<Key>AtIndex,<Key>AtIndex格式的方法抖棘。如果countOf<Key>和另外兩個(gè)方法中的要個(gè)被找到,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所的方法的代理集合(它是NSKeyValueArray狸涌,是NSArray的子類)钉答,調(diào)用這個(gè)代理集合的方法,或者說給這個(gè)代理集合發(fā)送NSArray的方法杈抢,就會(huì)以countOf<Key>,objectIn<Key>AtIndex,<Key>AtIndex這幾個(gè)方法組合的形式調(diào)用数尿。還有一個(gè)可選的get<Ket>:range:方法。所以你想重新定義KVC的一些功能惶楼,你可以添加這些方法右蹦,需要注意的是你的方法名要符合KVC的標(biāo)準(zhǔn)命名方法,包括方法簽名歼捐。
如果上面的方法沒有找到何陆,那么會(huì)查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>格式的方法豹储。如果這三個(gè)方法都找到贷盲,那么就返回一個(gè)可以響應(yīng)NSSet所的方法的代理集合,以送給這個(gè)代理集合消息方法剥扣,就會(huì)以countOf<Key>巩剖,enumeratorOf<Key>,memberOf<Key>組合的形式調(diào)用。
如果還沒有找到钠怯,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為)佳魔,那么和先前的設(shè)值一樣,會(huì)按_<key>,_is<Key>,<key>,is<Key>的順序搜索成員變量名晦炊,這里不推薦這么做鞠鲜,因?yàn)檫@樣直接訪問實(shí)例變量破壞了封裝性宁脊,使代碼更脆弱。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話贤姆,那么會(huì)直接調(diào)用valueForUndefinedKey:
還沒有找到的話榆苞,調(diào)用valueForUndefinedKey:
KVC相關(guān)技術(shù):
1.KVC的主要方法:
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
這里要說一下keyPath:它是一個(gè)被點(diǎn)操作符隔開的用于訪問對象的指定屬性的字符串序列。(就像平時(shí)使用的的屬性)
[people1 setValue:@"USA" forKeyPath:@"address.country"];
country1 = people1.address.country;
country2 = [people1 valueForKeyPath:@"address.country"];
2.點(diǎn)語法和KVC
在實(shí)現(xiàn)了訪問器方法的類中霞捡,使用點(diǎn)語法和KVC訪問對象其實(shí)差別不大坐漏,二者可以任意混用。但是沒有訪問起方法的類中弄砍,點(diǎn)語法無法使用仙畦,這時(shí)KVC就有優(yōu)勢了。)
3.一對多關(guān)系(To-Many)中的集合訪問器方法
一對一應(yīng)該很容易理解音婶,一對多呢就是當(dāng)你的對象里又多個(gè)屬性呢(例子:Person類中的name屬性慨畸,每個(gè)人只有一個(gè)名字。但也有一對多的關(guān)系衣式,比如Person中有一個(gè)friendsName屬性寸士,這是個(gè)集合(在Objective-C中可以是NSArray,NSSet等)碴卧,保存的是一個(gè)人的所有朋友的名字弱卡。)
當(dāng)操作一對多的屬性中的內(nèi)容時(shí),我們有兩種選擇:
①間接操作
先通過KVC方法取到集合屬性住册,然后通過集合屬性操作集合中的元素婶博。比較習(xí)慣用這個(gè)方法。
即通過鍵值對(key-value)取出這個(gè)實(shí)例荧飞,然后取屬性
②直接操作
蘋果為我們提供了一些方法模板凡人,我們可以以規(guī)定的格式實(shí)現(xiàn)這些方法來達(dá)到訪問集合屬性中元素的目的。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
4叹阔、鍵值驗(yàn)證(Key-Value Validation)
VC提供了驗(yàn)證Key對應(yīng)的Value是否可用的方法挠轴,KVC是不會(huì)自動(dòng)調(diào)用鍵值驗(yàn)證方法的,就是說我們需要手動(dòng)驗(yàn)證耳幢。
當(dāng)開發(fā)者需要驗(yàn)證能不能用KVC設(shè)定某個(gè)值時(shí)岸晦,可以調(diào)用validateValue: forKey:這個(gè)方法來驗(yàn)證,如果這個(gè)類的開發(fā)者實(shí)現(xiàn)了-(BOOL)validate<Key>:error:這個(gè)方法睛藻,那么KVC就會(huì)直接調(diào)用這個(gè)方法來返回启上,如果沒有,就直接返回YES.
#注意:
KVC在設(shè)值時(shí)不會(huì)主動(dòng)去做驗(yàn)證修档,所以即使你在類里面寫了驗(yàn)證方法碧绞,但是KVC因?yàn)椴粫?huì)去主動(dòng)驗(yàn)證,需要開發(fā)者手動(dòng)去驗(yàn)證。
但是有些技術(shù)吱窝,比如CoreData會(huì)自動(dòng)調(diào)用讥邻。
4、KVC的使用
- KVC支持?jǐn)?shù)值和結(jié)構(gòu)體型屬性
KVC可以自動(dòng)的將數(shù)值或結(jié)構(gòu)體型的數(shù)據(jù)打包或解包成NSNumber或NSValue對象院峡,以達(dá)到適配的目的兴使。當(dāng)然還有很多其他的,這里就舉幾個(gè)例子
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithDouble:(double)value;
+ (NSNumber *)numberWithBool:(BOOL)value;
這是將NSValue主要用于處理結(jié)構(gòu)體型的數(shù)據(jù)
// 這是將基本類型轉(zhuǎn)為NSValue照激,其中发魄,result是基本數(shù)據(jù)的值,int是我們要轉(zhuǎn)化的基本數(shù)據(jù)類型
NSValue *value = [NSValue valueWithBytes:&result objCType:@encode(int)];
// NSValue轉(zhuǎn)化成基本數(shù)據(jù)類型,value是一個(gè)NSValue類型的對象俩垃,result是一個(gè)已知的類型的基本數(shù)據(jù)類型励幼。
// 經(jīng)過這樣的轉(zhuǎn)化,NSValue中保存的數(shù)值就放到了result中了口柳。
[value getValue:&result];
// 這里只列舉一些苹粟,相信其他的你也可以自己試出來
+ (NSValue *)valueWithCGPoint:(CGPoint)point;
+ (NSValue *)valueWithCGSize:(CGSize)size;
+ (NSValue *)valueWithCGRect:(CGRect)rect;
- KVC和字典
當(dāng)對NSDictionary對象使用KVC時(shí),valueForKey:的表現(xiàn)行為和objectForKey:一樣跃闹。所以使用valueForKeyPath:用來訪問多層嵌套的字典是比較方便的嵌削。
// dictionaryWithValuesForKeys: 是指輸入一組key,返回這組key對應(yīng)的屬性望艺,再組成一個(gè)字典苛秕。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
// setValuesForKeysWithDictionary 是用來修改Model中對應(yīng)key的屬性。下面直接用代碼會(huì)更直觀一點(diǎn)
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
用KVC來訪問和修改私有變量
對于自定義類里的私有屬性找默,我們可以直接利用kvc訪問這個(gè)屬性艇劫。KVC Collection Operators(集合操作)
集合操作:是一個(gè)特殊的Key Path,它是一個(gè)集合/數(shù)組通過調(diào)用valueForKeyPath:可允許一個(gè)集合中的對象屬性根據(jù)集合操作符做相應(yīng)的操作。
注意: 只能是這個(gè)方法惩激,如果傳給了valueForKey:方法保證你程序崩潰店煞。
集合操作符是一個(gè)以@開頭特殊的字符串.所有的集合操作,除了@count咧欣,其他都需要有右邊的keyPath(一般為屬性名)浅缸,目前還不支持自定義集合操作符。
集合操作符分為三種:
1.簡單的集合操作 返回NSString魄咕、NSNumber衩椒、NSDate
2.對象操作符 返回NSArray
3.數(shù)組或集合操作符 返回NSArray、NSSet
集合操作簡單例子
①Simple Collection Operators(簡單的操作符)
簡單集合運(yùn)算符共有@avg哮兰,@count毛萌,@max,@min喝滞,@sum5種
②Object Operator (對象操作符)
@distinctUnionOfObjects 返回一個(gè)由操作符右邊的key path所指定的對象屬性組成的數(shù)組阁将,不對數(shù)組去重
@unionOfObjects 返回一個(gè)由操作符右邊的key path所指定的對象屬性組成的數(shù)組,并對數(shù)組去重
③Array and Set Operators(數(shù)組和集合操作符)
@distinctUnionOfArrays和@unionOfArrays: 返回NSArray,distinct版本會(huì)對數(shù)組取重
@distinctUnionOfSets: 返回一個(gè)NSSet對象右遭,因?yàn)镾ets中的元素本身就是唯一的做盅,所以沒有對應(yīng)的@unionOfSets運(yùn)算符缤削。
2.KVO--鍵值觀察Key-value observing)
提供了一種當(dāng)其它對象屬性被修改的時(shí)候能通知當(dāng)前對象的機(jī)制。再M(fèi)VC大行其道的Cocoa中吹榴,KVO機(jī)制很適合實(shí)現(xiàn)model和controller類之間的通訊亭敢。---它是基于KVC實(shí)現(xiàn)的。
KVO實(shí)現(xiàn)原理:
當(dāng)某個(gè)類的對象第一次被添加觀察的時(shí)候图筹,
1.系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類(繼承自原本的類)帅刀,在這個(gè)派生類中重寫基類中任何被觀察屬性的 setter 方法。派生類在被重寫的 setter 方法實(shí)現(xiàn)真正的通知機(jī)制远剩,就如前面手動(dòng)實(shí)現(xiàn)鍵值觀察那樣扣溺。這么做是基于設(shè)置屬性會(huì)調(diào)用 setter 方法,而通過重寫就獲得了 KVO 需要的通知機(jī)制瓜晤。
2.同時(shí)派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個(gè)類锥余。然后系統(tǒng)將這個(gè)對象的 isa 指針指向這個(gè)新誕生的派生類,因此這個(gè)對象就成為該派生類的對象了活鹰,因而在該對象上對 setter 的調(diào)用就會(huì)調(diào)用重寫的 setter哈恰,從而激活鍵值通知機(jī)制。此外志群,派生類還重寫了 dealloc 方法來釋放資源着绷。
我的理解就是,在你添加觀察的時(shí)候呢锌云,系統(tǒng)就創(chuàng)建一個(gè)派生類荠医,然后派生類重寫setter,class等方法桑涎,然后把指向原本的類指針指向派生類彬向。由于重寫了class類,這樣你就以為是原來的類了攻冷。重寫seeter方法呢娃胆,就是為了實(shí)現(xiàn)觀察對象的改變
// 添加一下這兩個(gè)方法
// 實(shí)現(xiàn)kvo的關(guān)鍵--使用KVO,只要有will/didChangeValueForKey:方法就可以了等曼。
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
// 其中里烦,didChangeValueForKey:方法負(fù)責(zé)調(diào)用:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
// 如下
- (void)setNow:(NSDate *)aDate {
[self willChangeValueForKey:@"now"];
_now = aDate;
[self didChangeValueForKey:@"now"];
}
這樣就實(shí)現(xiàn)了KVO。在派生類重寫呢也可以節(jié)省內(nèi)存消耗禁谦。自己手動(dòng)實(shí)現(xiàn)這個(gè)呢胁黑,可以幫助理解吧可以學(xué)習(xí)學(xué)習(xí)。
這里補(bǔ)充一下KVO州泊、NSNotification丧蘸、delegate的區(qū)別:
KVC:
優(yōu)勢:
1.能夠提供一種簡單的方法實(shí)現(xiàn)兩個(gè)對象間的同步。例如:model和view之間同步遥皂;
2.能夠?qū)Ψ俏覀儎?chuàng)建的對象力喷,即內(nèi)部對象的狀態(tài)改變作出響應(yīng)刽漂,而且不需要改變內(nèi)部對象(SKD對象)的實(shí)現(xiàn);
3.能夠提供觀察的屬性的最新值以及先前值冗懦;
4.用key paths來觀察屬性爽冕,因此也可以觀察嵌套對象仇祭;
5.完成了對觀察對象的抽象披蕉,因?yàn)椴恍枰~外的代碼來允許觀察值能夠被觀察
缺點(diǎn):
1.我們觀察的屬性必須使用strings來定義。因此在編譯器不會(huì)出現(xiàn)警告以及檢查乌奇;
2.對屬性重構(gòu)將導(dǎo)致我們的觀察代碼不再可用没讲;
3.復(fù)雜的“IF”語句要求對象正在觀察多個(gè)值。這是因?yàn)樗械挠^察代碼通過一個(gè)方法來指向礁苗;
4.當(dāng)釋放觀察者時(shí)不需要移除觀察者爬凑。
delegate:
就是你有一些事自己不想做或者不能做交給別人做(當(dāng)然只能交給一個(gè)人,不然會(huì)不知道誰該做什么)试伙,設(shè)置協(xié)議嘁信,代理并告訴代理人什么時(shí)候做事。然后讓代理人聲明跟你簽約并且做事疏叨。
優(yōu)勢:
1.非常嚴(yán)格的語法潘靖。所有將聽到的事件必須是在delegate協(xié)議中有清晰的定義。
2.如果delegate中的一個(gè)方法沒有實(shí)現(xiàn)那么就會(huì)出現(xiàn)編譯警告/錯(cuò)誤
3.協(xié)議必須在controller的作用域范圍內(nèi)定義
4.在一個(gè)應(yīng)用中的控制流程是可跟蹤的并且是可識(shí)別的蚤蔓;
5.在一個(gè)控制器中可以定義定義多個(gè)不同的協(xié)議卦溢,每個(gè)協(xié)議有不同的delegates
6.沒有第三方對象要求保持/監(jiān)視通信過程。
7.能夠接收調(diào)用的協(xié)議方法的返回值秀又。這意味著delegate能夠提供反饋信息給controller
缺點(diǎn):
1.需要定義很多代碼:1.協(xié)議定義单寂;2.controller的delegate屬性;3.在delegate本身中實(shí)現(xiàn)delegate方法定義
2.在釋放代理對象時(shí)吐辙,需要小心的將delegate改為nil宣决。一旦設(shè)定失敗,那么調(diào)用釋放對象的方法將會(huì)出現(xiàn)內(nèi)存crash
3.在一個(gè)controller中有多個(gè)delegate對象昏苏,并且delegate是遵守同一個(gè)協(xié)議尊沸,但還是很難告訴多個(gè)對象同一個(gè)事件,不過有可能捷雕。
NSNotification:
通過Notification Center這個(gè)單例對象椒丧,在事件發(fā)生時(shí)通知一些對象,讓對象做出相應(yīng)反應(yīng)救巷。
優(yōu)勢:
1.不需要編寫多少代碼壶熏,實(shí)現(xiàn)比較簡單;
2.對于一個(gè)發(fā)出的通知浦译,多個(gè)對象能夠做出反應(yīng)棒假,簡單實(shí)現(xiàn)1對多的方式溯职,較之于 Delegate 可以實(shí)現(xiàn)更大的跨度的通信機(jī)制;
3.能夠傳遞參數(shù)(object和userInfo)帽哑,object和userInfo可以攜帶發(fā)送通知時(shí)傳遞的信息谜酒。
缺點(diǎn):
1.在編譯期間不會(huì)檢查通知是否能夠被觀察者正確的處理;
2.在釋放通知的觀察者時(shí)妻枕,需要在通知中心移除觀察者僻族;
3.在調(diào)試的時(shí)候,通知傳遞的過程很難控制和跟蹤屡谐;
4.發(fā)送通知和接收通知時(shí)需要提前知道通知名稱述么,如果通知名稱不一致,會(huì)出現(xiàn)不同步的情況愕掏;
5.通知發(fā)出后度秘,不能從觀察者獲得任何的反饋信息。
大致就這樣吧饵撑,這次寫了好久剑梳。。滑潘。主要我也在理解學(xué)習(xí)哈哈垢乙。