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的差異
Value must be known at compile-time, const birthday = "2008/12/25"
Can’t be changed after initialized.
Value must be known at run-time, final birthday = getBirthDateFromDB()
Can’t be changed after initialized.
任何class和struct都會有至少一個initializer(沒特別寫就是default)。default initializer創造新的instance並對裡面的property 賦值。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
如果沒有寫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"
Xcode > 選取class
Refactor > Generate Memberwise Initializer
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裡。
沒特別寫有自動產生default 和 memberwise兩種init 方法
只剩custom init
把custom init寫在extension,集滿三種init
Designated initializers 是 class主要的 init方法,通常情況只會有一個。但也有可能從superclass繼承來,就會有多個。任何class都必須最少有一個。
Convenience initializers 是 class 次要的 init方法,可以用他來呼叫Designated initializers。不一定要有。
2. convenience initializer 只能呼叫同一個層級的initializer。
3. convenience initializer最終必須呼叫designated initializer。
A simple way to remember this is:
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的記憶體也準備完成。第一階段結束。
第二階段
*從上往下,每個designated initializer都有自訂instance的空間。這個時機點可以存取self、修改property、呼叫instance method。
*最後!!換convenience initializer自訂instance&存取self。
繼承Initialization
只能繼承designated initializer,不能繼承convenience initializer。如果subclass的convenience initializer跟superclass一樣,那這個subclass就永遠無法直接call到superclass的convenience 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 讓 subclass都必須實行這個init。有required就不必再寫override。
class SomeClass {
required init() {
// initializer implementation goes here
}
}
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的差別