首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》4.7 类型引用

关灯直达底部

实例引用自己的类型是很有用的,比如,向该类型发送消息。在之前的示例中,Dog实例方法通过显式向Dog类型发送一条消息来获取到一个Dog类属性,这是通过使用Dog这个单词做到的:


class Dog {    class var whatDogsSay : String {        return /"Woof/"    }    func bark {        print(Dog.whatDogsSay)    }}  

表达式Dog.whatDogsSay看起来很笨拙并且不灵活。为什么要在Dog类中使用硬编码的类名呢?它有一个类;它应该知道是什么。

在Objective-C中,我们习惯使用类实例方法来处理这种情况。在Swift中,实例可能不是类(可能是结构体实例或枚举实例);Swift实例拥有类型。Swift针对这一目的提供了一个名为dynamicType的实例方法。实例可以通过该方法访问其类型。因此,如果不想显式使用Dog来通过Dog实例调用Dog类方法,那么下面就是另一种解决方案:


class Dog {    class var whatDogsSay : String {        return /"Woof/"    }    func bark {        print(self.dynamicType.whatDogsSay)    }}  

使用dynamicType而非硬编码类名的重要之处在于它遵循了多态:


class Dog {    class var whatDogsSay : String {        return /"Woof/"    }    func bark {        print(self.dynamicType.whatDogsSay)    }}class NoisyDog : Dog {    override class var whatDogsSay : String {        return /"Woof woof woof/"    }}  

下面来看一下结果:


let nd = NoisyDognd.bark // Woof woof woof  

如果调用NoisyDog的bark方法,那么会打印出/"Woof woof woof/"。原因在于dynamicType的含义是“该实例现在的实际类型,这正是该类型成为动态的原因所在”。我们向NoisyDog实例发送了“bark”消息。bark实现引用了self.dynamicType;self表示当前实例,即NoisyDog,因此self.dynamicType是NoisyDog类,它是获取到的NoisyDog的whatDogsSay。

还可以通过dynamicType获取到对象类型的名字(字符串),这通常用于调试的目的。在调用print(myObject.dynamicType)时,控制台上会打印出类型名。

在某些情况下,你会将对象类型作为值进行传递。这么做是合法的;对象类型本身也是对象。下面是你需要知道的几点:

·声明接收某个对象类型(如作为变量或参数的类型),请使用点符号加上类型名与关键字Type。

·将对象类型作为值(比如,为变量指定类型或将类型传递给函数),请使用类型引用(类型名,或实例的dynamicType),后面可以通过点符号跟着关键字self。

比如,下面这个函数的参数会接收一个Dog类型:


func typeExpecter(whattype:Dog.Type) {}  

如下代码调用了该函数:


typeExpecter(Dog) // or: typeExpecter(Dog.self)  

还可以像下面这样调用:


let d = Dog // or: let d = NoisyDogtypeExpecter(d.dynamicType) // or: typeExpecter(d.dynamicType.self)  

为何要这么做呢?典型场景就是函数是个实例工厂:给定一个类型,它会创建出该类型的实例,可能还会做一些处理,然后将其返回。你可以使用指向类型的变量引用来创建该类型的实例,方式是向其发送一条init(...)消息。

比如,如下Dog类带有一个init(name:)初始化器,同时它还有一个子类NoisyDog:


class Dog {    var name : String    init(name:String) {        self.name = name    }}class NoisyDog : Dog {}  

下面是创建Dog或NoisyDog的工厂方法(通过参数来指定),为实例指定一个名字,然后将实例返回:


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

如你所见,由于whattype引用了一个类型,所以可以调用其初始化器创建该类型的实例,不过有一个问题,上述代码无法编译通过。原因在于编译器不能确定init(name:)初始化器是否会被Dog的每个子类型所实现。为了消除编译器的这个疑虑,我们需要通过required关键字声明该初始化器:


class Dog {    var name : String    required init(name:String) {        self.name = name    }}class NoisyDog : Dog {}  

现在来解释一下需要将初始化器声明为required的原因:required会消除编译器的疑虑;Dog的每个子类都要继承或重新实现init(name:);因此,可以向指向Dog或其子类的类型引用发送init(name:)消息。现在代码可以编译通过,我们也可以调用函数:


let d = dogMakerAndNamer(Dog) // d is a Dog named Fidolet d2 = dogMakerAndNamer(NoisyDog) // d2 is a NoisyDog named Fido  

在类方法中,self表示类,这正是多态发挥作用之处。这意味着在类方法中,你可以向self发送消息,以多态的形式调用初始化器。下面是一个示例,我们将实例工厂方法移到Dog中,作为一个类方法,并将这个类方法命名为makeAndName。我们需要这个类方法创建并返回一个具名Dog,返回的Dog类型取决于向哪个类发送makeAndName消息。如果调用Dog.makeAndName(),那么返回的是Dog;如果调用NoisyDog.makeAndName(),那么返回的是NoisyDog。类型是多态的self类,因此makeAndName类方法可以初始化self:


class Dog {    var name : String    required init(name:String) {        self.name = name    }    class func makeAndName -> Dog {        let d = self.init(name:/"Fido/")        return d    }}class NoisyDog : Dog {}  

其工作方式与我们期望的一致:


let d = Dog.makeAndName // d is a Dog named Fidolet d2 = NoisyDog.makeAndName // d2 is a NoisyDog named Fido  

不过有一个问题。虽然d2实际上是个NoisyDog,但其类型却是Dog。这是因为makeAndName类方法在声明时返回的类型是Dog,这并不是我们想要的结果。我们希望该方法所返回的实例类型与接收makeAndName消息的类型保持一致。换句话说,我们需要一种多态化的类型声明!这个类型是Self(注意,首字母是大写的)。它用作方法声明的返回类型,表示“运行时该类型的实例”。如下:


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 {}  

现在,当调用NoisyDog.makeAndName()时,我们将会得到类型为NoisyDog的NoisyDog。

Self也可以用于实例方法声明。因此,我们可以编写出该工厂方法的实例方法版本。下面是Dog类与NoisyDog类的声明,同时在Dog类中声明了一个返回Self的havePuppy方法:


class Dog {    var name : String    required init(name:String) {        self.name = name    }   func havePuppy(name name:String) -> Self {       return self.dynamicType.init(name:name)    }}class NoisyDog : Dog {}  

下面是测试代码:


let d = Dog(name:/"Fido/")let d2 = d.havePuppy(name:/"Fido Junior/")let nd = NoisyDog(name:/"Rover/")let nd2 = nd.havePuppy(name:/"Rover Junior/")  

如你所想,d2是个Dog,不过nd2却是个类型为NoisyDog的NoisyDog。

讲了这么多可能有些乱,下面来总结一下:

.dynamicType

用在代码中,发送给实例:表示该实例的多态化(内部)类型,无论实例引用的类型是什么均如此。静态/类成员可以通过实例的dynamicType进行访问。

.Type

用在声明中,发送给类型:多态类型而不是类型的实例。比如,在函数声明中,Dog表示需要一个Dog实例(或其子类的实例),而Dog.Type则表示需要Dog类型本身(或是其子类的类型)。

.self

用在代码中,发送给类型。比如,在需要Dog.Type时,你可以传递Dog.self(向实例发送.self是合法的,但却毫无意义)。

self

用在实例代码中表示多态语义下的当前实例。

用在静态/类代码中表示多态语义下的类型;self.init(...)会实例化该类型。

Self

在方法声明中,如果指定了返回类型,那么它表示多态化的类或实例的类。