Objective-C VS?Swift?的排序方式。
?學習目的:?如何將函數作為參數使用,并且將函數當作數據,以完全類型安全的方式復制同樣的功能
Objective-C:?
如果你想用?Foundation?進行排序,你會遇到一長 串不同的選項,它有接受?selector、block?或者函數指針作為比較斷言的排序方法,或者你也 可以傳入一個?NSSortDescriptor?數組來定義如何排序。所有這些都提供了大量的靈活度和各 式功能,但代價是使排序變得相當復雜?-?沒有一個選項可以讓我能?“只需要基于默認順序進行 一個常規的排序”
-?接受?block?作為比較斷言的方法,在實質上和?Swift?的?sorted(by:)?方法是一樣的
-?接受?NSSortDescriptor?數組的方法,很好地利用了?Objective-C?的動態特性,使它們變成十分靈活和強大?(但是是弱類型)?的?API,但它們不能被直 接移植到?Swift?中。
對于?selector?和動態派發的支持在?Swift?中依然有效,但是?Swift?標準庫更傾向于使用基于函 數的方式。
例子:(一個能展示?Objective-C?強大的運行時的工作方式的例子):
補充一點: 運行時知識:
作為一門動態編程語言,Objective-C 會盡可能的將編譯和鏈接時要做的事情推遲到運行時。只要有可能,Objective-C 總是使用動態 的方式來解決問題。
這意味著 Objective-C 語言不僅需要一個編譯環境,同時也需要一個運行時系統來執行編譯好的代碼。運行時系統(runtime)扮演的角色類似于 Objective-C 語言的操作系統,Objective-C 基于該系統來工作。因此,runtime好比Objective-C的靈魂
和runtime system交互的三種方式
1、通過Objective-C源代碼
2、通過類NSObject的方法? //(我們的下面的例子就是繼承 NSObject, 來繼承NSObject的行為)
3、通過運行時系統的函數
@objcMembers ?(@objcMembers,這樣它的所有成員都將在?Objective-C?中可見)
final class?Person: NSObject {
let?first: String
let?last: String
let?yearOfBirth: Int
init(first: String, last: String, yearOfBirth: Int) {
self.first =?first
self.last = last?self.yearOfBirth = yearOfBirth?// super.init()?在這里被隱式調用
}?}
接下來我們定義一個數組,其中包含了不同名字和出生年份的人:
let?people = [
Person(first:?"Emily", last:?"Young", yearOfBirth: 2002),?Person(first:?"David", last:?"Gray", yearOfBirth: 1991),?Person(first:?"Robert", last:?"Barnes", yearOfBirth: 1985),Person(first:?"Ava", last:?"Barnes", yearOfBirth: 2000),?Person(first:?"Joanne", last:?"Miller", yearOfBirth: 1994),?Person(first:?"Ava", last:?"Barnes", yearOfBirth: 1998),
]
排序規則:?先按照姓排序,再按照名排序,最后是出生年份
方法:使用?NSSortDescriptor?對象來描述如何排序對象,通過 它可以表達出各個排序標準?(使用?localizedStandardCompare?來進行遵循區域設置的排序): (localizedStandardCompare 字符串比較 不區分大小寫)
ascending: true 生序, false 降序
let?lastDescriptor = NSSortDescriptor(key:?#keyPath(Person.last),?ascending:?true,
selector:?#selector(NSString.localizedStandardCompare(_:)))
let?firstDescriptor = NSSortDescriptor(key:?#keyPath(Person.first),?ascending:?true,
selector:?#selector(NSString.localizedStandardCompare(_:)))
let?yearDescriptor = NSSortDescriptor(key:?#keyPath(Person.yearOfBirth),?ascending:?true)
要對數組進行排序,我們使用?NSArray?的?sortedArray(using:)?方法。這個方法可以接受一系列 排序描述符。為了確定兩個元素的順序,它會先使用第一個描述符,并檢查其結果。如果兩個 元素在第一個描述符下相同,那么它將使用第二個描述符,以此類推:
let?descriptors = [lastDescriptor,?firstDescriptor, yearDescriptor]?(people?as?NSArray).sortedArray(using: descriptors)
/*
[Ava Barnes (1998), Ava Barnes (2000), Robert Barnes (1985),
David Gray (1991), Joanne Miller (1994), Emily Young (2002)]?*/
排序描述符用到了?Objective-C?的兩個運行時特性:首先,key?是?Objective-C?的鍵路徑,它其 實是一個包含屬性名字的鏈表。不要把它和?Swift 4?引入的原生的?(強類型的)?鍵路徑搞混。我 們會在稍后再對它進行更多討論。
其次是鍵值編程?(key-value-coding),它可以在運行時通過鍵查找一個對象上的對應值。?selector?參數接受一個?selector (實際上也是一個用來描述方法名字的字符串),在運行時,這 個?selector?將被用來查找比較函數,當對兩個對象進行比較時,這個函數將使用指定鍵對應的 值進行比較。
這是運行時編程的一個很酷的用例,排序描述符的數組可以在運行時構建,這一點在實現比如 用戶點擊某一列時按照該列進行排序這種需求時會特別有用。
我們要怎么用?Swift?的?sort?來復制這個功能呢?
1.要復制這個排序的部分功能是很簡單的,比如, 你想要使用?localizedStandardCompare?來排序一個數組的話:
var?strings = ["Hello",?"hallo",?"Hallo",?"hello"]
strings.sort { $0.localizedStandardCompare($1) == .orderedAscending}?strings?// ["hallo", "Hallo", "hello", "Hello"]
如果只是想用對象的某一個屬性進行排序的話,也非常簡單:
people.sorted { $0.yearOfBirth < $1.yearOfBirth }
/*
[Robert Barnes (1985), David Gray (1991), Joanne Miller (1994),
Ava Barnes (1998), Ava Barnes (2000), Emily Young (2002)]
?*/
不過,當你要把可選值屬性與像是?localizedStandardCompare?這樣的方法結合起來使用的話, 這條路就有點兒走不通了。代碼會迅速變得丑陋不堪。例如,我們想用在可選值中定義的?fileExtension?屬性來對一個包含文件名的數組進行排序:
var?files = ["one",?"file.h",?"file.c",?"test.h"]?files.sort { l, r?in?r.fileExtension.flatMap {
l.fileExtension?.localizedStandardCompare($0)?} == .orderedAscending }
files?// ["one", "file.c", "file.h", "test.h"]
這真的很丑。稍后我們會讓可選值的排序稍微容易一些。不過就目前而言,我們甚至還沒嘗試 對多個屬性進行排序。要同時排序姓和名,我們可以用標準庫的?lexicographicallyPrecedes?方 法來進行實現。這個方法接受兩個序列,并對它們執行一個電話簿方式的比較,也就是說,這 個比較將順次從兩個序列中各取一個元素來進行比較,直到發現不相等的元素。所以,我們可 以用姓和名構建兩個數組,然后使用?lexicographicallyPrecedes?來比較它們。我們還需要一個 函數來執行這個比較,這里我們把使用了?localizedStandardCompare?的比較代碼放到這個函 數中:
people.sorted { p0, p1?in
let?left = [p0.last, p0.first]
let?right = [p1.last, p1.first]
return?left.lexicographicallyPrecedes(right) {
$0.localizedStandardCompare($1) == .orderedAscending?}
}
/*
[Ava Barnes (2000), Ava Barnes (1998), Robert Barnes (1985),
David Gray (1991), Joanne Miller (1994), Emily Young (2002)]?*/
至此,我們用了差不多相同的行數重新實現了原來的那個排序方法。不過還有很大的改進空間: 在每次比較的時候都構建一個數組是非常沒有效率的,比較操作也是被寫死的,通過這種方法 我們將無法實現對?yearOfBirth?的排序。