文章轉(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)。for
,while
語(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ò)它是在NSArray
和NSSet
所組成的集合中工作的。如果我們做一些例如:比較幾個(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) is the creator & maintainer of AFNetworking and other popular open-source projects, including Postgres.app, ASCIIwwdc and Nomad.
翻譯者
Candyan 專業(yè) iOS,業(yè)余 Android,偶爾搗鼓下Server的工程師。