Swift之类型引用

类型引用


在开发中,我们经常用到实例的类型,希望实例与其真正的对象类型关联,既面向对象中的多态性。接下来将通过一个例子来进行讨论,比如,定义一个Dog类,用来描述狗,如下:

1
2
3
4
5
6
7
8
class Dog {
class var whatDogsSay : String {
return "Woof"
}
func bark() {
print(Dog.whatDogsSay)
}
}

Dog类的bark为狗叫所定义的方法,实现中直接硬编码Dog.whatDogsSay,这种方法会很不灵活,尤其是其如果作为父类被子类时。

Swift中,可以使用type(of:)方法来访问对象实际的类型,既会返回实例对象真正的类型,所以,可以对之前的Dog进行改进,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dog {
class var whatDogsSay : String {
return "Woof"
}
func bark() {
print(type(of:self).whatDogsSay)
}
}

class NoisyDog : Dog {
override class var whatDogsSay : String {
return "Woof woof woof"
}
}

如上定义,结果如下:

1
2
let nd = NoisyDog()
nd.bark() // Woof woof woof

如果向NoisyDog实例发送bark消息,结果为”Woof woof woof”。这就是type(of:)方法的作用,他会指向实例真正的类型。self表示实例,类型为NoisyDog,所以type(of:self)返回NoisyDog类,所以会调用NoisyDog类的whatDogsSay类属性。

有时候,需要传递一个对象类型作为参数:

  • 声明对象的类型时,可以使用类型名加.再加关键字Type
  • 如果使用一个对象作为值时,比如,将类型赋值给一个变量或者作为参数值传递给函数时,可以使用类型名加self关键字,或者type(of:)方法。

例如,有一个函数dogTypeExpecter,接收Dog类型作为参数:

1
2
func dogTypeExpecter(_ whattype:Dog.Type) {
}

调用方式如下:

1
dogTypeExpecter(Dog.self)

或:

1
2
3
4
5
6
let d = Dog()
dogTypeExpecter(type(of:d))

dogTypeExpecter(NoisyDog.self)
let nd = NoisyDog()
dogTypeExpecter(type(of:nd))

这种情况经常出现在工厂方法中,既给定某个类型,创建该类型的实例,Swift中使用init来定义初始化器,如:

1
2
3
4
5
6
7
8
class Dog {
var name : String
init(name:String) {
self.name = name
}
}
class NoisyDog : Dog {
}

如下为一个工厂方法来创建DogNoisyDog,通过参数来指明类型:

1
2
3
4
func dogMakerAndNamer(_ whattype:Dog.Type) -> Dog {
let d = whattype.init(name:"Fido") // compile error
return d
}

如上定义后,编译器会报错,原因是编译器无法确保Dog的每一个子类都会实现init(name:)初始化方法,所以,我们可以通过添加required关键字来强制Dog子类实现或继承该初始化方法。

1
2
3
4
5
6
7
8
class Dog {
var name : String
required init(name:String) {
self.name = name
}
}
class NoisyDog : Dog {
}

调用结果:

1
2
let d = dogMakerAndNamer(Dog.self) // d is a Dog named Fido
let d2 = dogMakerAndNamer(NoisyDog.self) // d2 is a NoisyDog named Fido

在类方法中,self代表类,在实例方法中,self表示类实例,在工厂方法中,我们在返回类型中使用了Self关键字,其具有动态性,代表返回的真正类型,其也可以用在实例方法的声明中。

1
2
3
4
5
6
7
8
9
10
11
12
class Dog {
var name : String
required init(name:String) {
self.name = name
}
class func makeAndName() -> Self {
let d = self.init(name:"Fido")
return d
}
}
class NoisyDog : Dog {
}

总结


type(of:):返回对象实例所指向的真正对象类型。
.Type:在类型声明时使用,比如,在函数参数声明时,期望参数为Dog类型或其子类类型,可以写成Dog.Type
.self:代表一个类型,如,函数参数需要Dog.Type类型时,可以传递Dog.self
Self:在方法声明时,在返回类型中使用Self关键字,具有动态性。

附录


Swift中,静态变量、静态方法可以使用staticclass两种关键字,但是需要注意的是,static声明的无法重载,但是class关键字可以,不过class只能使用在类中,不能用在结构体、枚举等中。