前言

  • 与 OC 一样,Swift 中也存在构造和析构过程。不同的是,OC 中的构造方法和析构方法只是普通的方法,而 Swift 中构造器和析构器是一种特殊的结构。

1、构造器

  • 在 Swift 中,类或者结构体在初始化时必须保证它们中包含的所有属性都被初始化,构造器用来初始化类或者结构体中的属性,在形式上构造器和方法很像,但是它并不是一个方法。

    • 声明构造器不需要 func 关键字。

    • 和 OC 类似,在 Swift 中使用 init 表义构在器。

    • 在一个类或者结构体中可以有多个构造器,所有的构造器都叫 init,系统通过重载的特性来判断使用那个构造器。

    • 在参数命名上可以使用和属性相同的名称做对应,如果构造器中参数的名称和属性的名称相同,则使用 self 关键字标示属性。

    • 在类中 Swift 提供三种构造器来做初始化:指定构造器、便利构造器和可失败构造器。

      // 指定构造器
      
      init(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
      // 便利构造器
      
      convenience init(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
      // 可失败构造器
      
      init?(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
  • 在某些情况下系统会自动生成一个构造器。

    • 如果是在某个类中,当类中的所有属性都有初始值,并且类中没有定义构造器时,就会得到一个没有参数的构造器 init()
    • 如果是在结构体中,当类中的所有属性都有初始值,并且结构体中没有定义构造器时,就会得到一个默认将所有属性作为参数的构造器。
  • 使用构造器时注意事项。

    • 在任何构造器完成时,必须保证所有的属性都被初始化了,即便可选型属性的值是 nil,也算它有值。
    • 调用类中的方法和属性必须在初始化完成之后才能进行。
  • 在 Swift 中采用继承式初始化方式。

    • 父类和子类的构造器进行交互时,是通过各自的指定构造器进行交互的。
    • 便捷构造器只关心本类中的其它构造器,而不关心父类或者子类的构造器。
    • 如果在构造器前加上 required 关键字,那么这个类的子类就必须实现它的这个构造器。

1.1 指定构造器

  • 指定构造器包含系统自动生成的构造器和除便利构造器及可失败构造器之外的所有构造器,是默认的初始化方法。

    • 指定构造器必须在 init 中调用父类的指定构造器,而不能调用其自身的其它构造器。

      init(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
  • 如果没有在类中实现任何指定构造器,那么将继承父类中的所有指定构造器。

    // 父类
    
    class Transport {
    
        var scope = ""
    
        init() {}                                       // 无参数指定构造器
    
        init(str: String) {                             // 有一个参数的指定构造器
    self.scope = str
    }
    }
    // 子类
    
    class Car: Transport {
    // 没有在类中实现任何指定构造器
    }
    // 使用
    
    let myCar = Car()                                   // 使用了父类中的无参数构造器
    let myNewCar = Car(str: "ludi") // 使用父类中有一个参数的构造器
  • 一旦在子类中创建了自己的指定构造器,将不能再使用父类中的构造器,并且子类中的指定构造器声明中需要调用父类中的某个指定构造器。

    // 父类
    
    class Transport {
    
        var scope = ""
    
        init() {}                                       // 无参数指定构造器
    
        init(str: String) {                             // 有一个参数的指定构造器
    self.scope = str
    }
    }
    // 子类
    
    class Car: Transport {
    
        var wheel = "pulisitong"
    
        init(scope: String, wheel: String) {            // 创建自己的指定构造器
    
            super.init()                                // 指定构造器必须调用父类的指定构造器
    
            self.scope = scope
    self.wheel = wheel
    }
    }
    // 使用
    
    let myCar = Car(scope: "ludi", wheel: "miqilin")    // 此时不能使用父类中无参或者有一个参数的构造器

1.2 便利构造器

  • 调用其它构造器的构造器就叫便利构造器。

    • 便利构造器必须调用一个其它的构造器。

    • 便利构造器必须直接或者间接调用指定构造器之后才能访问其它值,它还可以通过调用其它便利构造器来间接调用指定构造器。

    • 如果一个便利构造器想要调用指定构造器,则它必须并且只能调用本类中的指定构造器,而不能调任何父类的构造器。

      convenience init(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
  • 如果子类中重写了父类中所有的指定构造器,那么将继承父类中所有的便利构造器。

    // 父类
    
    class Transport {
    
        var scope = ""
    
        init() {}                                       // 无参数指定构造器
    
        init(str: String) {                             // 有一个参数的指定构造器
    self.scope = str
    } convenience init(scope: String) { // 便利构造器
    self.init(str: scope) // 调用自身类的指定构造器
    }
    }
    // 子类
    
    class Car: Transport {
    
        override init() {                               // 重写父类指定构造器
    super.init() // 指定构造器必须调用父类的指定构造器
    } override init(str: String) { // 重写父类指定构造器
    super.init() // 指定构造器必须调用父类的指定构造器 self.scope = "Car" + str
    }
    }
    // 使用
    
    let myCar = Car(scope: "ludi")                      // 调用父类的便利构在器

1.3 可失败构造器

  • 有一些初始化方法允许失败,并且返回 nil

    • 可失败构造器的定义中 init 后面跟着一个 ?

      init?(参数名1: 参数类型, 参数名2: 参数类型, ...) {
      
          statements
      }
  • 通常面对这种可失败构造器建议使用 if-letguard-let-else 结构,如果初始化成功就执行操作,否则就做其它操作。

    • if-let 结构

      if let image = UIImage(named: "test") {
      
          // 执行与 image 有关的代码
      
      } else {
      
          // 执行与 image 无关的代码
      }
    • guard-let-else 结构

      guard let image = UIImage(named: "test") else {
      
          // 执行与 image 无关的代码
      } // 执行与 image 有关的代码

1.4 匿名构造器

  • 有些时候某种对象的子类我们只创建一次,没有必要专门写一个构造器。

    • 使用闭包的形式创建一个匿名的构造器,闭包后需要加 (),用来告诉 Swift 需要立刻执行此闭包,否则会把闭包本身作为值赋给了属性。

      let button: UIButton = {
      
          let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
      button.backgroundColor = UIColor.white return button
      }()
    • 使用其它对象方法创建一个匿名的构造器。

      let someArray = ["1", "2", "3"]
      let someString = someArray.joined(separator: ", ") print(someString) // 1, 2, 3

2、析构器

  • 和构造器对应,Swift 还提供了析构器,用于释放对象。

    • 析构器只适用于类。

    • 一个类中只能定义一个析构器。

    • 析构器的定义和构造器很像,使用关键字 deinit,并且析构器没有参数。

      deinit() {
      
          // 执行析构过程
      }
  • 在把对象设置为 nil 时,系统会自动调用析构器。

    deinit {
    
        self.conn.close()
    self.conn = nil
    }
  • 由于 Swift 中引入了自动引用计数(ARC),因此不需要读者手动管理内存,但在使用一些自己的资源的时候,需要使用析构器。

    • 比如我要建立一个数据库访问类,在初始化时打开链接,如果程序退出,连接不释放,资源就会浪费。
  • 值类型没有析构是因为值类型不会出现 “共享”,每个值类型只有一个拥有者。

    • 比如某个方法,值类型会在其拥有者被系统回收时一同回收,我们不需要在意值类型的生命周期。

Swift 构造与析构的更多相关文章

  1. Swift的构造和析构过程

    构造过程 Swift的构造过程通过定义构造器来实现. 只是与Objective-C不同的是,Swift的构造器不须要返回值,相同也不须要表明Func. 另外值得提的是,当构造器中为存储型属性赋值时.不 ...

  2. C++浅析——继承类中构造和析构顺序

    先看测试代码,CTEST 继承自CBase,并包含一个CMember成员对象: static int nIndex = 1; class CMember { public: CMember() { p ...

  3. Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数

    在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).

  4. 【09】绝不在构造和析构过程中调用virtual方法

    1.绝不在构造和析构过程中调用virtual方法,为啥? 原因很简单,对于前者,这种情况下,子类专有成分还没有构造,对于后者,子类专有成分已经销毁,因此调用的并不是子类重写的方法,这不是程序员所期望的 ...

  5. C++不能中断构造函数来拒绝产生对象(在构造和析构中抛出异常)

    这是我的感觉,具体需要研究一下- 找到一篇文章:在构造和析构中抛出异常 测试验证在类构造和析构中抛出异常, 是否会调用该类析构. 如果在一个类成员函数中抛异常, 可以进入该类的析构函数. /// @f ...

  6. STL——空间配置器(构造和析构基本工具)

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作,默默付出.但若以STL的实现角度而言,第一个需要介绍的就是空间配 ...

  7. 魔法方法:构造和析构 - 零基础入门学习Python041

    魔法方法:构造和析构 让编程改变世界 Change the world by program 构造和析构 什么是魔法方法呢?我们来系统总结下: - 魔法方法总是被双下划线包围,例如__init__ - ...

  8. 再探Delphi2010 Class的构造和析构顺序

    发了上一篇博客.盒子上有朋友认为Class的构造和析构延迟加载.是在Unit的初始化后调用的Class的构造.在Unit的反初始化前调用的Class的析构函数. 为了证明一下我又做了个试验 unit ...

  9. Effective C++_笔记_条款09_绝不在构造和析构过程中调用virtual函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 为方便采用书上的例子,先提出问题,在说解决方案. 1 问题 1: ...

随机推荐

  1. [Functional Programming] Daggy

    const daggy = require('daggy'); const {tagged, taggedSum} = daggy; const Coord = daggy.tagged('Coord ...

  2. cocos2d-x3.0 XML解析

    在2dx3.0中xml解析已经不用自己找库了.已经为我们集成好了. text.xml <?xml version ="1.0" encoding ="UTF8&qu ...

  3. setUserVisibleHint-- fragment真正的onResume和onPause方法

    现在越来越多的应用会使用viewpager+fragment显示自己的内容页,fragment和activity有很多共同点,如下图就是fragment的生命周期 但是fragment和activit ...

  4. 【转】java 线程的几种状态

    java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明: NEW 状态是指线程刚创建, 尚未启动 RUNNABLE 状态是线程正在正常 ...

  5. "___gxx_personality_v0", referenced from:

    这是因为里面有用到C++ 的一些东西.所以会出现这个问题 两种解决办法. 第一种.TARGETS -> Build Phases -> Link Binary With Libraries ...

  6. 什么是Session分布式共享

    在了解session分布式共享之前先来了解Session.Redis和Nginx的相关知识. 一.Session相关知识 1.Session 介绍 Session在网络应用中,称为“会话控制”. 每个 ...

  7. Cocos2d-X中的声音和音效

    在玩游戏时都会有各种游戏音,如启动游戏时会有背景音,胜利或者失败会有一些音效.在Cocos2d-X中能够使用CocosDenshion实现各种声音 在使用CocosDenshion之前须要在程序中加入 ...

  8. Android ——VideoView禁止"无法播放该视频"弹窗

    我们在使用videoView播放视频时,如果获取内容失败.网址不对.或者视频格式不对等,会弹出“无法播放该视频”的弹窗,阻塞用户使用. 这种情况,如果在一些自助服务类场合下,弹窗会造成十分不友好的用户 ...

  9. windows命令行 批量对源代码添加版权头/头信息

    简短精悍的代码,特别适合开源项目使用. for /r %%F in (*.as) DO ( move "%%F" tmp.txt type copyright.txt > & ...

  10. Double-check idiom for lazy initialization of instance fields