KVC 集合運(yùn)算符

文章轉(zhuǎn)自http://nshipster.cn/
另外iOS 中集合遍歷方法的比較和技巧 · sunnyxx的技術(shù)博客這篇文章對(duì)于KVC 集合運(yùn)算符的性能也做了描述

Ruby愛好者總愛嘲笑Objective-C臃腫的語(yǔ)法。

盡管新的Object Literals特性讓我們的語(yǔ)法瘦了幾斤,但那些紅頭發(fā)的惡霸們還總是用他們的單行map和花哨的Symbol#to_proc來(lái)嘲諷我們。

實(shí)際上,一門語(yǔ)言是否優(yōu)雅歸結(jié)起來(lái)就是其怎么樣能更好的避免循環(huán)。forwhile語(yǔ)句是一種拖累;即使是快速枚舉也一樣。無(wú)論你怎么樣使他們看起來(lái)更加的友好,循環(huán)依然是一個(gè)在自然語(yǔ)言中用非常簡(jiǎn)單方式描述所做事情的代碼塊

"給我這個(gè)列表里面所有員工的平均薪酬",等等。。。

Objective-C

double totalSalary = 0.0;
for (Employee *employee in employees) {
  totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];

╮(╯_╰)╭

幸運(yùn)的是,鍵-值編碼給我們了一種更加簡(jiǎn)潔的,幾乎像Ruby一樣的方式來(lái)做這件事:

Objective-C

[employees valueForKeyPath:@"@avg.salary"];

KVC集合運(yùn)算符允許在valueForKeyPath:方法中使用key path符號(hào)在一個(gè)集合中執(zhí)行方法。無(wú)論什么時(shí)候你在key path中看見了@,它都代表了一個(gè)特定的集合方法,其結(jié)果可以被返回或者鏈接,就像其他的key path一樣。

集合運(yùn)算符會(huì)根據(jù)其返回值的不同分為以下三種類型:

  • 簡(jiǎn)單的集合運(yùn)算符 返回的是strings, number, 或者 dates
  • 對(duì)象運(yùn)算符 返回的是一個(gè)數(shù)組
  • 數(shù)組和集合運(yùn)算符 返回的是一個(gè)數(shù)組或者集合

要理解其工作原理,最好方式就是去action里面看看。想象一個(gè)Product類和一個(gè)由以下數(shù)據(jù)所組成的products數(shù)組:

Objective-C

@interface Product : NSObject
@property NSString *name;
@property double price;
@property NSDate *launchedOn;
@end

鍵-值 編碼會(huì)在必要的時(shí)候把基本數(shù)據(jù)類型的數(shù)據(jù)自動(dòng)裝箱和拆箱到NSNumber或者NSValue中來(lái)確保一切工作正常。

Name Price Launch Date
iPhone 5 $199 September 21, 2012
iPad Mini $329 November 2, 2012
MacBook Pro $1699 June 11, 2012
iMac $1299 November 2, 2012

簡(jiǎn)單集合操作符

  • @count: 返回一個(gè)值為集合中對(duì)象總數(shù)的NSNumber對(duì)象。
  • @sum: 首先把集合中的每個(gè)對(duì)象都轉(zhuǎn)換為double類型,然后計(jì)算其總,最后返回一個(gè)值為這個(gè)總和的NSNumber對(duì)象。
  • @avg: 把集合中的每個(gè)對(duì)象都轉(zhuǎn)換為double類型,返回一個(gè)值為平均值的NSNumber對(duì)象。
  • @max: 使用compare:方法來(lái)確定最大值。所以為了讓其正常工作,集合中所有的對(duì)象都必須支持和另一個(gè)對(duì)象的比較。
  • @min: 和@max一樣,但是返回的是集合中的最小值。

例如

Objective-C

[products valueForKeyPath:@"@count"]; // 4
[products valueForKeyPath:@"@sum.price"]; // 3526.00
[products valueForKeyPath:@"@avg.price"]; // 881.50
[products valueForKeyPath:@"@max.price"]; // 1699.00
[products valueForKeyPath:@"@min.launchedOn"]; // June 11, 2012

Pro提示:你可以簡(jiǎn)單的通過(guò)把self作為操作符后面的key path來(lái)獲取一個(gè)由NSNumber組成的數(shù)組或者集合的總值,例如[@[@(1), @(2), @(3)] valueForKeyPath:@"@max.self"] (感謝 @davandermobile, 來(lái)自 Objective Sea)

對(duì)象操作符

想象下,我們有一個(gè)inventory數(shù)組,代表了當(dāng)?shù)靥O果商店的當(dāng)前庫(kù)存(iPad Mini不足,并且沒有新的iMac,因?yàn)檫€沒有發(fā)貨):

Objective-C

NSArray *inventory = @[iPhone5, iPhone5, iPhone5, iPadMini, macBookPro, macBookPro];

  • @unionOfObjects / @distinctUnionOfObjects: 返回一個(gè)由操作符右邊的key path所指定的對(duì)象屬性組成的數(shù)組。其中@distinctUnionOfObjects 會(huì)對(duì)數(shù)組去重, 而且 @unionOfObjects 不會(huì).

例如

Objective-C

[inventory valueForKeyPath:@"@unionOfObjects.name"]; // "iPhone 5", "iPhone 5", "iPhone 5", "iPad Mini", "MacBook Pro", "MacBook Pro"
[inventory valueForKeyPath:@"@distinctUnionOfObjects.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"

數(shù)組和集合操作符

數(shù)組和集合操作符跟對(duì)象操作符很相似,只不過(guò)它是在NSArrayNSSet所組成的集合中工作的。如果我們做一些例如:比較幾個(gè)商店中的庫(kù)存(和我們上一節(jié)類似的appleStore庫(kù)存和買iPhone 5和iPad Mini的versizonStore庫(kù)存)這樣的工作,這個(gè)就會(huì)很有用。

  • @distinctUnionOfArrays / @unionOfArrays: 返回了一個(gè)數(shù)組,其中包含這個(gè)集合中每個(gè)數(shù)組對(duì)于這個(gè)操作符右面指定的key path進(jìn)行操作之后的值。正如你期望的,distinct版本會(huì)移除重復(fù)的值。

  • @distinctUnionOfSets: 和@distinctUnionOfArrays差不多, 但是它期望的是一個(gè)包含著NSSet對(duì)象的NSSet,并且會(huì)返回一個(gè)NSSet對(duì)象。因?yàn)榧喜荒馨貜?fù)的值,所以它只有distinct操作。

例如

Objective-C

[@[appleStoreInventory, verizonStoreInventory] valueForKeyPath:@"@distinctUnionOfArrays.name"]; // "iPhone 5", "iPad Mini", "MacBook Pro"


這可能是一個(gè)可怕的想法

令人好奇的是,蘋果的KVC集合操作符文檔冒出了下面這個(gè)提示:

注意: 目前還不能自定義集合操作符。

這個(gè)提示是有意義的,因?yàn)榇蠖鄶?shù)人在第一次看到集合運(yùn)算符時(shí)都在想這個(gè)。

然而,事實(shí)證明,在我們的小伙伴objc/runtime的幫助下,這個(gè)實(shí)際上 有可以能的實(shí)現(xiàn)的。

Guy English有一篇很神奇的文章,在文章中,他swizzles valueForKeyPath:來(lái)解析自定義的DSL,其擴(kuò)展了一些有趣的效果:

Objective-C

NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"];

這段代碼可以得到只有休了不足10天假期的人的名字(無(wú)疑是要提醒他們?nèi)バ輦€(gè)假吧!)

或者,來(lái)看個(gè)可笑的極端情況:

Objective-C

NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];

Ruby小伙伴們羨慕吧。只用一行就在藝人記錄中過(guò)濾出來(lái)了名字叫"Bon Iver"的藝人,并且用匹配到的專輯的專輯封面的圖像數(shù)據(jù)初始化了一個(gè)NSImage對(duì)象。

這是一個(gè)好的想法嗎?可能不是。(NSPredicate更加合適,并且其使得邏輯更加簡(jiǎn)單,易懂)

這個(gè)很酷嗎?當(dāng)然啦!這個(gè)聰明的例子展示了Objective-C DSL和元編程未來(lái)可能的發(fā)展方向。


KVC集合運(yùn)算符是一個(gè)想節(jié)省幾行代碼并在這一過(guò)程中看起來(lái)很酷的人必須要了解的。當(dāng)像Ruby這樣的腳本語(yǔ)言自夸它的單行能力是多么的靈活時(shí),我們也許應(yīng)該花一點(diǎn)兒時(shí)間來(lái)慶祝Objective-C中的約束和集合操作符。畢竟,Ruby非常非常慢,我說(shuō)的對(duì)嗎?

作者
Mattt Thompson

Mattt Thompson

Mattt Thompson (@mattt) is the creator & maintainer of AFNetworking and other popular open-source projects, including Postgres.app, ASCIIwwdc and Nomad.

翻譯者

Candyan

Candyan 專業(yè) iOS,業(yè)余 Android,偶爾搗鼓下Server的工程師。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • KVC集合運(yùn)算符允許在valueForKeyPath:方法中使用key path符號(hào)在一個(gè)集合中執(zhí)行方法。無(wú)論什么...
    ping_oO閱讀 523評(píng)論 0 0
  • 蘋果官方文檔翻譯 《Objective-C語(yǔ)言編程》(Programming with Objective-C) ...
    fever105閱讀 25,903評(píng)論 19 130
  • 展現(xiàn)的全是低俗的交友觀 女生不再潔身自好 男生不再紳士有格 以露骨的話題度過(guò)光陰 朋友是擁有各自的圈子 經(jīng)過(guò)風(fēng)浪之...
    大情子閱讀 240評(píng)論 0 0
  • 最近做畢設(shè)讀論文讀得心累,很多論文公式一堆提升逼格,很多想法也不具普適性,好像大家都在各玩各的。前兩日看到知乎一個(gè)...
    RENO216閱讀 1,872評(píng)論 0 1
  • 第一句: 追逐的不是眼前的路,而是因?yàn)橛心敲匆粭l路而去追逐 四年前說(shuō)過(guò)這句話,那個(gè)時(shí)候自己還在為高考而作準(zhǔn)備,那個(gè)...
    繼續(xù)海闊天空閱讀 261評(píng)論 0 1