part 2:(Initialization,Deinitialization)

14Initialization


Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.


初始化就是准备类,结构体,枚举实例的过程。

包括设置存储属性值,执行该有的设置和实例使用之前需要做的一些事情


14.1Setting Initial Values for Stored Properties


存储属性的初始化可以放在初始化函数中,也可以声明属性时同时赋默认值,


14.1.1Initializers


初始化函数


14.1.2Default Property Values


如果某个存储属性每次都是初始化成相同的值,那么建议写成默认值的形式,而不是在初始化函数中赋值,虽然结果相同,但是默认值的形式看起来更加简练。而且默认值的形式可以使你在继承时处理起来更简单。


14.2Customizing Initialization


可以自定义带参的初始化过程


14.2.1Initialization Parameters


struct Celsius {

   var temperatureInCelsius: Double

   init(fromFahrenheit fahrenheit: Double) {

       temperatureInCelsius = (fahrenheit - 32.0) / 1.8

    }

   init(fromKelvin kelvin: Double) {

       temperatureInCelsius = kelvin - 273.15

    }

}

let boilingPointOfWater =Celsius(fromFahrenheit: 212.0)

// boilingPointOfWater.temperatureInCelsius is 100.0

let freezingPointOfWater =Celsius(fromKelvin: 273.15)

// freezingPointOfWater.temperatureInCelsius is 0.0


14.2.2Local and External Parameter Names


局部和外部参数名


1,跟函数和方法参数类似,初始化函数的参数也有局部名称和外部名称。

2,初始化函数不像其他方法可以自定义函数名,所以参数名就显得特别重要,因此swift会自动为每个参数添加外部名称。


struct Color {

   let red, green, blue: Double

   init(red: Double, green:Double, blue: Double) {

       self.red   = red

       self.green = green

       self.blue  = blue

    }

   init(white: Double) {

       red   = white

       green = white

       blue  = white

    }

}


let magenta =Color(red: 1.0, green:0.0, blue: 1.0)

let halfGray =Color(white: 0.5)


let veryGreen =Color(0.0, 1.0, 0.0)

// this reports a compile-time error - external names are required


14.2.3Initializer Parameters Without External Names


如果你是不想要初始化函数有外部名称,你可以在外部名称的位置写上下划线以指明你不想要外部名称。


14.2.4Optional Property Types


class SurveyQuestion {

   var text: String

   var response: String?

   init(text: String) {

       self.text = text

    }

   func ask() {

       println(text)

    }

}

let cheeseQuestion =SurveyQuestion(text: "Do you like cheese?")

cheeseQuestion.ask()

// prints "Do you like cheese?"

cheeseQuestion.response ="Yes, I do like cheese."


response是个可选属性,初始化时自动被初始化为nil


14.2.5Modifying Constant Properties During Initialization


初始化的时候修改常量属性

只有引入常量的那个类在初始化时可以修改次常量,子类中不能修改


把上面例子中类 的问题属性设置成常量


class SurveyQuestion {

   let text: String

   var response: String?

   init(text: String) {

       self.text = text //初始化时修改常量值

    }

   func ask() {

       println(text)

    }

}

let beetsQuestion =SurveyQuestion(text: "How about beets?")

beetsQuestion.ask()

// prints "How about beets?"

beetsQuestion.response ="I also like beets. (But not with cheese.)"


14.3Default Initializers


Swift provides a default initializer for any structure or base class that provides default values for all of its properties and does not provide at least one initializer itself


swift为符合条件的结构体和基类提供默认的初始化函数  1)所有属性都有默认值  2)没有显示的初始化函数


class ShoppingListItem {

   var name: String?

   var quantity = 1

   var purchased = false

}

var item = ShoppingListItem()

//{nil quantity 1 purchased false}


14.3.1Memberwise Initializers for Structure Types


Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers. This is true even if the structure’s stored properties do not have default values.


不管存储属性有没有默认值,结构体类型如果没有自定义初始化函数,那么将自动有一个按成员逐一初始化的初始化函数。



struct Size {

   var width = 0.0, height =0.0

}

let twoByTwo =Size(width: 2.0, height:2.0)


我试了下,把var改成let也是可以的


14.4Initializer Delegation for Value Types


Initializers can call other initializers to perform part of an instance’s initialization. This process, known asinitializer delegation, avoids duplicating code across multiple initializers.


初始化函数可以调用其他的初始化函数。这个过程叫做初始化代理,目的是避免初始化函数间重复编码。


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)

    }


}


注意:由于自定义的初始化函数会隐藏掉默认的初始化函数,如果你想自定义初始化函数,又想保留默认的初始化函数,你可以把你的初始化函数写在extension中


所以上面的可以改成


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)

//    }


}


extension Rect{

   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 basicRect =Rect()

// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)


let originRect =Rect(origin: Point(x:2.0, y: 2.0),

    size:Size(width: 5.0, height:5.0))

// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)


let centerRect =Rect(center: Point(x:4.0, y: 4.0),

    size:Size(width: 3.0, height:3.0))

// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)


14.5Class Inheritance and Initialization


初始化的时候,一个类的所有属性包括从父类继承下来的属性都应该被初始化

为确保所有属性都被初始化,swift定义了两类初始化函数designated initializers and convenience initializers


14.5.1Designated Initializers and Convenience Initializers


怎么翻译呢?设定构造器和便捷构造器吧


设定构造器是类的主要的构造器。设定构造器先初始化本身引进的属性,然后调用父类的构造器继续初始化。


每个类都必须至少有一个设定构造器


便捷构造器是次要的。你可以定义便捷构造器来调用设定构造器,并为其参数传递默认值。

便捷构造器不是必须的,只是提供一种更便捷的初始化方式而已


14.5.2Syntax for Designated and Convenience Initializers


设定构造器语法格式

init(parameters) {

    statements

}


便捷构造器在init前加convenience

convenience init(parameters) {

    statements

}


14.5.3Initializer Chaining


初始化链

为了简化设定构造器与便捷构造器之间的关系,swift提供以下规则


规则一:设定构造器必须调用直接父类的设定构造器

规则二:便捷构造器必须调用同类的另一个构造器

规则三:便捷构造器必须最终调用设定构造器


14.5.4Two-Phase Initialization


两段式初始化

第一阶段初始化本类引入的存储属性

第二阶段修改存储属性的值


提醒:swift的两段式初始化过程与oc的相似。主要的区别是在第一阶段,oc都是0nil。而swift的初始化可以自定义初始值,这样可以应付那些不能初始化成0或nil的值。


swift编译器提供了4种安全检查来确保两段式初始化过程无误的完成


安全检查1

设定构造器在调用父类构造器之前必须初始化所有本类引进的存储属性


安全检查2

设定构造器在给继承属性赋值之前必须先调用父类构造器


安全检查3

便捷构造器在给属性赋值之前必须先调用其他的构造器


安全检查4

构造器在第一阶段完成之前不能调用实例方法,不能读取实例属性,不能使用self

 



两阶段初始化展开


阶段一:

1)设定构造器或便捷构造器在类上被调用

2)开辟内存空间

3)设定构造器确保该类引入的所有存储属性都被初始化

4)设定构造器调用父类的构造器来执行相同的检查工作

5)父类构造器调用父父类的构造器直到整个构造链完成

6)一旦最顶端的构造器调用完成,那么就可以确定最终类的所有存储属性已经被初始化,这个实例的内存也全部初始化完毕,第一阶段完毕


阶段二:

1)从构造链的最顶端返回回来,构造链上的每一个设定构造器都有机会修改实例的值,这时可以访问self属性,可以修改属性值,调用实例的方法等等。

2)最后便捷构造器也可以修改实例,也可以使用self


14.5.5Initializer Inheritance and Overriding


不像oc的子类,swift的子类并不默认继承父类的构造函数。swift这样做是为了防止子类在初始化时没有正确地,完全地初始化。


class Vehicle {

   var numberOfWheels = 0

   var description: String {

        return"\(numberOfWheels) wheel(s)"

    }

}


let vehicle =Vehicle()

println("Vehicle:\(vehicle.description)")

// Vehicle: 0 wheel(s)


class Bicycle:Vehicle {

    override init() {

       super.init()

        numberOfWheels =2

    }

}


let bicycle =Bicycle()

println("Bicycle:\(bicycle.description)")

// Bicycle: 2 wheel(s)


14.5.6Automatic Initializer Inheritance


前面提到,子类默认并不继承父类的构造函数。然而,在满足某些条件的情况下还是可以自动继承父类的构造函数的。实际上,这就意味着不用你写很多很多相同的构造函数。


假设你为子类新增的存储属性都提供了默认值。那么:

规则一:如果子类没有定义设定构造器,那么自动继承父类的所有的设定构造器

规则二:如果子类提供了父类所有设定构造器的实现(不管是按规则一的继承,还是自己自定义的实现),那么将自动继承所有父类的便捷构造器。


注意:子类可以实现父类的设定构造器作为子类的便捷构造器,以满足规则二。



14.5.7Designated and Convenience Initializers in Action



class Food {

   var name: String

   init(name: String) {

       self.name = name

    }

    convenience init() {

       self.init(name:"[Unnamed]")

    }

}


let namedMeat =Food(name: "Bacon")

// namedMeat's name is "Bacon"


let mysteryMeat =Food()

// mysteryMeat's name is "[Unnamed]"


class RecipeIngredient:Food {

   var quantity: Int

   init(name: String, quantity:Int) {

       self.quantity = quantity

       super.init(name: name)

    }

    override convenience init(name:String) {

       self.init(name: name, quantity:1)

    }

}

//RecipeIngredient通过init(name:String)覆写了Food的所有设定构造器,所以RecipeIngredient自动继承了父类的所有便捷构造器



let oneMysteryItem =RecipeIngredient()

//{{name "[Unnamed]"} quantity 1}

let oneBacon =RecipeIngredient(name: "Bacon")

//{{name "[Bacon]"} quantity 1}

let sixEggs =RecipeIngredient(name: "Eggs", quantity: 6)

//{{name "[Eggs]"} quantity 6}



class ShoppingListItem:RecipeIngredient {

   var purchased = false

   var description: String {

       var output = "\(quantity) x\(name)"

        output +=purchased ? " ✔" :" ✘"

       return output

    }

}



//ShoppingListItem新增的属性有默认值,并且没有定义任何构造器,所以它将自动继承父类所有的设定构造器和便捷构造器。





var breakfastList = [

    ShoppingListItem(),

    ShoppingListItem(name:"Bacon"),

   ShoppingListItem(name: "Eggs", quantity: 6),

]

breakfastList[0].name ="Orange juice"

breakfastList[0].purchased =true

for item inbreakfastList {

   println(item.description)

}

// 1 x Orange juice ✔

// 1 x Bacon ✘

// 6 x Eggs ✘


14.6Failable Initializers


有时一些类,结构体,枚举的初始化过程会失败,有可能是初始化参数不对,初始化所需要的外部资源缺少了,等等。


为了处理这种情况,需要定义failable initializer (可失败构造器)。可失败构造器需要在init后面加?


注意:不能定义相同类型和名称的可失败和不可失败的构造器


可失败构造器返回可选类型,当构造失败则return nil


注意:严格来说,构造器是不返回值的,它的角色是确保实例被正确初始化。虽然构造失败时我们return nil,但成功时,我们并不return


struct Animal {

   let species: String

   init?(species: String) {

       if species.isEmpty {return nil }

       self.species = species

    }

}


let someCreature =Animal(species: "Giraffe")

// someCreature is of type Animal?, not Animal


iflet giraffe = someCreature {

    println("An animal was initialized with a species of\(giraffe.species)")

}

// prints "An animal was initialized with a species of Giraffe"


//如果我们传个空字符串进去,则将构造失败

let anonymousCreature =Animal(species: "")

// anonymousCreature is of type Animal?, not Animal


if anonymousCreature ==nil {

    println("The anonymous creature could not be initialized")

}

// prints "The anonymous creature could not be initialized"



14.6.1Failable Initializers for Enumerations


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

        }

    }

}


let fahrenheitUnit =TemperatureUnit(symbol: "F")

if fahrenheitUnit !=nil {

    println("This is a known temperature unit, so initialization succeeded.")

}

// prints "This is a known temperature unit, so initialization succeeded."


let unknownUnit =TemperatureUnit(symbol: "X")

if unknownUnit ==nil {

    println("This is an unknown temperature unit, so initialization failed.")

}

// prints "This is an unknown temperature unit, so initialization failed."


14.6.2Failable Initializers for Enumerations with Raw Values


enum TemperatureUnit:Character {

   case Kelvin = "K", Celsius ="C", Fahrenheit = "F"

}


let fahrenheitUnit =TemperatureUnit(rawValue: "F")

if fahrenheitUnit !=nil {

    println("This is a known temperature unit, so initialization succeeded.")

}

// prints "This is a known temperature unit, so initialization succeeded."


let unknownUnit =TemperatureUnit(rawValue: "X")

if unknownUnit ==nil {

    println("This is an unknown temperature unit, so initialization failed.")

}

// prints "This is an unknown temperature unit, so initialization failed."



14.6.3Failable Initializers for Classes


值类型的可失败构造器可随时触发失败

类类型的可失败构造器只有在所有该类引进的存储属性都已赋值,并且构造代理也已执行完之后才能触发


class Product {

   let name: String!

   init?(name: String) {

       if name.isEmpty {return nil }

       self.name = name

    }

}


//常量属性name不能赋空值。为了满足这个要求,使用可失败构造器确保这个属性非空

//然而Product是累类,不是结构体,这就意味着在触发构造失败之前name属性是有初始值的。

//name属性是展开的可选类型。因为是可选类型,他的默认值是nil,这意味着Product的所有属性都是有初始值的。所以可失败构造器就可以在一开始就判断入参是否为空,并触发构造失败。

//因为name是个常量,所以但构造成功,就可确定那么属性时非空的,就不需要在访问时再判断是否为空


iflet bowTie = Product(name:"bow tie") {

    // no need to check if bowTie.name == nil

    println("The product's name is\(bowTie.name)")

}

// prints "The product's name is bow tie"


14.6.4Propagation of Initialization Failure


构造失败传染


一个类,结构体,枚举的可失败构造器可代理同一个类,结构体,枚举的可失败构造器。

类似,子类的可失败构造器可调用父类的可失败构造器


不管哪一种情况,当你调用的可失败构造器失败了,那么整个初始化过程立即结束。


注意:可失败构造器也可以调用不是失败的构造器


class CartItem:Product {

   let quantity: Int!

   init?(name: String, quantity:Int) {

       println("before name=\(name)  quantity=\(quantity)")

       super.init(name: name)

       println("after name=\(name)  quantity=\(quantity)")

       if quantity < 1 {return nil }

       self.quantity = quantity

    }

}


iflet twoSocks = CartItem(name:"sock", quantity: 2) {

   println("Item: \(twoSocks.name), quantity:\(twoSocks.quantity)")

}

// prints "Item: sock, quantity: 2"


iflet zeroShirts = CartItem(name:"shirt", quantity: 0) {

   println("Item: \(zeroShirts.name), quantity:\(zeroShirts.quantity)")

}else {

    println("Unable to initialize zero shirts")

}

// prints "Unable to initialize zero shirts"


iflet oneUnnamed = CartItem(name:"", quantity: 1) {

   println("Item: \(oneUnnamed.name), quantity:\(oneUnnamed.quantity)")

}else {

    println("Unable to initialize one unnamed product")

}

// prints "Unable to initialize one unnamed product"


打印


before name=sock  quantity=2

after name=sock  quantity=2

Item: sock, quantity: 2

before name=shirt  quantity=0

after name=shirt  quantity=0

Unable to initialize zero shirts

before name=  quantity=1

Unable to initialize one unnamed product


在初始化oneUnnamed时,由于入参name为空,在super.init(name:name)时就失败了。整个初始化过程立即结束,后面的代码不再执行。所以打印的结果中没有相应的after日志。



14.6.5Overriding a Failable Initializer


可以覆写父类的可失败构造器

可以用不可失败的构造器来覆写父类的可失败构造器


这是你能够做到即使父类有可失败构造器,但子类却没有


请注意如果你覆写父类的可失败构造器,那么子类的构造器不能调用父类的可失败构造器。

不可失败的构造器永远不能调用可失败的构造器。


class Document {

   var name: String?

    // this initializer creates a document with a nil name value

   init() {}

    // this initializer creates a document with a non-empty name value

   init?(name: String) {

       if name.isEmpty {return nil }

       self.name = name

    }

}


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 用不可失败构造器覆写了父类的可失败构造器


14.6.6Implicitly Unwrapped Failable Initializers


可以通过init?来定义可失败构造器

也可以通过 init ! 来定义隐式展开可失败构造器


可以在init?里面调用init!,也可以用 init覆写  init? 


NOTE

You can also delegate from a nonfailing initializer to an implicitly unwrapped failing initializer. However, you will trigger an assertion if the implicitly unwrapped failing initializer returnsnil.


14.7Required Initializers


init之前写required修饰符来标明子类必须实现这个构造器


如果你希望孙类也实现这个构造器,那么子类那边也要写required


覆写required设定构造器时不需要写 override


如果继承下来的构造器能满足required 的要求,那么就不需要显示在定义


class SomeClass {

    required init() {

        // initializer implementation goes here

    }

}


class SomeSubclass:SomeClass {

    required init() {

        // subclass implementation of the required initializer goes here

    }

}


//遮掩定义没问题

//因为required init()被继承下来了

class SomeSubSubclass:SomeSubclass{

    

}


let cla = SomeClass() //SomeClass

let sub = SomeSubclass() //{SomeClass}

let subsub = SomeSubSubclass() //{{SomeClass}}


//这样定义编译不过:

//Issur  'required' initializer 'init()' must be provided by subclass of 'SomeSubclass'

class SomeSubSubclass1:SomeSubclass {

   init(name:String){

        

    }

}


//这样定义也编译不过:

//Issue  'required' modifier must be present on all overrides of a required initializer

class SomeSubSubclass2:SomeSubclass {

   init(){ 

    

    }

}


14.8Setting a Default Property Value with a Closure or Function


//可以使用闭包或全局函数来为属性提供默认值。当实例创建时,闭包或函数就会被调用,其返回值就是那个属性的默认值


class SomeClass {

   let someProperty: SomeType = {

        // create a default value for someProperty inside this closure

        // someValue must be of the same type as SomeType

       return someValue

        }()

}


//注意闭包后面跟着小括号,意思是立即执行,返回值赋值给属性。不加小括号代表把这个闭包赋值给那个属性。

//注意用闭包初始化属性时,对象还没初始化好。所以不能访问其他的属性值(即使他们有默认值),不能使用self,不能使用实例方法。


15Deinitialization

 

当一个类实例被释放时调用析构器,关键字deinit。只有类才有析构器


15.1How Deinitialization Works


当对象不再使用时,swift自动释放实例,swift通过arc来管理内存。一般情况下你不需要手动清理内存。但是如果有操作资源,比如打开文件,写入数据等,那么你就需要在对象释放前手动关闭资源。


类定义时最多只能有一个析构器。析构器不带参数,不带括号


deinit {

    // perform the deinitialization

}


析构函数在对象释放时自动被调用。

不允许手动调用析构函数。

弗雷德饿析构函数会被子类继承,并且在子类析构函数结束时自动被调用。

不管你有没有提供析构函数,父类的析构函数都是会被调用的。


15.2Deinitializers in Action


//只能有一家银行,并且金币不能超过10000

struct Bank {

   static var coinsInBank =10_000

   static func vendCoins(var numberOfCoinsToVend:Int) -> Int {

        numberOfCoinsToVend =min(numberOfCoinsToVend, coinsInBank)

        coinsInBank -= numberOfCoinsToVend

       return numberOfCoinsToVend

    }

   static func receiveCoins(coins:Int) {

        coinsInBank += coins

    }

}


//玩家

class Player {

   var coinsInPurse: Int

   init(coins: Int) {

       coinsInPurse = Bank.vendCoins(coins)

    }

   func winCoins(coins: Int) {

       coinsInPurse += Bank.vendCoins(coins)

    }

    //析构时,金币还给银行

   deinit {

        Bank.receiveCoins(coinsInPurse)

    }

}


var playerOne:Player? = Player(coins:100)

println("A new player has joined the game with\(playerOne!.coinsInPurse) coins")

// prints "A new player has joined the game with 100 coins"

println("There are now\(Bank.coinsInBank) coins left in the bank")

// prints "There are now 9900 coins left in the bank"



playerOne!.winCoins(2_000)

println("PlayerOne won 2000 coins & now has\(playerOne!.coinsInPurse) coins")

// prints "PlayerOne won 2000 coins & now has 2100 coins"

println("The bank now only has\(Bank.coinsInBank) coins left")

// prints "The bank now only has 7900 coins left"


playerOne = nil

println("PlayerOne has left the game")

// prints "PlayerOne has left the game"

println("The bank now has\(Bank.coinsInBank) coins")

// prints "The bank now has 10000 coins"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值