Swift 学习笔记 (初始化)

Posted 幻影-2000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 学习笔记 (初始化)相关的知识,希望对你有一定的参考价值。

初始化是为类 结构体 或者枚举准备实例的过程。这个过程需要给实例里的每一个存储属性设置一个初始值并且在新实例可以使用之前执行任何其它所必需的配置或初始化。

初始化器

初始化器在创建特定类型的实例时被调用。在这个简单的形式中,初始化器就像一个没有形式参数的实例方法,使用 init 关键字来写:

init() {
    // perform some initialization here
}

代码示例

struct Fahrenheit {
    var temperature:Double
    
    init() {
        temperature = 2.0;
    }
}

var f = Fahrenheit()
f.temperature //2

默认的属性值

struct Fahrenheit {
    
    var temperature = 32.0
    
}

初始化形式参数

struct Celsius {
    var temperatureInCelsius:Double
    
    init(fromFahrenheit fahrenheit:Double) {
        temperatureInCelsius = fahrenheit
    }
    init(fromKelvin kelvin:Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)

let freezingPointOfWater = Celsius(fromKelvin: 273.15)

可选属性类型

如果你的自定义类型有一个逻辑上是允许无值的存储属性。声明属性为可选类型。可选类型的属性自动地初始化为nil,表示该属性在初始化期间故意的设为还没有值。

class SurveyQuestion {
    var text:String
    var response:String?
    init(text:String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}

let cheeseQuestion = SurveyQuestion(text: "do you like cheese?")
cheeseQuestion.ask()
cheeseQuestion.response = "Yes,I do like cheese."

对调查问题的回答直到被问的时候才能知道,所以 response 属性被声明为 String? 类型,或者是“可选 Stirng ”。当新的 SurveyQuestion 实例被初始化的时候,它会自动分配一个为 nil 的默认值,意味着“还没有字符串”。

在初始化中分配常量属性

你可以修改上面的SurveyQuestion例子,给text使用常量属性而不是变量属性来表示问题,来明确一旦SurveyQuestion的实例被创建,那个问题将不会被改变。尽管现在的text属性是一个常量。但他依然可以在类的初始化器里设置。

class SurveyQuestion {
    let text:String
    var response:String?
    init(text:String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}

let cheeseQuestion = SurveyQuestion(text: "how about beets?")
cheeseQuestion.ask()
cheeseQuestion.response = "I also like beets."

默认初始化器

Swift为所有媒体公初始化器的结构体或者类提供了一个默认的初始化器来给所有的属性提供了默认值。这个磨人的初始化器只是简单的创建了一个所有属性都有默认值的新实例。

class shoppingListItem {
    var name:String?
    var quantity = 1
    var purchased = false
}
var item = shoppingListItem()

结构体类型的成员初始化器

如果结构体类型中没有定义任何自定义初始化器,它会自动获得一个成员初始化器。不同于默认初始化器,结构体会接收成员初始化器即使它的存储属性有默认值。

这个成员初始化器是一个快速初始化新结构体实例成员属性的方式。新实例的属性初始值可以通过名称传递到成员初始化器里

struct Size {
    var width = 0.0,height = 0.0
}
let twoByTwo = Size(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)
    }
}


let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
                      size: Size(width: 5.0, height: 5.0))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                      size: Size(width: 3.0, height: 3.0))

类的继承和初始化

class Avatar {
    var name:String
    var life = 100 {
        
        didSet {
            if life > 100 {
                life = 100
            }
            if life <= 0 {
                self.isAlive = false
            }
        }
    }
    var isAlive:Bool = true
    var description:String {
        return "I am avatar \(name)"
    }
    init(name:String) {
        self.name = name
    }
    //便利构造函数
    convenience init(firstName:String,secondName:String) {
        self.init(name:firstName + secondName)
    }
    func beAttacked(attack:Int) {
        life -= attack
        if life <= 0 {
            isAlive = false
        }
    }
}

class User:Avatar {
    var score = 0
    var level = 0
    var group:String
    //重写属性
    override var description: String {
        return "I am user \(name)"
    }

    func getScore(score:Int) {
        self.score += score
        if score > level * 100 {
            level += 1
        }
    }
    //先把子类的所有初始化完成 之后才能调用父类的构造方法
    init(name: String,group:String = "") {
        //第一阶段 构造实例
        self.group = group;
        super.init(name: name)
        //第二阶段 逻辑判断 self的使用必须在构造完成以后
        if group == "" {
            self.getScore(score: -10)
        }
    }
    //便利构造函数 构造函数中调用了自己的构造函数
    convenience init(group: String = "") {
        let name = User.generateUserName()
        self.init(name:name,group:group)
    }
    static func generateUserName() ->String {
        return "Player" + String(arc4random()%100000)
    }
}

class Magician:User {
    var magic = 100
    //重写属性
    override var description: String {
        return "I am magician \(name)"
    }
    
    func heal(user:User) {
        user.life += 10
    }
    
    //构造函数也可以重写
    override init(name: String, group: String) {
        let defalutGroups = ["Gryffindor","Hufflepuff","Ravenclaw","Slytherin"]
        for theGroup in defalutGroups {
            if group == theGroup {
                super.init(name: name, group: group)
                return
            }
        }
        let group = defalutGroups[Int(arc4random()%4)]
        super.init(name: name, group: group)
    }
}

final class Warrior:User {
    var weapon:String
    //重写属性
    override var description: String {
        return "I am warrior \(name)"
    }
    //重写方法
    override func beAttacked(attack: Int) {
        self.life -= attack / 2
    }
    //构造函数也可以有默认值
    init(name: String, group: String,weapon:String = "Sword") {
        self.weapon = weapon
        
        super.init(name: name, group: group)
    }
}

class Monster:Avatar {
    func attack(user:User,amount:Int){
        user.beAttacked(attack: amount)
    }
}

final class Zombie:Monster {
    var type = "default"
}

let user = User(name: "tian", group: "lh")
//多态 一个实例同时属于多个类 就是多态的一种形式 一个方法有不同的响应也是多态的一种形式。在OC中体现为:不同对象对同一消息的不同响应.子类可以重写父类的方法

//因为构造函数有默认值 所以在构造的时候会有两个构造函数
let warrior = Warrior(name: "tian", group: "lh")
warrior.weapon //有一个默认值

构造方法的继承

如果子类实现了父类所有的指定构造函数,难么子类会自动继承父类所有的便利构造函数。上面的例子都有体现。

可失败初始化器

定义类 结构体或者枚举初始化时可以失败在某些情况下可能会很有作用,这个失败可能会有以下几种方式触发。包括传入无效的形式参数值,或者缺少某种外部所需的资源。还有可能是其它阻止初始化的情况。

可失败的初始化器创建了一个初始化类型的可选值。你通过在可失败初始化器写 return nil 语句,来表明可失败初始化器在何种情况下会触发初始化失败。

struct Animal {
    let species:String
    init?(species:String) {
        if species.isEmpty {
            return nil
        }
        self.species = species
    }
}

if let animal = Animal(species:"") {
    animal.species
}else {
    print("传入的参数不正确 没被初始化成功")
}

枚举的可失败初始化器

你可以使用一个可失败的初始化器来带一个活着多个形式参数的枚举选择适合的情况。如果提供的形式参数没有匹配到合适的情况初始化器就可能失败。

代码示例

enum TemperatureUnit:Int {
    case Kelvin = 1,Celsius,Fahrenheit
    init?(symbol:Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}

if let temp = TemperatureUnit(symbol:"C") {
    temp.rawValue
}else {
    print("没有初始化成功")
}

初始化失败的传递

类 结构体 枚举的可失败初始化器可以横向委托到同一个类 结构体或枚举里的另一个可失败初始化器。类似地,子类的可失败初始化器可以向上委托到父类的可失败初始化器。

无论哪种情况,如果你委托到另一个初始化器导致了初始化失败,那么整个初始化过程也会立即失败,并且之后任何初始化代码都不会执行。

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

class CartItem: Product {
    let quantity: Int
    init?(name:String, quantity:Int ) {
        if quantity < 1 {
            return nil
        }
        self.quantity = quantity
        super.init(name: name)
    }
}

//如果你用不能为空 name 属性和数量为 1 或者更多来创建 CartItem 实例,则初始化成功:
if let twoSocks = CartItem(name: "sock", quantity: 2) {
    print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
//如果你创建了一个 CartItem 实例, quantity 的值为 0 , CartItem 初始化器会导致初始化失败:
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
    print("Unable to initialize zero shirts")
}

//类似地,如果你尝试创建一个 CartItem 实例,并且令 name 为空值,那么父类 Product 的初始化器就会导致初始化失败:
if let oneUnnamed = CartItem(name: "", quantity: 1) {
    print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
    print("Unable to initialize one unnamed product")
}

重写可失败的初始化器

你可以在子类里重写父类的可失败初始化器。就好比其他的初始化器。或者,你可以用子类的非失败初始化器来重写父类的可失败初始化器,这样允许你定义一个初始化不会失败的子类,尽管父类的初始化允许失败。

但是需要注意的是,当你使用子类非失败的初始化器重写了一个可失败的初始化器,向上委托到父类初始化器的唯一方法是强制展开父类可失败初始化器的结果。

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

class AutomaticallyNamedDocument: Document {
    override init() {
        super.init()
        self.name = "[Untitled]"
    }
    
    override init(name:String) {
        super.init()
        if name.isEmpty {
            self.name = "[Untitled]"
        }else {
            self.name = name
        }
    }
}

AutomaticallyNamedDocument 类用非可失败的 init(name:) 初始化器重写了父类的可失败 init?(name:) 初始化器。因为 AutomaticallyNamedDocument 类用不同的方式处理了空字符串的情况,它的初始化器不会失败,所以它提供了非可失败初始化器来代替。

必要的初始化器

在类的初始化器前添加required修饰符来表明所有的该类的子类都要实现该初始化器。

class SomeClass {
    var name:String
    required init(name:String) {
        self.name = name
    }
}

class subClass:SomeClass {
    required init(name: String) {
        super.init(name: name)
    }
}

当子类重写父类的必要初始化器时,必须在子类的初始化器前同样添加 required 修饰符以确保当其它类继承该子类时,该初始化器同为必要初始化器。在重写父类的必要初始化器时,不需要添加 override 修饰符。

通过闭包和函数来设置属性的默认值

class SomeClass {
    //这个实际上是一个getter函数
    var name:String {
        //get {
        //   return "tianlanlan"
        //}
        //效果一样
        return "tianlanlan"
        
    }
    //通过闭包设置属性的默认值
    var age:Int = {
        return 18
    }()
}

上面就是Swift中所有的初始化器的简单介绍。可以看到比OC中要丰富的多。另外Swift中也提供了一种反初始化机制。有点类似于OC中的dealloc方法。

反初始化

在类实例被释放的时候,反初始化器就会被立即调用。可以使用deinit关键字来写反初始化器。

反初始化的原理

当实例不在被需要的时候Swift会自动将其释放掉。以节省资源。代码示例

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)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
//释放掉这个实例
playerOne = nil
Bank.coinsInBank //10000 说明调用了deinit的方法

 

以上是关于Swift 学习笔记 (初始化)的主要内容,如果未能解决你的问题,请参考以下文章

Swift学习笔记-内存安全性

Swift学习笔记-内存安全性

Swift学习笔记:字符串

swift学习笔记构造过程

Swift学习笔记九:类 Class

Swift学习笔记:集合类型