Swift4 基礎部分: Initialization

本文是學習《The Swift Programming Language》整理的相關隨筆,基本的語法不作介紹,主要介紹Swift中的一些特性或者與OC差異點。

系列文章:

初始化器(Initializers)

  • 直接查看基本的使用例子:

例子:

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

var f = Fahrenheit();
print("The default temperature is \(f.temperature)° Fahrenheit")

執行結果:

The default temperature is 32.0° Fahrenheit

自定義初始化(Customizing Initialization)

例子:

    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)
print("The default temperature is \(boilingPointOfWater.temperatureInCelsius)° Fahrenheit")
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
print("The default temperature is \(freezingPointOfWater.temperatureInCelsius)° Fahrenheit")

執行結果:

The default temperature is 100.0° Fahrenheit
The default temperature is 0.0° Fahrenheit

參數名稱和參數標簽(Parameter Names and Argument Labels)

例子:

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)
print("magenta red:\(magenta.red) green:\(magenta.green) blue:\(magenta.blue)");
print("halfGray red:\(halfGray.red) green:\(halfGray.green) blue:\(halfGray.blue)");

執行結果:

magenta red:1.0 green:0.0 blue:1.0
halfGray red:0.5 green:0.5 blue:0.5

不需要參數標簽的初始化器(Initializer Parameters Without Argument Labels)

直接看例子:

struct Celsius{
    var temperatureInCelsius:Double;
    init(fromFahrenheit fahrenheit:Double){
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8;
    }
    
    init(fromKelvin kelvin:Double){
        temperatureInCelsius = kelvin - 273.15;
    }
    
    // 此處就是具體的體現
    init(_ celsius:Double){
        temperatureInCelsius = celsius;
    }
}
let bodyTemperature = Celsius(37.0);
print(bodyTemperature.temperatureInCelsius);

執行結果:

37.0

可選屬性類型(Optional Property Types)

If your custom type has a stored property that is 
logically allowed to have “no value”—perhaps because its 
value cannot be set during initialization, or because it 
is allowed to have “no value” at some later point—declare 
the property with an optional type. 
  • 如果你定制的類型是允許取值為空的存儲型屬性--不管是因為它無法在初始化時賦值,還是因為它可以在之后某個時間點可以賦值為空--你都需要將它定義為可選類型。
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."

執行結果:

Do you like cheese?

構造過程中常量屬性的修改(Assigning Constant Properties During Initialization)

  • 只要在構造過程結束前常量的值能確定,你可以在構造過程中的任意時間點修改常量屬性的值。
  • 對某個類實例來說,它的常量屬性只能在定義它的類的構造過程中修改;不能在子類中修改。

例子:

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

class DetailSurveyQuestion:SurveyQuestion{
    
    init(content content: String){
        self.text = content;
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// 輸出 "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"

執行結果:編譯錯誤驗證上述結論2

Playground execution failed: error: MyPlayground.playground:900:19: error: cannot assign to property: 'text' is a 'let' constant
        self.text = content;
        ~~~~~~~~~ ^

去掉子類的實現,執行結果:

How about beets?

默認構造器(Default Initializers)

Swift provides a default initializer for any structure or 
class that provides default values for all of its 
properties and does not provide at least one initializer 
itself.
  • Swift 將為所有屬性已提供默認值的且自身沒有定義任何構造器的結構體或基類,提供一個默認的構造器。

例子:


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

結構體的逐一成員構造器(Memberwise Initializers for Structure Types)

Structure types automatically receive a memberwise 
initializer if they do not define any of their own custom 
initializers. Unlike a default initializer, the structure 
receives a memberwise initializer even if it has stored 
properties that do not have default values.
  • 結構體類型可以自動接收逐一成員構造器,如果他們沒有定義任何的構造器。

例子:

struct Size {
    var width = 0.0, height = 0.0;
}
let size = Size(width:2.0, height:2.0);
print("size \(size.width) \(size.height)");

執行結果:

size 2.0 2.0

值類型的構造器代理(Initializer Delegation for Value Types)

Initializers can call other initializers to perform part 
of an instance’s initialization. This process, known as 
initializer 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);
    }
}

let originReact = Rect(origin:Point(x:2.0,y:2.0),size:Size(width:5.0,height:5.0));
print("point:\(originReact.origin) size:\(originReact.size)");

執行結果:

point:Point(x: 2.0, y: 2.0) size:Size(width: 5.0, height: 5.0)

類的繼承與構造過程(Class Inheritance and Initialization)

Swift defines two kinds of initializers for class types to 
help ensure all stored properties receive an initial 
value. These are known as designated initializers and 
convenience initializers.
  • Swift 提供了兩種類型的類構造器來確保所有類實例中存儲型屬性都能獲得初始值,它們分別是指定構造器和便利構造器。

指定構造器和便利構造器的語法(Syntax for Designated and Convenience Initializers)

指定構造器語法:

init(parameters) {
    statements
}

便利構造器語法:

convenience init(parameters) {
    statements
} 

類的構造器代理(Initializer Delegation for Class Types)

基本規則:

Designated initializers must always delegate up.
Convenience initializers must always delegate across.
  • 指定構造器必須總是向上代理。
  • 便利構造器必須總是橫向代理。

構造器的繼承與重載(Initializer Inheritance and Overriding)

When you write a subclass initializer that matches a 
superclass designated initializer, you are effectively 
providing an override of that designated initializer. 
Therefore, you must write the override modifier before the 
subclass’s initializer definition.
  • 可以在子類中通過關鍵字override重載父類的構造器方法。

例子:

class Vehicle {
    var numberOfWheels = 0;
    var description:String {
        return "\(numberOfWheels) wheel(s)";
    }
}

class Bicycle:Vehicle {
    override init() {
        super.init();
        numberOfWheels = 2;
    }
}

let bicycle = Bicycle();
print("Bicycle: \(bicycle.description)");

執行結果:

Bicycle: 2 wheel(s)

指定構造器和便利構造器實戰(Designated and Convenience Initializers in Action)

直接看一下指定構造器與便利構造器的實際應用的例子:

class Food {
    var name: String;
    
    init(name: String) {
        self.name = name;
    }
    
    convenience init() {
        self.init(name: "[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);
    }
}

class ShoppingListItem: RecipeIngredient {
    var purchased = false;
    
    var description: String {
        var output = "\(quantity) x \(name)";
        output += purchased ? " ?" : " ?";
        return output;
    }
}

var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6),
];
breakfastList[0].name = "Orange juice";
breakfastList[0].purchased = true;
for item in breakfastList {
    print(item.description);
}

執行結果:

1 x Orange juice ?
1 x Bacon ?
6 x Eggs ?

可失敗的構造器(Failable Initializers)

It is sometimes useful to define a class, structure, or 
enumeration for which initialization can fail. This 
failure might be triggered by invalid initialization 
parameter values, the absence of a required external 
resource, or some other condition that prevents 
initialization from succeeding.
  • 如果一個類,結構體或枚舉類型的對象,在構造自身的過程中有可能失敗,則為其定義一個可失敗構造器,是非常有用的。這類錯誤可能是參數或者確實外部資源等。

例子:

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)");
}

執行結果:

An animal was initialized with a species of Giraffe

枚舉類型的可失敗構造器(Failable 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 {
    print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

執行結果:

This is a defined temperature unit, so initialization succeeded.
This is not a defined temperature unit, so initialization failed.

帶原始值的枚舉類型的可失敗構造器(Failable Initializers for Enumerations with Raw Values)

例子:

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.")
}

let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

執行結果:

This is a defined temperature unit, so initialization succeeded.
This is not a defined temperature unit, so initialization failed.

構造失敗的傳遞(Propagation of Initialization Failure)

A failable initializer of a class, structure, or 
enumeration can delegate across to another failable 
initializer from the same class, structure, or 
enumeration. Similarly, a subclass failable initializer 
can delegate up to a superclass failable initializer.
  • 可失敗構造器在同一類,結構體和枚舉中橫向代理其他的可失敗構造器。類似的,子類的可失敗構造器也能向上代理基類的可失敗構造器。

例子:

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);
    }
}

if let twoSocks = CartItem(name: "sock", quantity: 2) {
    print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)");
}

if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)");
} else {
    print("Unable to initialize zero shirts");
}

if let oneUnnamed = CartItem(name: "", quantity: 1) {
    print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)");
} else {
    print("Unable to initialize one unnamed product");
}

執行結果:

Item: sock, quantity: 2
Unable to initialize zero shirts
Unable to initialize one unnamed product

覆寫父類的可失敗的構造器(Overriding a Failable Initializer)

You can override a superclass failable initializer in a subclass, just like any other initializer.
  • 就如同其它構造器一樣,你也可以用子類的可失敗構造器覆蓋基類的可失敗構造器。

例子:

class Document {
    var name: String?;

    init() {};

    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;
        }
    }
}

必須存在的構造器(Required Initializers)

Write the required modifier before the definition of a 
class initializer to indicate that every subclass of the 
class must implement that initializer: 

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}

通過閉包和函數來設置屬性的默認值(Setting a Default Property Value with a Closure or Function)

If a stored property’s default value requires some 
customization or setup, you can use a closure or global 
function to provide a customized default value for that 
property. 
  • 如果某個存儲型屬性的默認值需要特別的定制或準備,你就可以使用閉包或全局函數來為其屬性提供定制的默認值。

例子:


struct Chessboard {
    let boardColors:[Bool] = {
        var temporaryBoard = [Bool]();
        var isBlack = false;
        for i in 1...8 {
            for j in 1...8 {
                temporaryBoard.append(isBlack);
                isBlack = !isBlack;
            }
        }
        isBlack = !isBlack;
        return temporaryBoard;
    }()
    
    func squareIsBlackAt(row:Int,column:Int) -> Bool{
        return boardColors[row * 8 + column];
    }
}

let board = Chessboard();
print(board.squareIsBlackAt(row: 0, column: 1));
print(board.squareIsBlackAt(row: 7, column: 7));

執行結果:

true
true
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容