Swift中的元类型:.Type与.self

元类型

元类型就是类型的类型。元类型表述为metaType

.Type和.self

Swift中元类型用.Type表示。比如Int.Type就是Int的元类型。

类型和值有着不同的形式,比如说5是个Int类型的值。

.Type是类型的元类型;类型的.self是元类型的值,也就是类型本身。

1
2
let metaTypeValue: Int.Type = Int.self
// metaTypeValue就是Int

元类型在Swift中的应用

在Swift中,获得元类型后可以访问静态变量和静态方法。我们经常使用元类型,比如在tableView中的AnyClass

1
2
3
4
func register(AnyClass?, forCellReuseIdentifier: String)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
//AnyClass
typealias AnyClass = AnyObject.Type

AnyClass就是一个任意类型元类型的别名。

当我们访问静态变量的时候其实也是通过元类型来访问的,只是Xcode帮我们省略了.self:

1
2
3
Int.max
//等价于
Int.self.max

type(of:) 和 .self

type(of:).self都可以获得元类型的值,

1
2
let instanceMetaType: String.Type = type(of: "string") // String
let staicMetaType: String.Type = String.self // String

这两种方式的区别在于:

  • .self获取到的是静态的元类型,声明的时候是什么类型就是什么类型
  • .type(of:)获取到的是运行时的元类型
1
2
3
let myNum: Any = 1 
print("myNum.Type = \(type(of: myNum))")
// myNum.Type = Int

Protocol的元类

Protocol自身并不是一个类型,只有当一个对象实现了Protocol后才有了类型对象。所以Protocol.self并不等于Protocol.TypeProtocol.Type要成为一个有效的元类,就需要一个可承载的类型:

1
2
3
4
5
6
protocol MyProtocol {}
// 如果没有类型遵守MyProtocol协议,无法得到一个MyProtocol.Type类型的实例
let metaType:MyProtocol.Type = MyProtocol.self // 这是错误的

struct MyType:MyProtocol {}
let metaType:MyProtocol.Type = MyType.self // 这是正确的

实战示例和应用

假设有两个Cell类,想要一个工厂方法可以根据类型初始化对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protocol ContentCell {}
class IntCell:UIView,ContentCell {
requeired init(value:Int) {
super.init(frame:CGRect.zero)
}
requierd init?(coder aDecoder:NSCoder) {
fatalError("init(code:) has not been implemented")
}
}

class StringCell:UIView,ContentCell {
requeired init(value:Int) {
super.init(frame:CGRect.zero)
}
requierd init?(coder aDecoder:NSCoder) {
fatalError("init(code:) has not been implemented")
}
}

工厂方法的实现:

1
2
3
4
5
6
7
8
9
10
func createCell(type:ContentCell.Type) -> ContentCell? {
if let intCell = type as? IntCell.Type {
return intCel.init(value: 0)
}else if let stringCell = type as? StringCell.Type {
return stringCell.init(value:"string")
}
return nil
}

let intCell = createCell(type:IntCell.self)

使用泛型+类型推断:

1
2
3
4
5
6
7
8
9
func createCell<T: ContentCell>() ->T? {
if let intCell = T.self as? IntCell.Type {
return intCell.init(value:0) as? T
}else if let stringCell = T.self as? StringCell.Type {
return stringCell.init(value: "string") as? T
}
return nil
}
let stringCell:StringCell? = createCell()

Reusable中的tableViewdequeue采用了类似的实现:

1
2
3
4
5
6
7
func dequeueReusableCell<T:UITableViewCell>(for indexPath: IndexPath, cellType:T.Type = T.self) ->T
where T: Reusable {
guard let cell = self.dequeueReusableCell(withIdentifier:cellType.reuseIdentifier,for: indexPath) as? T else {
fatalError("Failed to dequeue a cell")
}
return cell
}

dequeue的时候根据目标类型推断,不需要再额外声明元类型:

1
2
3
class MyCell:UITableViewCell,Reusable
tableView.register(cellType:MyCell.self)
let cell:MyCell = tableView.dequeueReusableCell(for:indexPath)