Swift之家

Swift-构造器使用介绍

2017-11-13  本文已影响2人  小小土豆dev

构造过程:是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。

通过定义构造器来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

构造器分为:指定构造器、遍历构造器(convenience)


存储属性的初始赋值

类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。

方式1:可以在构造器中为存储型属性赋初值

方式2:可以在定义属性时为其设置默认值

当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。

最简单的,不带参数的构造器

struct Fahrenheit {

  var temperature: Double

  init() {

    temperature = 32.0

  }}

调用构造器分为两种方式:

var f = Fahrenheit()

var ff = Fahrenheit.init()

方式1:可以用类名加括号方式调用Fahrenheit(),如果有参数,括号中间是参数名称。

方式2:可以用类名加init方法调用Fahrenheit.init(),如果有参数,init后面括号中间是参数名称。


自定义构造过程

struct Celsius {

  var temperatureInCelsius: Double

  init(fromFahrenheit fahrenheit: Double) {

    temperatureInCelsius = (fahrenheit - 32.0) / 1.8

  }

  init(fromKelvin kelvin: Double) {

    temperatureInCelsius = kelvin - 273.15

  }

}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)

let freezingPointOfWater = Celsius(fromKelvin: 273.15)

参数的内部名称和外部名称:

构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。

struct Color {

  let red, green, blue: Double

  init(red: Double, green: Double, blue: Double) {

    self.red= red

    self.green= green

    self.blue= blue

}

let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

let veryGreen = Color(0.0, 1.0, 0.0) // 报编译时错误,需要外部名称

不带外部名的构造器参数

如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线( _ )来显式描述它的外部名,以此重写上面所说的默认行为

structCelsius2 {

  var temperatureInCelsius: Double

  init(_ celsius: Double){

    temperatureInCelsius= celsius

  }

}

let bodyTemperature = Celsius2(37.0)


构造过程中常量属性的修改

你可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。

class SurveyQuestion2 {

  let text: String

  init(text: String) {

    self.text= text

  }

}

let beetsQuestion = SurveyQuestion2(text: "How about beets?”)

默认构造器

如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例

class ShoppingListItem {

  var name: String?

  var quantity = 1

  var purchased = false

}

var item = ShoppingListItem()

由于 ShoppingListItem 类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为 name 属性设置默认值,但由于 name 是可选字符串类型,它将默认设置为 nil)。

结构体的逐一成员构造器

1、除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。

2、逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。

struct Size1 {

  var width = 0.0, height = 0.0

}

let twoByTwo = Size1(width: 2.0, height: 2.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、类里面的所有存储型属性(包括所有继承自父类的属性)都必须在构造过程中设置初始值。

2、Swift为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是 指定构造器 和 便利构造器。

指定构造器

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。

便利构造器

便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。

你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。

指定构造器 和 便利构造器 的语法

类的指定构造器的写法跟值类型简单构造器一样

init(parameters) {

  statements

}

便利构造器也采用相同样式的写法,但需要在 init 关键字之前放置 convenience 关键字,并使用空格将它们俩分开

convenience init(parameters) {

  statements

}

类的构造器代理规则

规则 1 :指定构造器必须调用其直接父类的的指定构造器。

规则 2 :便利构造器必须调用同类中定义的其它构造器。

规则 3 :便利构造器必须最终导致一个指定构造器被调用。

指定构造器必须总是向上代理

便利构造器必须总是横向代理

Swift中类的构造过程包含两个阶段:

第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,

第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存 储型属性。

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

构造器的继承和重写

class Vehicle {

  var numberOfWheels = 0

  var description: String {

    return "\(numberOfWheels) wheel(s)"

  }

}

class Bicycle: Vehicle {

  override init() {

    super.init()

    numberOfWheels = 2

  }

}

let bicycle = Bicycle()print("Bicycle: \(bicycle.description)")

构造器的自动继承

规则1 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。

规则2 如果子类提供了所有父类指定构造器的实现,无论是通过规则 1 继承过来的,还是提供了自定义实现,它将自动继承所有父类的便利构造器


可失败构造器

如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在 init 关键字后面添加问号( init? )。

这个可失败构造器检查传入的参数是否为一个空字符串。如果为空字符串,则构造失败。否则, species 属性被赋值,构造成功。

struct Animal {

  let species: String

  init?(species: String) {

    if species.isEmpty {

      return nil

    }

    self.species= species

  }

}

重写一个可失败构造器

class Document {

  var name: String?

  // 该构造器创建了一个 name 属性的值为 nil 的 document 实例

  init () {}

  // 该构造器创建了一个 name 属性的值为非空字符串的 document 实例

  init?(name: String) {

    self.name = name

    if name.isEmpty {

    return nil

    }

  }

}

class AutomaticallyNamedDocument: Document {

  override init () {

    super.init ()

    self.name = "[Untitled]"

  }

  //用一个非可失败构造器 init(name:) 重写了父类的可失败构造器 init?(name:) 。

  override init(name: String) {

    super.init()

    if name.isEmpty {

      self.name = "[Untitled]"

    } else {

      self.name = name

    }

  }

}


必要构造器

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器:

class SomeClass {

  required init () {

    // 构造器的实现代码

  }

}

在子类重写父类的必要构造器时,必须在子类的构造器前也添加 required 修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加 override 修饰符:

class SomeSubclass: SomeClass {

  required init () {

    // 构造器的实现代码

  }

}


析构器

析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字 deinit 来标示,类似于构造器要用 init 来标示。

析构过程原理

1、Swift 会自动释放不再需要的实例以释放资源。Swift 通过自动引用计数(ARC) 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。

2、在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数

deinit {

  // 执行析构过程

}

3、析构器是在实例释放发生前被自动调用。你不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。

4、因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。

上一篇下一篇

猜你喜欢

热点阅读