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的方法
时间: 2024-12-18 10:40:23

Swift 学习笔记 (初始化)的相关文章

swift学习笔记-&gt;初始化

这节重点讲下swift的初始化函数   也就是构造函数init 类,结构,枚举  都有初始化函数,就拿类举例 首先说明一点   在类的初始化时  所有储存属性必须有值   这点在前面类的学习中也有提到过 class test{ var i:Int } 所以说这样是会报错的 初始化方法  构造器 class test{ var i:Int = 1 }//此时该类拥有一个默认的初始化方法 方法中给变量ii赋值 class test1{ var i:Int init (){ self.i=1 } }

Swift学习笔记十:属性

1.存储属性       1. 作为特定类或结构实例的一部分,存储属性存储着常量或者变量的值.存储属性可分为变量存储属性(关键字var描述)和常量存储属性(关键字let描述). struct student{ let name = "" var score = 0 } let a = student(name:"小笨狼",score:96)           注意:                ① 定义储存属性时,需要为每一个属性定义一个默认值.在初始化的时候,

Swift学习笔记(4)--字符串及基本使用

String是例如"hello, world","海贼王" 这样的有序的Character(字符)类型的值的集合,通过String类型来表示. Swift 的String类型与 Foundation NSString类进行了无缝桥接.如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作.所有NSString API 都可以调用您创建的任意String类型的值.除此之外,还可以使用本章介绍的String特性.您也可以在任意要求传

Swift学习笔记

Apple 新推的Swift已经好几天了.对于向我这样的oc都还没完全琢磨透彻的菜鸟来说--(简直就是福利啊,joke) 看了几天的Swift,只是有了基本的印象.总感觉比较换混乱,可能源自与自己没怎么学过脚本语言.索性,边看边记,加深印象. 本来部分内容源自Apple的<The Swift Programming Language>以及互联网教程.其余内容均为个人理解,不保证正确. 进入正题: 1.Swift是什么? Apple唤他作:雨燕.其实英语过了四级的都应该看出来,Swift还有一层

swift学习笔记(七)自动引用计数

与Object-c一样,swift使用自动引用计数来跟踪并管理应用使用的内存.当实例不再被使用时,及retainCount=0时,会自动释放是理所占用的内存空间. 注:引用计数仅适用于类的实例,因为struct和enumeration属于值类型,也就不牵涉引用,所以其存储和管理方式并不是引用计数. 当一个实例被初始化时,系统会自动分配一定的内存空间,用于管理属性和方法.当实例对象不再被使用时,其内存空间被收回. swift中的引用类型分为三种,即Strong强引用,weak弱引用和无主引用unw

Swift学习笔记:类和结构

一.类和结构的异同 类和结构有一些相似的地方,它们都可以: 1. 定义一些可以赋值的属性: 2. 定义具有功能性的方法 3. 定义下标,使用下标语法 4. 定义初始化方法来设置初始状态 5. 在原实现方法上的可扩展性 根据协议提供某一特定类别的基本功能 1. 类还有一些结构不具备的特性: 2. 类的继承性 3. 对类实例实时的类型转换 4. 析构一个类的实例使之释放空间 5. 引用计数,一个类实例可以有多个引用 1. 定义语法 struct Name{ let firstName = "&quo

swift学习笔记(六)析构过程和使用闭包对属性进行默认值赋值

一.通过闭包和函数实现属性的默认值 当某个存储属性的默认值需要定制时,可以通过闭包或全局函数来为其提供定制的默认值. 注:全局函数结构体和枚举使用关键字static标注    函数则使用class关键字标注 当对一个属性使用闭包函数进行赋值时,每当此属性所述的类型被创建实例时,对应的闭包或函数会被调用,而他们的返回值会被作为属性的默认值. ESC: Class SomeCLass{ let someProperty:SomeType={ //给someProperty赋一个默认值 //返回一个与

SWIFT学习笔记04

1.在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能. 2.for character in "Dog!" { println(character) } // D // o // g // ! 3.通过标明一个Character类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量: let yenSign: Character = "¥" 4.计算字符数量 l

Swift学习笔记(13)--属性 (Properties)

普通属性用var和let即可,本文不做详述 1.延迟存储属性 延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性.在属性声明前使用@lazy来标示一个延迟存储属性. class DataImporter { /* DataImporter 是一个将外部文件中的数据导入的类. 这个类的初始化会消耗不少时间. */ var fileName = "data.txt" // 这是提供数据导入功能 } class DataManager { @lazy var importer = D