在 Swift 中能夠表示 “任意” 這個概念的除了 Any 和 AnyObject 以外,還有一個 AnyClass。AnyClass 在 Swift 中被一個 typealias 所定義:
typealias AnyClass = AnyObject.Type
通過 AnyObject.Type 這種方式所得到是一個元類型 (Meta)。在聲明時我們總是在類型的名稱后面加上 .Type,比如 A.Type 代表的是 A 這個類型的類型。也就是說,我們可以聲明一個元類型來存儲 A 這個類型本身,而在從 A 中取出其類型時,我們需要使用到 .self:
class A {
}
let typeA: A.Type = A.self
其實在 Swift 中,.self 可以用在類型后面取得類型本身,也可以用在某個實例后面取得這個實例本身。前一種方法可以用來獲得一個表示該類型的值,這在某些時候會很有用;而后者因為拿到的實例本身,所以暫時似乎沒有太多需要這么使用的案例。
了解了這個基礎之后,我們就明白 AnyObject.Type,或者說 AnyClass 所表達的東西其實并沒有什么奇怪,就是任意類型本身。所以,上面對于 A 的類型的取值,我們也可以強制讓它是一個 AnyClass:
class A {
}
let typeA: AnyClass = A.self
這樣,要是 A 中有一個類方法時,我們就可以通過 typeA 來對其進行調用了:
class A {
class func method() {
print("Hello")
}
}
let typeA: A.Type = A.self
typeA.method()
// 或者
let anyClass: AnyClass = A.self
(anyClass as! A.Type).method()
也許你會問,這樣做有什么意義呢,我們難道不是可以直接使用 A.method() 來調用么?沒錯,對于單個獨立的類型來說我們完全沒有必要關心它的元類型,但是元類型或者元編程的概念可以變得非常靈活和強大,這在我們編寫某些框架性的代碼時會非常方便。比如我們想要傳遞一些類型的時候,就不需要不斷地去改動代碼了。在下面的這個例子中雖然我們是用代碼聲明的方式獲取了MusicViewController 和 AlbumViewController 的元類型,但是其實這一步驟完全可以通過讀入配置文件之類的方式來完成的。而在將這些元類型存入數組并且傳遞給別的方法來進行配置這一點上,元類型編程就很難被替代了:
class MusicViewController: UIViewController {
}
class AlbumViewController: UIViewController {
}
let usingVCTypes: [AnyClass] = [MusicViewController.self,
AlbumViewController.self]
func setupViewControllers(_ vcTypes: [AnyClass]) {
for vcType in vcTypes {
if vcType is UIViewController.Type {
let vc = (vcType as! UIViewController.Type).init()
print(vc)
}
}
}
setupViewControllers(usingVCTypes)
這么一來,我們完全可以搭好框架,然后用 DSL 的方式進行配置,就可以在不觸及 Swift 編碼的情況下,很簡單地完成一系列復雜操作了。
另外,在 Cocoa API 中我們也常遇到需要一個 AnyClass 的輸入,這時候我們也應該使用 .self 的方式來獲取所需要的元類型,例如在注冊 tableView 的 cell 的類型的時候:
self.tableView.registerClass(
UITableViewCell.self, forCellReuseIdentifier: "myCell")
.Type 表示的是某個類型的元類型,而在 Swift 中,除了 class,struct 和 enum 這三個類型外,我們還可以定義 protocol。對于 protocol 來說,有時候我們也會想取得協(xié)議的元類型。這時我們可以在某個 protocol 的名字后面使用 .Protocol 來獲取,使用的方法和 .Type 是類似的。