Swift-Initialization

2023/10/11閱讀時間約 11 分鐘

swift讀書筆記

Documentation
Edit descriptiondocs.swift.org

objective-c 的init 會return value,swift 不會。

所有的property都必須在Init()裡面設定初始值,或設定stored property,這種情況給值下不會觸發property observers。如果是初始化時沒辦法確定值,或可能為nil,型別可設成?

如果property值不會改變的話,建議直接給一個預設值,而不是在Init()裡設定。這種做法讓init()、deinit()都更簡潔。

特殊寫法:在init的時候assign constant property

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

概念近似於Flutter的final,由於實際上是常數,只能賦值一次。

Flutter final與const的差異

Constant

Value must be known at compile-time, const birthday = "2008/12/25"
Can’t be changed after initialized.

FINAL

Value must be known at run-time, final birthday = getBirthDateFromDB()
Can’t be changed after initialized.

Default Initializer

任何class和struct都會有至少一個initializer(沒特別寫就是default)。default initializer創造新的instance並對裡面的property 賦值。

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

Memberwise initializers for struct

如果沒有寫custom init的方法,struct會自動產生memberwise initializer。

struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

當呼叫memberwise initializer時,可以省略任何具有預設值的property的值。在上面的範例中,struct Size的屬性都有預設值。可以任意省略其中一個屬性或兩個屬性,並且initializer將使用省略的預設值。例如:height width

let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// Prints "0.0 2.0"


let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// Prints "0.0 0.0"

class 如何使用 memberwise initializer??

Xcode > 選取class

Refactor > Generate Memberwise Initializer

Initializer delegation

initializer可以呼叫其他的initializer來完成初始化,避免重複的code。

Value types比較單純,因為不支援繼承,所以只能呼叫自己提供的initializer,可以使用self.init來呼叫其他initializer。Class 的initialization 必須確保繼承來的property也完成初始化。

如果value types寫了custom initializer,就不能再存取default / memberwise initializer。這是避免有其他人用成自動產生的initializer就跳過在custom initializer的額外設定。如果想要三種都保留的話,可以把custom initializer寫在extension裡。

raw-image

沒特別寫有自動產生default 和 memberwise兩種init 方法

raw-image

只剩custom init

raw-image

把custom init寫在extension,集滿三種init

Class Initializations: Designated Initializers and Convenience Initializers

Designated initializers 是 class主要的 init方法,通常情況只會有一個。但也有可能從superclass繼承來,就會有多個。任何class都必須最少有一個。

Convenience initializers 是 class 次要的 init方法,可以用他來呼叫Designated initializers。不一定要有。

初始化代理流程

  1. designated initializer必須呼叫上層的designated initializer。

2. convenience initializer 只能呼叫同一個層級的initializer。

3. convenience initializer最終必須呼叫designated initializer。

A simple way to remember this is:

  • Designated initializers 方向往上
  • Convenience initializers 橫向發展
raw-image

兩階段初始化

class初始化有兩階段。第一階段,先把所有stored property賦予初始值。第二階段,每個class在形成instance前,可以自訂自己的property值。兩階段是確保不會有property在初始化結束前就被存取,同時也確保property的值不會意外的被其他init改掉。

意外的小知識:objc跟swift很像都是二階段初始化,差別是objc第一階段全部塞0或nil。

第一階段

*先呼叫class的initializer

*分配記憶體給等下的instance。這時候還在準備中。

*designated initializer確保所有的property都有值。分配給這部分的property的記憶體已準備好。

*designated initializer往上☝️交給上層的designated initializer重複剛剛的任務,一直到最上層的class。

*到頂端後,所有的property都已經有值,instance的記憶體也準備完成。第一階段結束。

raw-image

第二階段

*從上往下,每個designated initializer都有自訂instance的空間。這個時機點可以存取self、修改property、呼叫instance method。

*最後!!換convenience initializer自訂instance&存取self。

raw-image

繼承Initialization

只能繼承designated initializer,不能繼承convenience initializer。如果subclass的convenience initializer跟superclass一樣,那這個subclass就永遠無法直接call到superclass的convenience initializer。

Failable Initializer

init keyword (init?)

在失敗的時候創造一個optional的type。雖然寫法是return nil,成功的時候不必寫return 物件。因爲嚴格來說,initialization並沒有真正return東西。不能同時存在同參數的init和init?。

Creates an integer from the given floating-point value, if it can be represented exactly.

init?(exactly source: Double)
let wholeNumber: Double = 12345.0
let pi = 3.14159


if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"


let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int


if valueChanged == nil {
print("\(pi) conversion to Int doesn't maintain value")
}

Required Initializers

標示required 讓 subclass都必須實行這個init。有required就不必再寫override。

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

Setting Default property with functions and closures

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

最後會有一個()結尾,這是告訴swift要馬上執行這個closure。沒有寫的話只是單純把closure assign給這個變數。

不能在closure裡用self的東西,因為這時候instance裡的其他部分都還沒初始化完成。

跟computed Property的差別

raw-image
  1. 寫法不同,computed property只能用var,後面也不需跟著()
  2. computed property每次叫到都會重新計算結果,stored property只會初始化一次。
5會員
21內容數
紀錄iOS開發上遇到的問題或是一些流程筆記。主要都是Swift。
留言0
查看全部
發表第一個留言支持創作者!