前言

  • 与 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. redis 安装报错

    CentOS 6.5 安装 Redis 执行 make #error "Newer version of jemalloc required" 根据你系统安装时或之后安装的选项的情 ...

  2. yii源码一 -- CComponent

    CComponent: path:framework/base/CComponent.php overview:This file contains the foundation classes fo ...

  3. linux:C++的socket编程

    基本的局域网聊天 局域网聊天TCP服务端: #include <sys/types.h> #include <sys/socket.h> #include <stdio. ...

  4. Oracle数据表中输入引号等特殊字符

    Oracle输入特殊字符的特殊方法: UPDATE BOOKMARK SET BM_VALUE=q'/ --在这里写下需要输入的内容(可以包括引号.回车等特殊的符号),所见即所得 /' -- WHER ...

  5. Android 之 Fagment 完全解析

    Android 上的界面展示都是通过 Activity 实现的,Activity 非常常用,不再赘述.但是 Activity 也有它的局限性,同样的界面在手机上显示可能很好看,在平板上就未必了,因为平 ...

  6. Javascript中怎样获取统一管理的Java提示语

    项目开发中,各个页面.各个业务操作都会使用提示语.面对这么多message,更好的方式是统一管理这些消息. 这样在做国际化的时候进行统一处理也变的方便. 推荐方案使用数据库来管理全部提示语,在项目启动 ...

  7. Java Netty (2)

    通过一个实例来说明Netty的使用.用1个服务器连接5个客户端线程,客户端连接上服务器以后就向服务器发送消息,服务器接收到消息后向客户端返回消息,客户端接收到消息以后,等待随机的时间,再向服务端发送消 ...

  8. PHP如何发送邮件

    例如使用163邮箱 SMTP,Simple Mail Transfer Protocol,简单邮件传输协议 保证我们的邮件服务器开启了SMTP服务 设置授权码   使用PHPMailer类发送邮件 1 ...

  9. MySQL 批量杀mysql线程

    mysql> SELECT concat('KILL ',id,';') FROM information_schema.processlist WHERE user='root'; +---- ...

  10. go1.8之安装配置

    说明: 之前学习过go语言(大概是0.9版本),后来更新太快,也没怎么使用,就荒废掉了,今年有项目需要用go开发,重新捡起. 这是我在学习go语言过程中整理的内容,这里记录下,也方便我以后查阅. 操作 ...