Swift3.0 - 初始化和释放

注意

1.系统要求存储属性必须初始化 2.可选值可以不用初始化,如果不初始化值,系统默认用nil初始化它 3.如果非可选类型存储属性不设置默认值,则必须在初始化方法中对其进行初始化 4.类必须自己写初始化方法,初始化没有默认值的非可选存储属性 5.结构体系统默认会添加初始化方法,当然自己也可以自定义 6.子类如果没有自己的初始化方法,系统默认使用父类的初始化方法,一旦有了自己的初始化方法,或者重写了父类的初始化方法,则父类的所有初始化不能被子类调用 7.你可以给子类添加和父类相同的初始化方法,但需要加上override 修饰 8.重写父类的convenience修饰的方便初始化方法,不需要加override 关键字

  • 指定初始化(Designated)

1.可以有多个指定初始化方法

class Person{
var name:String
var age:Int = 0
var weight:Double = 0.0
var height:Double = 0.0
init(name:String,height:Double) {
    self.name = name
    self.height = height
}
init(name:String) {
    self.name = name
}
}
  • 方便初始化(convenience)

记住:

1.在同一个类,使用convenience修饰的初始化方法必须调用一个其他初始化方法 2.convenience 必须最终调用一个指定的初始化方法 3.当子类继承父类时,子类的初始化方法,必须调用父类的指定初始化方法,不能调用使用convienience修饰的方便初始化方法 4.在swift3.0 初始化中,可以自己调用自己的初始化方法,系统不会检测出来,在创建convenience方便初始化方法的时候,需要小心,千万不要相互引用了

看图理解更透彻

实例代码

// 父类
class Person{
    var name:String
    var age:Int = 0
    var weight:Double = 0.0
    var height:Double = 0.0
    init(name:String,height:Double) {
        self.name = name
        self.height = height
    }
    init(name:String) {
        self.name = name
    }
    // 1.定义一个convenience 修饰的初始化方法,如果在同一个类中必须 调用其他没有convenience修饰的初始化方法
    convenience init(name:String,age:Int){
        self.init(name:name)
        self.age = age
    }
    // 2.如果定义两个或者多个convenience 修饰的初始化,只需要调用任意一个初始化方法即可满足语法要求
    convenience init(name:String,age:Int,weight:Double){
        self.init(name:name,age:age)
        self.weight = weight
    }
}
// 子类
class Man: Person {
    var address:String = ""
    init(name:String,age:Int,weight:Double) {
         // 3.必须调用父类指定初始化方法,不能调用convenience 修饰的方便初始化方法
         super.init(name: name)
    } 
}

看完代码这种图就好理解了

  • 类初始化的过程

第一阶段

1.调用指定初始化方法或者方便初始化 2.给新的实例分配内存,但内存还没有初始化 3.指定初始化方法确定所有存储属性都被初始化,内存这个时候被初始化 4.然后去调用父类的指定初始化方法,任务和调用自己指定初始化方法相同 5.继续在类继承链中指定上述过程,直到达到链的顶部为止 6.当到完成基类的初始化的时候,实例的初始化算是完成了,我们的第一阶段完成

第二阶段

1.可以对属性值进行修改 2.可以调用对象方法

  • 重写初始化方法

先看一个例子

// 父类
class Person{
    var name:String
    var age:Int = 0
    var weight:Double = 0.0
    var height:Double = 0.0
    init(name:String,height:Double) {
        self.name = name
        self.height = height
    }
    init(name:String) {
        self.name = name
    }
    convenience init(name:String,age:Int,weight:Double){
        self.init(name:name)
        self.age = age
        self.weight = weight
    }
}
 // 子类
  class Man: Person {
var address:String = ""

   // 重写父类指定初始化方法
    override init(name:String) {
        super.init(name: name)
    }
    // 重写父类convenience 修饰的初始化方法 不需要添加override 关键字
     init(name:String,age:Int,weight:Double){
        super.init(name: name)
    }
    // 创建自己的初始化方法
    convenience      init(name:String,age:Int,weight:Double,address:String){
        self.init(name: name)
        self.address = address
        self.age = age
        self.weight = weight
    }  
}

总结:

1.创建新的指定初始化方法,必须调用父类的指定初始化方法 (Designated) 2.创建新的方便初始化方法,必须调用自己的指定初始化方法,或者方便初始化方法(convenience) 3.重写父类的指定初始化方法,在方法名前加override ,然后调用父类的指定初始化方法 4.重写父类的方便初始化方法(convenience) 不需要加override 或者convenience 关键字,调用父类的指定初始化方法,如果加上convenice关键字,则必须调用自己的初始化方法 5.如果子类没有初始化方法,系统会自动继承父类的初始化方法 6.初始化调用父类初始化时,需要先初始化子类的存储属性,但是如果是convenience修饰的初始化方法,要先调用自己的其他初始化方法,然后再给自己的存储属性赋值

  • 创建一个可能失败的初始化方法

注意:

1.不能在重写的初始化方法改为可能失败的初始化方法 2.不能使用相同的参数定义一个可能失败的初始化方法和不会失败的初始化方法 3.可能失败的类型可以重写为不会失败类型 4.init? 可以被重写为init!,当然可逆也是可以的 5.init?和init! 都可以被重写为init

例子1

class Man: Person {
    var address:String = ""
    convenience  init?(name:String,age:Int,weight:Double,address:String){
    if name == "" {
        return nil
    }
    self.init(name: name)
    self.address = address
    self.age = age
    self.weight = weight
    }
}

例子2

enum TemperatureUnit {
    case kelvin, celsius, fahrenheit
    init?(symbol: Character) {
        switch symbol {
       case "K":
            self = .kelvin
        case "C":
            self = .celsius
        case "F":
            self = .fahrenheit
        default:
            return nil
        }
    }
}

例子2: 子类将父类可能失败的初始化方法,修改为不会失败的类型

class Animal{
    var name:String
    init?(name:String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

对于可能出现空值的对象或者其他类型,在使用之前必须进行验证

enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}

let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization     succeeded.")
}

需求: 创建一个文件类,文件名字可以为nil,但是不能为空即""

写法一:

class Document {
    var name:String?
    init?(name:String?){
        if name != nil && name!.isEmpty{
            return nil
        }
        self.name = name
    }
}

分析这种写法

只有一种初始化方法,也就是说,不管有没有名字,我们都需要给初始化传个参数,显然这样不合理,目标不明确

写法二:

class Document {
var name: String?
init() {} // 专门初始化name为 nil的情况 
init?(name: String) { // 传入名字 ,肯定不为nil ,只需要判断是否为空即可
    if name.isEmpty { return nil }
    self.name = name
}
}

疑问: init! 和init? 被重写为init 的意义何在?

暂时没想到

  • 需要的初始化方法(required) 注意

1.子类必须重写父类用required修饰的方法 2.可以和convenience 组合使用

a-1.父类要求一个初始化方法被重写

class Person{
    var name:String
    var age:Int = 0
    var weight:Double = 0.0
    var height:Double = 0.0
    init(name:String,height:Double) {
        self.name = name
        self.height = height
    }
    init(name:String) {
        self.name = name
    }
   // 要求子类必须重写这个方法
   required convenience init(name:String,age:Int,weight:Double){
        self.init(name:name)
        self.age = age
        self.weight = weight
    }
}

a-2.子类重写父类要求的初始化方法

class Man: Person {
    var address:String = ""
    // 重写父类要求的初始化convenience 修饰的初始化方法 不需要添加override 关键字
    required init(name:String,age:Int,weight:Double){
    super.init(name: name)
   }
}
  • 反初始化(deinit)

注意:

1.deinit 在对象被释放前调用

写法很简单

deinit {
}

苹果文档有个例子简单讲解了一下它的重要性 例子: 有一个赌徒在银行存了10_000 元,赌徒从银行取钱然后去赌博,当赌徒对象释放了就将钱全部存到银行

class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

赌徒类

class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
  }
   func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
   }
  deinit {
        Bank.receive(coins: coinsInPurse)
    }
}

执行

var playerOne: Player? = Player(coins: 100)
playerOne!.win(coins: 2_000)
playerOne = nil

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏python成长之路

字典常用操作

19740
来自专栏Java后端生活

解决JavaScript加减乘除的精度问题

25870
来自专栏Golang语言社区

Go 语言数组

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

382100
来自专栏微信公众号:Java团长

Java基础03 构造器与方法重载

在Java基础02 方法与数据成员中,我们提到,Java中的对象在创建的时候会初始化(initialization)。初始化时,对象的数据成员被赋予初始值。我们...

10010
来自专栏互联网大杂烩

堆内存和栈内存

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作...

8930
来自专栏Vamei实验室

Java基础03 构造器与方法重载

在方法与数据成员中,我们提到,Java中的对象在创建的时候会初始化(initialization)。初始化时,对象的数据成员被赋予初始值。我们可以显式初始化。如...

217100
来自专栏Android开发指南

5:面向对象总结

382120
来自专栏抠抠空间

字典 (dict) 的增删改查及其他方法

一、字典的简介     字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果...

29570
来自专栏逆向技术

C语言第八讲,指针*

            C语言第八讲,指针* 一丶简单理解指针 说到指针,很多人都说是C语言的重点. 也说是C语言的难点. 其实指针并不是难.而是很多人搞不清地...

42060
来自专栏python读书笔记

python数据分析基础day4-字典字典的定义字典创建字典元素的获取字典的排序

今天说一下重要的数据类型,字典。 字典的定义 python中字典类型就是键值对的集合,其中键在一个字典中必须是唯一的,值没有这个要求。此外,值可以是数值,字符串...

32270

扫码关注云+社区

领取腾讯云代金券