Swift-构造器使用介绍

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

通过定义构造器来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 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、因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容