iOS 进阶文集iOS学习开发iOS进阶指南

Swift之继承、构造过程

2016-08-06  本文已影响920人  繁华落尽丶lee

继承

Swift中 ,类可以调用和访问超类的方法,属性和下标脚本,并且可以重写这些方法,属性和下标脚本来优化或修改它们的行为。Swift会检查你的重写定义在超类中是否有匹配的定义,保证重写的正确性。可以为类继承来的任何属性添加属性观察器,属性改变会被通知。

基类

不继承于其他类的类,称之为基类。

//定义一个基类
class Vehicle {
    //存储属性
    var currentSpeed = 0.0
    //只读属性
    var description : String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    //方法
    func makeNoise() {
        //什么也不做
    }
}
let someVehicle = Vehicle()
print(someVehicle.description)

注意:Swift中的类并不是从一个通用的基类继承,如果不指定超类,这个类自动成为基类。

子类

一个已有类的基础上创建一个新类,称之为子类生成。子类继承超类的特性,并且可以优化或者改变它,也可以添加新特性。

//继承Vehicle
class Bicycle: Vehicle {
    var hasBasket = false
}
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")

重写

子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标脚本(subscript)提供自己定制的实现。
注意:重写某个特性,需要添加关键字override。告诉编译器是重写,并不是相同的定义。

访问超类方法,属性及下标脚本

防止重写

只要在声明关键字前加上final特性即可。

构造过程

构造过程是使用类、结构体或枚举类型一个实例的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储属性的初始值和执行其他必须的设置或初始化工作。
注意:我们可以使用构造器来实现构造过程。与Objective-C不同,Swift构造器无需返回值,主要任务是保证新实例在第一次使用前完成正确的初始化。

存储属性的初始赋值

类和结构体在创建实例时,必须为所有存储属性设置合适的初始值。存储属性可以在定义时赋值,也可以在构造器中赋值。
注意:当给存储属性设置默认值或者在构造器中为其赋值时,他们的值是被直接设置的,不会触发任何属性观察器。

自定义构造过程

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }
    init(white: Double) {
        red = white
        green = white
        blue = white
    }
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
//注意:
let veryGreen = Color(0.0, 1.0, 0.0) //报错

注意:添加(_)可以隐藏外部名,也可以自定义外部名。

值类型的构造器代理

//构造器可以通过调用其它构造器来完成实例的部分构造过程,称之为构造代理。
struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init(){}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

//1、第一个构造器是空构造器, 调用返回一个Rect实例
let basicRect = Rect() //原点(0,0),尺寸(0,0)
//2、 第二个构造器
let originRect = Rect(center: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) //原点(2,2),尺寸(5, 5)
//3、 第三个构造器
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) //原点(2.5, 2.5) 尺寸(3.0, 3.0)
//: 先通过center和size的值计算出origin坐标。然后在调用给init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中。

类的继承和构造过程

类中所有存储型属性,包括所有继承自父类的属性,都必须在构造器中设置初始值。Swift中两种类构造器:指定构造器和遍历构造器。

指定构造器和便利构造器

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。每一个类都必须拥有至少一个指定构造器。许多类通过继承父类的指定构造器满足这个条件。
便利构造器是类中比较次要的、辅助型的构造器。可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。也可以通过便利构造器来创建一个特殊用途或特定输入的实例。

语法

类的指定构造器的写法同值类型简单构造器:

init(parameters) {
  statements
}

便利构造器, 在init之前添加convenience关键字。

convenience init() {
    statements 
}
类构造器代理原则

为了简化指定构造器和便利构造器之间的调用关系,Swift采用三条限制构造器之间的代理调用:

两段式构造过程

Swift中类的构造过程包括两段:

  1. 每个存储型属性通过引入它们类的构造器来设置初始值。
  2. 当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。

两段式构造器作用:
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止存储值在初始化之前被访问;也可以防止属性被另一个构造器意外地赋予不用的值。

两段式构造过程中基于安全检查的构造流程展示:
阶段1

阶段2

构造器的继承和重写

跟Objective-C中的子类不同,Swift中的子类不会默认继承父类的构造器。Swift的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。
重写父类指定构造器时,需要添加override修饰符;子类便利构造器不能直接调用父类便利构造器,所以子类不必提供一个父类的构造器的重写。

例子:

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheels"
    }
}
//为存储型属性提供默认值,未提供自定义构造器。自动生成一个默认构造器,默认构造器在类中是指定构造器,它可以用于创建属性叫numberOfWheels值为0的Vehicle属性。
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")

class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}
//自定义指定构造器init()。这个构造器和父类的指定构造器相匹配。所以需要加上override修饰符。
// supe.init() 可以保证在修改属性之前,能被父类初始化。
// 另外,只能修改继承来的变量属性,不能修改常量属性。

自动构造器的继承

子类默认不会继承父类的构造器。特定条件下,父类构造器是可以被自动继承的。

指定构造器和便利构造器实例

例子:

class Food {
    var name: String
    init(name:String) {
        self.name = name
    }
    convenience init() {
        self.init(name:"[Unnamed]")
    }
}
Food构造器链.png
let nameMeat = Food(name: "Bacon")
nameMeat.name // Bacon

Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能够确保所有新Food实例中存储型属性都被初始化。
Food类同样提供一个没有参数的便利构造器init()。这个构造器提供了一个默认的占位名字,通过代理调用同一类中定义的指定构造器,并给参数name传值来实现。

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1);
    }
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
RecipeIngredient构造器.png
class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ?" : " ?"
        return output
    }
}
var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6)
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
    print(item.description)
}
//1 x Orange juice ?
//1 x Bacon ?
//6 x Eggs ?

由于自己引入的所有属性都提供了默认值,并没有定义任何构造器,自动继承所有父类中指定构造器和便利构造器。


三类构造器图.png

参考资料

The Swift Programming Language

上一篇下一篇

猜你喜欢

热点阅读