Swift 拾遺(2)

枚舉

  • 可以提供關聯值
  • 定義計算屬性
  • 實例方法 構造函數
  • 遵循協議

定義:

 enum BarStyle {
 case  BarRed,BarBlue
}
// 設置關聯值
enum BarTitle {
case BarTitleShow (Bool)
case BarStyle(Dictionary)
...

}

關聯值可以是不同的類型

  • 原始值
    枚舉成員可以設置默認值,且這些case 的原始值得類型必須是相同的
enum ASCIIControlCharacter:Character {
case tab = '\t'
case line = ''
case whiteSpace = ' '
...
}

原始值的隱式賦值
如果原始值得類型是String 或者整數,不要顯示的為每一個枚舉成員設置原始值,Swift 將自動設置。整型自動加一,字符串默認是枚舉成員的String 名稱

結構體和類

  • 類是引用類型,結構體是值類型
  • 類允許繼承,結構體不允許
  • 類的類型轉換允許在運行時進行檢查
  • 結構體有逐一成員構造函數,類沒有

恒等運算符 === !== 用于判斷兩個變量是否引用同一個類實例

屬性

  1. 存儲屬性
    ** 延遲存儲屬性聲明必須使用var ,因為屬性的初始值有可能在實例的構造函數完成之后才會得到,只有在第一次訪問的時候才會被創建,而常量的初始值在構造函數完成之前就必須要有初始值**

可選類型

使用 ? 或者 ! 修飾的變量,默認值均為 nil ,兩者的區別只是需不需要隱式解析

循環引用

遇到循環引用就會想到 weak ,unowned,兩者有何區別呢?

  1. 計算屬性
    不存儲值,只提供 gettersetter方法
struct React {
// 這里容易和 閉包賦值產生混淆,var center :Point = {}();這是閉包賦值
 var center:Point {
   get { ... }
   set { newValue ... }
  }
}

只有 getter 沒有 setter 的計算屬性就是只讀計算屬性

3 . 屬性觀察器 willSet , didSet

父類的屬性在子類的構造器中被賦值時,它在父類中的willSet ,didSet 觀察器會被調用,隨后才會調用子類的觀察器。在副類的初始化函數調用之前,子類給屬性賦值,觀察器不會被觸發

class StepCounter { 
var totalSteps: Int = 0 {
     willSet(newTotalSteps) {
             print("About to set totalSteps to \(newTotalSteps)")
   }
 didSet {
       if totalSteps > oldValue {
       print("Added \(totalSteps - oldValue) steps")
      }
   }
  }
}

4 . 類型屬性
使用關鍵字 static 來定義類型屬性。在為類定義計算型類型屬性時,可以改用關鍵字class 來支持子類對父 類的實現進行重寫。

struct SomeStructure {
    static var storedTypeProperty = "Some value." 
    static var computedTypeProperty: Int {
         return 1 
    }
 }

再看一個完整的例子

struct AudioChannel {
  static let thresholdLevel = 10 
  static var maxInputLevelForAllChannels = 0 
   var currentLevel: Int = 0 { 
       didSet { 
         if currentLevel > AudioChannel.thresholdLevel { 
// 將當前音量限制在閥值之內
            currentLevel = AudioChannel.thresholdLevel
        } 
       if currentLevel > AudioChannel.maxInputLevelForAllChannels         { // 存儲當前音量作為新的最大輸入音量 AudioChannel.maxInputLevelForAllChannels = currentLevel
 }}
}}

方法

在方法的 func 關鍵字之前加上關鍵字static ,來指定類型方法。類還可以用關鍵字 class來允許子類重寫父類的方法實現。

繼承

  • 值類型不支持繼承
    重寫繼承過來的實力屬性或者類型屬性,提供自己在定制的setter getter 方法,或者添加屬性觀察器,觀察重寫的屬性值得改變

子類并不知道繼承來的屬性是存儲還是計算,只知道一個名字和類型,所以在重寫的時候必須指出類型和名字。
子類可以將一個只讀重寫為讀寫,但是反之不可。

你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置 的,所以,為它們提供 willSet 或 didSet 實現是不恰當。 此外還要注意,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,并且你已 經為那個屬性提供了定制的 setter,那么你在 setter 中就可以觀察到任何值變化了。

構造函數

  • 如果結構體或者類的所有屬性都有默認值,同時沒有自定義的構造函數,Swift會提供一個默認的構造函數
    如果提供了自定義的構造函數,系統就不再提供默認的

假如你希望默認構造器、逐一成員構造器以及你自己的自定義構造器都能用來創建實例,可以將自定義的構造器 寫到擴展( extension )中,而不是寫在值類型的原始定義中。

  • 指定構造函數必須向上調用構造函數(調用父類)
  • 便利構造函數必須橫向調用其他構造函數


    initializerDelegation02_2x.png

兩段式構造:
1 . 初始化存儲屬性
2 . 實例準備使用之前進一步定制存儲屬性的值

類型檢查的四個階段:

1 . 指定構造函數必須保證目前所在類引入的所有屬性先初始化,再調用父類的構造函數
2 . 調用父類構造函數后再為繼承的屬性賦新值
3 . 便利構造函數必須調用同一類的其他構造函數,再為任意屬性賦值
4 . 構造函數在第一階段未完成之前不能使用任何實例方法,實例屬性以及self

構造階段
階段1 :
  • A designated or convenience initializer is called on a class.
    Memory for a new instance of that class is allocated. The memory is not yet initialized.
  • A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
  • The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
  • This continues up the class inheritance chain until the top of the chain is reached.

Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

階段2:
  • Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self
    and can modify its properties, call its instance methods, and so on.
  • Finally, any convenience initializers in the chain have the option to customize the instance and to work with self

Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:


image: ../Art/twoPhaseInitialization01_2x.png
image: ../Art/twoPhaseInitialization01_2x.png

In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.
The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.
The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.
As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.
Here’s how phase 2 looks for the same initialization call:


image: ../Art/twoPhaseInitialization02_2x.png
image: ../Art/twoPhaseInitialization02_2x.png

The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).
Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).

Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.

  • 構造函數的繼承和重寫
    與OC 不同,Swift 中的子類默認(符合特定條件是ok的)不會繼承父類的構造函數 (不代表不能super 調用父類構造函數哦哦??)

當你重寫一個父類的指定構造器時,你總是需要寫override 修飾符,即使你的子類將父類的指定構造器重寫為了便利構造函數。

如果寫了一個與父類的便利構造函數相匹配的子類構造函數,由于子類不能直接調用父類的便利構造函數,因此嚴格意義上來說,你寫的子類并未對父類構造函數提供重寫行為,所以不需要添加override 修飾符

  • 繼承父類構造函數的條件
    1 . 子類沒有定義任何指定構造函數,他將自動繼承所有父類的指定構造函數
    2 . 如果子類提供了所有父類指定構造函數--無論是=通過規則1 繼承過來的還是提供了自定義實現,子類將自動繼承父類的所有便利構造函數(另外子類可以將父類的指定構造函數實現為便利構造函數)

  • 可失敗構造函數

 init? (...) {
  if notConfirmCondition {return nil }
...
}
  • 必要構造函數 -- 必須實現的
required init () {
  ...
}

通過閉包或者函數設置屬性的默認值

如果某個存儲屬性的默認值需要一些定制或者設置,可以使用閉包或者全局函數提供定制,每當實例創建時,對應的閉包或者函數就會被調用

使用閉包
class SomeClass {
  let someProperty :someType = {
    ...
   return someValue
 }()
// 注意這里的`()` 如果忽略,則將閉包賦值給屬性
注意和計算屬性getter 方法區分開
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容