19.Swift学习之构造函数与析构函数

重要说明

  • 本文中提到的构造函数,在很多书中有其他的说法,如构造器,构造方法,初始化,初始函数等
  • 本文中提到的析构函数,在很多书中有其他的说法,如反构造器,析构方法,反初始化,反初始函数等

构造函数的介绍

  • 构造函数用于初始化一个类的实例(创建对象)
  • 默认情况下载创建一个类时,必然会调用一个构造函数
  • 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数
  • 如果是继承自NSObject,可以对父类的构造函数进行重写

默认构造函数

  • 在创建类和结构体的实例时必须为所有的存储属性设置一个合适的初始值,如果不是在定义时初始化值,可以在构造函数中赋值
  • 构造函数就像一个没有形式参数的实例方法,使用 init 关键字来写
init() {
    // perform some initialization here
}
class Person: NSObject {
    var name : String
    var age : Int

    // 重写了NSObject(父类)的构造方法
    override init() {
        name = ""
        age = 0
    }
}

// 创建一个Person对象
let p = Person()
自定义构造函数
  • 很多时候,我们在创建一个对象时就会给属性赋值
  • 可以自定义构造函数
  • 注意:如果自定义了构造函数,会覆盖init()方法.即不在有默认的构造函数
class Person: NSObject {
    var name : String
    var age : Int

    // 自定义构造函数,会覆盖init()函数
    init(name : String, age : Int) {
        self.name = name
        self.age = age
    }
}

// 创建一个Person对象
let p = Person(name: "why", age: 18)

结构体类型的成员构造函数

  • 如果结构体类型中没有定义任何自定义构造函数,它会自动获得一个成员构造函数。不同于默认构造函数,结构体会接收成员构造函数即使它的存储属性没有默认值
//定义了一个名为 Size 有两个属性分别是 width 和 height 的结构体,这两个属性通过分配默认值 0.0 ,从而被推断为 Double 类型
struct Size {
    var width = 0.0, height = 0.0
}
//Size 结构体自动接收一个 init(width:heght:) 构造函数
let twoByTwo = Size(width: 2.0, height: 2.0)

值类型的构造函数委托

  • 构造函数可以调用其他构造函数来执行部分实例的初始化。这个过程,就是所谓的构造函数委托。
  • 构造函数的运作,对于值类型和类类型是不同的。
  • 值类型(结构体和枚举)不支持继承,所以他它们的构造函数委托的过程相当简单。
  • 注意如果为值类型定义了自定义构造函数,就不能访问默认构造函数或者是成员构造函数

类的继承和初始化

  • 所有类的存储属性——包括从它的父类继承的所有属性都必须在初始化期间分配初始值。
  • Swift 为类类型定义了两种构造函数以确保所有的存储属性接收一个初始值。这些就是所谓的指定构造函数和便捷构造函数
    • 指定构造函数是类的主要构造函数。指定构造函数可以初始化所有那个类引用的属性并且调用合适的父类构造函数来继续这个初始化过程给父类链
    • 类偏向于少量指定构造函数,并且一个类通常只有一个指定构造函数
    • 每个类至少得有一个指定构造函数
    • 便捷构造函数是次要的,为一个类支持构造函数。你可以在相同的类里定义一个便捷构造函数来调用一个指定的构造函数作为便捷构造函数来给指定构造函数设置默认形式参数。
//类的指定构造函数
init(parameters) {
    statements
}
//便捷构造函数有着相同的书写方式,但是要用 convenience 修饰符放到 init 关键字前,用空格隔开:
convenience init(parameters) {
    statements
}

类类型的构造函数委托

  • 为了简化指定和便捷构造函数之间的调用关系,Swift 在构造函数之间的委托调用有下面的三个规则:
    • 规则 1——指定构造函数必须从它的直系父类调用指定构造函数。
    • 规则 2——便捷构造函数必须从相同的类里调用另一个构造函数。
    • 规则 3——便捷构造函数最终必须调用一个指定构造函数。
  • 简单记忆的这些规则的方法如下:
    • 指定构造函数必须总是向上委托。
    • 便捷构造函数必须总是横向委托。

    类类型的构造函数委托

构造函数的继承与重写

  • 在Swift中,子类的构造函数有两种来源,首先是自己拥有的构造函数,其次是从父类中继承过来的构造函数。但是,比不是所有父类构造函数都能够被子类继承。子类继承父类的构造函数是有条件的,遵守以下2个规则:
    • 规则1——如果子类没有定义任何指定初始化器,它会自动继承父类所有的指定初始化器
    • 规则2——如果子类提供了所有父类指定初始化器的实现——要么是通过规则1继承来的,要么通过在定义中提供自定义实现的,那么它自动继承所有的父类便捷初始化器。
  • 如果一个子类中任意的构造器和父类的便利构造器一模一样, 不算重写
  • 一个例子
class Person {   
    var name: String!
    var weight: Double  
    init(name: String) {
        self.name = name
        self.weight = 0.0
    } 
    // 定义指定构造器
    init(name: String, weight: Double) {
        self.name = name
        self.weight = weight
    }
     
    // 定义便利构造器(使用convenience修饰)
    convenience init(n name: String, w weight: Double) {
        // 便利构造器必须调用同类中的指定构造器
        self.init(name: name, weight: weight)
    }
     
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}
 
class Man: Person {
    var sex: String = "男"   
    override init(name: String) {
        // 子类的指定构造器中必须调用父类的指定构造器
        super.init(name: name)
        self.name = name
        self.weight = 0.0
    }
    override init(name: String, weight: Double) {
        super.init(name: name, weight: weight)
        self.name = name
        self.weight = weight
    }   
    // 定义指定构造器与父类的便利构造器一样, 这里不算重写
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}
 
var manA = Man(name: "ZhangSan", weight: 62.0)
var manB = Man(showStr: "Hello Swift")

可失败构造函数

  • 定义类、结构体或枚举初始化时可以失败
  • 失败可能由以下几种方式触发,包括给初始化传入无效的形式参数值,或缺少某种外部所需的资源,又或是其他阻止初始化的情况
  • 为了处理这种可能,在类、结构体或枚举中定义一个或多个可失败的构造函数。通过在 init 关键字后面添加问号init?
struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
    print("An animal was initialized with a species of \(giraffe.species)")
}

必要构造函数

  • 在类的构造函数前添加required 修饰符来表明所有该类的子类都必须实现该构造函数
  • 当子类重写父类的必要构造函数时,必须在子类的构造函数前添加 required 修饰符以确保当其它类继承该子类时,该构造函数同为必要构造函数
  • 在重写父类的必要构造函数时,不需要添加 override 修饰符
class SomeClass {
    required init() {
    }
}
class SomeSubclass: SomeClass {
    required init() {
    }
}

析构函数

  • Swift 会自动释放不再需要的实例以释放资源
    • Swift 通过自动引用计数(ARC)处理实例的内存管理
    • 当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
    • 通常在析构函数中释放一些资源(如移除通知等操作)
  • 析构函数的写法
deinit {
    // 执行析构过程
}
示例练习
class Person {
    var name : String
    var age : Int

    init(name : String, age : Int) {
        self.name = name
        self.age = age
    }

    deinit {
        print("Person-deinit")
    }
}

var p : Person? = Person(name: "why", age: 18)
p = nil

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

Go 语言数组

Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。 相...

368100
来自专栏加米谷大数据

技术分享 | Python之列表(list)解析

Python内置的一种数据类型是列表(list),list是一种有序的集合,可以随时添加和删除其中的元素,列表中的每个元素都分配一个数字,是它的位置(或者叫索引...

44670
来自专栏编程

Python面向对象5:特殊方法

特殊方法,两边带双下划线的方法。比如__init__(self,...) 、__del__(self) 、__call__(self, *args) 、__st...

199100
来自专栏前端知识分享

js数组常用方法总结

最近工作中经常用到数组操作,每次都傻傻不知道怎么用,今天有时间整理了一下,希望对大家有帮助!这些基础的知识,要熟记于心。

1K30
来自专栏编程

Python基础2字符串

String 字符串是Python中最常用的数据类型,可以用单引号和双引号创建字 符串,字符串是不可变的。 字符串的基本操作:Python内建序列包括(列表、元...

20490
来自专栏用户3030674的专栏

java线程的实现

一共有两种方法Thread类和Runnable接口,相对来讲,更趋向于用Runnable 因为一个类可以实现多个接口,但是只能继承一个类,所以相对来说倾向用Ru...

8410
来自专栏大闲人柴毛毛

C++快速入门

引用变量 引用一个变量就是定义了一个变量,和原来的变量使用同一个值。引用变量将值改变,原来这个变量的值也随之改变。 它和传地址的性质一致。 /** *定义一...

33750
来自专栏Laoqi's Linux运维专列

元祖+列表

51260
来自专栏小鹏的专栏

用一个脚本学习 python

# -*- coding: utf-8 -*- # Python 2.7 学习参考脚本 # print 打印函数 print "Hello Worl...

29570
来自专栏程序员互动联盟

【Java概念学习】--数组的初始化

一维数组的声明方式: type var[]; 或type[] var; 声明数组时不能指定其长度(数组中元素的个数), Java中使用关键字new创建数组对象,...

344110

扫码关注云+社区

领取腾讯云代金券