Swift原类型
Created by 大刘 liuxing8807@126.com
参考:
SwiftRocks
Apple
Apple
什么是元类型
元类型,即类型的类型type of type
,描述类型的元数据类型. 看一个普通的示例:
struct SwiftRocks {
static let author = "Bruno Rocha"
func postArticle(name: String) {}
}
let blog: SwiftRocks = SwiftRocks()
blog
, 即SwiftRocks()
是一个实例,SwiftRocks
本身是对这个实例的描述:type of an instance
blog
可以调用实例方法postArticle
, 但是无法访问类属性author
,如何访问类属性author
?
可以通过:SwiftRocks.author
也可以:type(of: blog).author
type(of:)
方法的原型为:public func type<T, Metatype>(of value: T) -> Metatype
type(of: blog)
可以调用SwiftRocks
所有的类方法,说明type(of: blog)
方法通过实例blog
得到了某个东西可以访问所有的类属性,这个东西是什么呢?
不妨打印下type(of: blog)
是什么:
let something = type(of: blog)
print(something) // SwiftRocks
这里type(of: blog)
打印的结果是SwiftRocks
, 其实,这里应该是SwiftRocks.Type
, 只是打印出的东西显示为SwiftRocks,来看:
print(SwiftRocks.self) // SwiftRocks.self, SwiftRocks.self返回的是SwiftRocks.Type, 但print打它打印成了SwiftRocks
if type(of: blog) == SwiftRocks.self {
print("Equal") // Equal, 可以看到,SwiftRocks.self即:SwiftRocks.Type, 所以type(of: blog)即SwiftRocks.Type
}
type(of: blog)
和SwiftRocks.self
是相同的,它们返回的都是SwiftRocks
的MetaType
, 即元类型,即SwiftRocks.Type
是SwiftRocks
的原类型metatype.
可以理解为,对于 let blog: SwiftRocks = SwiftRocks()
, blog
是实例,SwiftRocks
是对这个实例的描述,即SwiftRocks
是实例blog
的Type
, 而SwiftRocks
本身的type
是SwiftRocks.Type
:
Type | instance |
---|---|
SwiftRocks | blog, 即SwiftRocks()对象 |
SwiftRocks.Type | SwiftRocks.self 或 typeof(blog) |
You can define the metatype of any type, including classes, structs, enums and protocols as being the name of that type followed by
.Type
. In short, whileSwiftRocks
refers to the type of an instance (which only lets you use instance properties), the metatypeSwiftRocks.Type
refers to the type of class itself, which lets you use theSwiftRocks's
class properties. "type of a type" makes a lot more sense now, right?
对元类型的引用可以访问类的所有属性和方法,包括init()方法:
let something = type(of: blog) // something的类型是元类型SwiftRocks.Type
let object = something.init() // 可以通过元类型的引用调用类方法,调用init()方法得到实例
object.postArticle(name: "haha")
可以通过元类型访问类属性和类方法是很有用的,比如我们可以把原类型作为参数传递,以新建UITableViewCell为例:
class MyCell: UITableViewCell {
...
}
func createCustomCell<T: UITableViewCell>(ofType: T.Type) -> T {
let cell = T.init()
// ...
return cell
}
createCustomCell(ofType: MyCell.self)
Metatypes也可以用在判断是否相等的比较中:
class Animal {
public func think() {
print("Animal think")
}
}
class Cat: Animal {
public override func think() {
print("Cat think")
}
}
class Dog: Animal {
public override func think() {
print("Dog think")
}
}
class Person: Animal {
public override func think() {
print("Person deep think")
}
}
func create<T: Animal>(animalType: T.Type) -> T {
switch animalType {
case is Cat.Type:
print("It's cat")
return Cat() as! T
case is Dog.Type:
print("It's dog")
return Dog() as! T
case is Person.Type:
print("It's person")
return Person() as! T
default:
break
}
return Animal() as! T
}
create(animalType: Person.self).think()
let p: Person = Person()
create(animalType: type(of: p)).think()
type(of:) Dynamic Metatypes vs .self Static Metatypes
在上面的示例中:
let blog: SwiftRocks = SwiftRocks()
let something = type(of: blog)
type(of: blog)返回的就是SwiftRocks的元类型,即SwiftRocks.Type, 如果没有对象,只有类SwiftRocks本身,可以通过SwiftRocks.self返回它的原类型。typeof(of: blog)和SwiftRocks.self是一个东西。
再举个示例:
String
是 type, "Hello World" 是 value, String.Type
是 type, String.self
是 value.
let personMetatype: Person.Type = Person.self
create(animalType: personMetatype).think()
func create<T: Animal>(animalType: T.Type) -> T {
...
}
.self
被Apple称为static metatype, 这是一个在编译期就可以确定的类型。我们平常调用比如SwiftRocks.author
其实相当于SwiftRocks.self.author
。
Static metatypes 在Swift中无处不在, 当每次访问类属性或类方法时,你都在隐式的使用它们。
在tableView的注册cell方法中就有一个AnyClass:
open func register(_ cellClass: AnyClass?, forCellReuseIdentifier identifier: String)
我们查看AnyClass的源码发现:
public typealias AnyClass = AnyObject.Type
AnyClass只是任意对象的元类型的别名而已。
.self
返回的是编译期就可以确定的static metatype, type(of: anObject)
返回的是运行时确定的dynamic metatype
Protocol Metatypes
上面所说的同样适用于协议protocols, 但是有一点区别,比如下面代码就无法工作:
protocol MyProtocol {
}
let metatype: MyProtocol.Type = MyProtocol.self // compile error: Cannot convert value of type 'MyProtocol.Protocol' to specified type 'MyProtocol.Type'
The reason for that is that in the context of protocols, MyProtocol.Type
doesn't refer to the protocol's own metatype, but the metatype of whatever type is inheriting that protocol. Apple calls this an existential metatype.
即:在protocol的上下文中,MyProtocol.Type并不是指向protocol自己的metatype, 而是指向实现protocol协议的其他Type:
protocol MyProtocol {
}
struct MyType: MyProtocol {
}
let metatype: MyProtocol.Type = MyType.self // OK