Swift 使用class關鍵字宣告類別 (Class),使用類別名稱加上參數列表()產生物件 (Object)。
class Rect {        //宣告類別
    var width: Int = 10
    var height: Int = 10
}
var rect = Rect()   //產生物件
rect.width          //return: 10
width與height為物件屬性,專屬於特定物件的變數或常數,存取時在物件名稱後加上.與屬性名稱。
基本上,物件初始化 (Initialization) 就是使用初始化函式 (initializer) 設定類別屬性的過程。 初始化函式使用init關鍵字當作函式名稱,除了不用func關鍵字與回傳值,定義方式與 function 類似。若類別沒有宣告初始化函式,則物件使用預設初始化函式init(){}。
物件屬性沒在初始化過程中賦值將導致編譯錯誤。
class Rect {        //error: class 'Rect' has no initializers
    var width: Int  //note: stored property 'width' without initial value prevents synthesized initializers
    var height: Int //note: stored property 'height' without initial value prevents synthesized initializers
}
物件初始化有三種方式:
初始化方式一:宣告變數(常數)給初始值。
class Rect {
    var width: Int = 10
    var height: Int = 10
}
var rect = Rect()   //return: {width 10 height 10}
初始化方式二:宣告變數(常數)為 optional。
class Rect {
    var width: Int?
    var height: Int?
}
var rect = Rect()   //return: {nil nil}
rect.width = 10     //return: {{Some 10} nil}
rect.height = 10    //return: {{Some 10} {Some 10}}
初始化方式三:在初始化函式中設定初始值,初始值可以是預設值或透過參數傳遞。
class Rect {
    var width: Int
    var height: Int
    init() {
        width = 10
        height = 10
    }
}
var rect = Rect()   //return: {width 10 height 10}
class Rect {
    var width: Int
    var height: Int
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}
var rect1 = Rect(width: 10, height: 10) //return: {width 10 height 10}
一旦自行宣告初始化函式,則預設初始化函式init(){}會失效。
class Rect {
    var width: Int
    var height: Int
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}
var rect1 = Rect(width: 10, height: 10) //return: {width 10 height 10}
var rect2 = Rect()                      //error: missing argument for parameter 'width' in call
可宣告多個初始化函式。
class Rect {
    var width: Int
    var height: Int
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
    init() {
        width = 10
        height = 10
    }
}
var rect1 = Rect(width: 10, height: 10) //return: {width 10 height 10}
var rect2 = Rect()                      //return: {width 10 height 10}
物件初始化不一定都能成功,若有可能失敗則使用初始化函式init?回傳nil。
class Rect {
    var width: Int      //must be larger than 0
    var height: Int     //must be larger than 0
    init?(width: Int, height: Int) {
        self.width = width
        self.height = height
        if width <= 0 || height <= 0 {
            return nil
        }
    }
}
var rect1 = Rect(width: 10, height: 10)     //return: {width 10 height 10}
var rect2 = Rect(width: 10, height: 0)      //return: nil
物件屬性一般為 stored property,設定與讀取直接作用在屬性本身。
class Rect {
    var width: Int = 10
    var height: Int = 10
}
var rect = Rect()   //return: {width 10 height 10}
rect.width = 20     //return: {width 20 height 10}
rect.width          //return: 20
物件屬性也可以透過計算得到,稱之為 computed property。宣告方式如下範例,使用get{}定義讀取的方式,使用set{}定義設定方式。set{}可不定義,若沒有設定則變數唯讀,而get{}必須定義。computed property 不可宣告為常數。
class Rect {
    var width: Int = 10
    var height: Int = 10
    var x: Int = 0
    var y: Int = 0
    var centerX: Int {
        get {
            return x + width/2
        }
        set {
            x = newValue - width/2
        }
    }
    var centerY: Int {
        get {
            return x + height/2
        }
        set {
            y = newValue - height/2
        }
    }
}
var rect = Rect()   //return: {width 10 height 10 x 0 y 0}
rect.centerX        //return: 5
rect.centerY        //return: 5
rect.centerX = 10   //return: {width 10 height 10 x 5 y 0}
rect.centerY = 10   //return: {width 10 height 10 x 5 y 5}
除了 stored property 與 computed property ,使用 property observer 可以監控變數的存取狀況,使用willSet{}配合newValue得知變數將被設定的新值,使用didSet{}配合oldValue得知被取代的變數舊值。
例如下例,按照常理rect長寬不可小於等於零,若width被設為0則提出警告並放棄當次設定。
class Rect {
    var width: Int = 10 {   //larger than 0
        willSet {
            if newValue <= 0 {
                println("illegal value")
            }
        }
        didSet {
            if width <= 0 {
                width = oldValue
            }
        }
    }
    var height: Int = 10
}
var rect = Rect()   //return: {width 10 height 10}
rect.width = -1     //return: {width 10 height 10}, output: illegal value
rect.width = 20     //return: {width 20 height 10}
物件屬性使用lazy關鍵字延後屬性初始化的時間。以下範例,呼叫Rect()並不會對position賦值,而是直到存取變數的時候才會真的初始化Point()。
class Point {
    var x: Int = 0
    var y: Int = 0
    init() {
        println("Point.init()")
    }
}
class Rect {
    var width: Int = 10
    var height: Int = 10
    lazy var position = Point()
    init() {
        println("Rect.init()")
    }
}
var rect = Rect()   //return: {width 10 height 10 nil}
                    //output: Rect.init()
rect.position       //return: {x 0 y 0}
                    //output: Point.init()
rect                //return: {width 10 height 10 {{x 0 y 0}}}
物件方法定義方式與 function 一樣。呼叫方式則是物件名稱後加上.連接方法名稱。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
var rect = Rect()
rect.info()     //return: "10x10"
物件變數可以宣告為 optional,定義時類別名稱後面加上?,取值時透過!強制解開包裝。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
var rect: Rect? = Rect()
rect!.info()    //return: "10x10"
rect = nil
rect!.info()    //fatal error: unexpectedly found nil while unwrapping an Optional value
使用!解開包裝取值需要判斷 optaional 是否為nil,改用?取值過程變成 optional chaining,只要取值過程遇到nil,則回傳nil,最終取得的值型別為 optional。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
var rect: Rect? = Rect()
rect?.info()    //return: "10x10" (type: String?)
rect = nil
rect?.info()    //return: nil (do nothing)
繼承像是什麼事都不做,就能平白享用父母留下來的財產。繼承方式為類別名稱加上:,然後接著要繼承類別的名稱。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
class CoolRect: Rect {
    // do nothing
}
var rect = CoolRect()
rect.info()     //return: "10x10"
Swift 不允許多重繼承。
class BaseA { }
class BaseB { }
class Subclass: BaseA, BaseB {  //error: multiple inheritance from classes 'BaseA' and 'BaseB'
}
除了繼承父類別的屬性與方法,子類別也可以建立自己的。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
class CoolRect: Rect {
    var color: String = "red"
    func desc() -> String {
        return "I'm \(color)"
    }
}
var rect = CoolRect()
rect.info()     //return: "10x10"
rect.desc()     //"I'm red"
初始化函式有兩種:
convenience關鍵字並將某些參數定為預設值,然後呼叫同層的 designaed initializer 或 convenience initializer。class Person {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    convenience init(name: String) {
        self.init(name: name, age: 1)
    }
}
var father = Person(name: "Jack", age: 30)  //return: {name "Jack" age 30}
var baby = Person(name: "Jason")            //return: {name "Jason" age 1}
Designated Initializer 與 Convenience Initializer 的關係有三個規則。

Designated initializer 的繼承。
class Base {
    var foo: Bool
    init(foo: Bool) {
        self.foo = foo
    }
}
class Sub: Base  {
}
var obj = Sub(foo: true)
Designated initializer 的覆寫。
class Base {
    var foo: Bool
    init(foo: Bool) {
        self.foo = foo
    }
}
class Sub: Base {
    var bar: Bool
    init(bar: Bool) {
        self.bar = bar
        super.init(foo: true)
    }
}
var x = Sub(bar: true)
var y = Sub(foo: true) // error: incorrect argument label in call (have 'foo:', expected 'bar:')
一旦子類別宣告 designated initializer,就失去繼承父類別 designated initializer 的能力。
Convenience initializer 的繼承。
class Base {
    var foo: Bool
    var bar: Bool
    init(foo: Bool, bar: Bool) {
        self.foo = foo
        self.bar = bar
    }
    init(foo: Bool) {
        self.foo = foo
        self.bar = true
    }
    convenience init() {
        self.init(foo: true, bar: true)
    }
}
class Sub: Base {
}
var x = Sub()                       //return: {{foo true bar true}}
var y = Sub(foo: true)              //return: {{foo true bar true}}
var z = Sub(foo: true, bar: true)   //return: {{foo true bar true}}
必須覆寫所有父類別所有 Designated Initializer 才能繼承 Convenience Initializer。
class Base {
    var foo: Bool
    var bar: Bool
    init(foo: Bool, bar: Bool) {
        self.foo = foo
        self.bar = bar
    }
    init(foo: Bool) {
        self.foo = foo
        self.bar = true
    }
    convenience init() {
        self.init(foo: true, bar: true)
    }
}
class Sub: Base {
    override init(foo: Bool, bar: Bool) {
        super.init(foo: !foo, bar: !bar)
    }
    override init(foo: Bool) {
        super.init(foo: !foo)
    }
}
var x = Sub()                       //return: {{foo false bar false}}
var y = Sub(foo: true)              //return: {{foo false bar true}}
var z = Sub(foo: true, bar: true)   //return: {{foo false bar false}}
不完全覆寫父類別所有 Designated Initializer,無法繼承 Convenience Initializer。
class Base {
    var foo: Bool
    var bar: Bool
    init(foo: Bool, bar: Bool) {
        self.foo = foo
        self.bar = bar
    }
    init(foo: Bool) {
        self.foo = foo
        self.bar = true
    }
    convenience init() {
        self.init(foo: true, bar: true)
    }
}
class Sub: Base {
    override init(foo: Bool, bar: Bool) {
        super.init(foo: !foo, bar: !bar)
    }
}
var x = Sub()                       //error: missing argument for parameter 'foo' in call
var y = Sub(foo: true)              //error: missing argument for parameter 'bar' in call
var z = Sub(foo: true, bar: true)   //return: {{foo false bar false}}
透過required關鍵字強制子類別需實作特定 initializer。
class Base {
    var foo: Bool
    init() {
        self.foo = true
    }
}
class Sub: Base {
    init(foo: Bool) {
        super.init()
        self.foo = foo
    }
}
var x = Sub(foo: true)
var y = Sub()   //error: missing argument for parameter 'foo' in call
class Base {
    var foo: Bool
    required init() {
        self.foo = true
    }
}
class Sub: Base {
    init(foo: Bool) {
        super.init()
        self.foo = foo
    }
} //error: 'required' initializer 'init()' must be provided by subclass of 'Base'
class Base {
    var foo: Bool
    required init() {
        self.foo = true
    }
}
class Sub: Base {
    init(foo: Bool) {
        super.init()
        self.foo = !foo
    }
    required init() {
        super.init()
        self.foo = !self.foo
    }
}
var x = Sub()           //return: {{foo false}}
var y = Sub(foo: true)  //return: {{foo false}}
如果想擴充繼承而來的方法,要用override明確宣告覆寫父類別方法。
class Rect {
    var width: Int = 10
    var height: Int = 10
    func info() -> String {
        return "\(width)x\(height)"
    }
}
class CoolRect: Rect {
    var color: String = "Red"
    override func info() -> String {
        return "\(color) " + super.info()
    }
}
var rect = CoolRect()
rect.info()     //return: "Red 10x10"
如果想擴充繼承而來的屬性,要用override明確宣告覆寫父類別屬性,覆蓋的屬性為 computed property。
class Rect {
    var width: Int = 10
    var height: Int = 10
}
class Square: Rect {
    var length: Int {
        get {
            return super.width
        }
        set {
            super.width = newValue
            super.height = newValue
        }
    }
    override var width: Int {
        get { return length }
        set { length = newValue }
    }
    override var height: Int {
        get { return length }
        set { length = newValue }
    }
}
var square = Square()   //return: {{width 10 height 10}}
square.width = 20       //return: {{width 20 height 20}}
square.height = 30      //return: {{width 30 height 30}}
square.length = 40      //return: {{width 40 height 40}}
property observer 也可覆寫,覆寫後呼叫順序為 subclass willSet, base class willSet, base class didSet, subclass didSet。
class Rect {
    var width: Int = 10 {
        willSet { println("父類別 willSet") }
        didSet { println("父類別 didSet") }
    }
    var height: Int = 10
}
class CoolRect: Rect {
    override var width: Int {
        willSet { println("子類別 willSet") }
        didSet { println("子類別 didSet") }
    }
}
var rect = CoolRect()   //return: {{width 10 height 10}}
rect.width = 20         //return: {{width 20 height 10}}
                        //output: 子類別 willSet
                        //output: 父類別 willSet
                        //output: 父類別 didSet
                        //output: 子類別 didSet
以下為更複雜的覆寫範例。
import Darwin
class Square {
    var width: Double = 10
    var area: Double {
        get { println("Base get"); return width*width }
        set { println("Base set"); width = sqrt(newValue) }
    }
}
var square = Square()
square.width        //return: 10
square.area         //return: 100
square.area = 225   //return {width 15}
square.width        //return: 15
class TalkingSquare: Square {
    override var area: Double {
        willSet { println("Sub willSet") }
        didSet { println("Sub didSet") }
    }
}
square = TalkingSquare()
square.area = 16  // trigger sequence: Sub willSet -> Base set -> Sub didSet
square.width
class FakedSquare: TalkingSquare {
    override var area: Double {           // 完全取代附類別的area
        get { return M_PI*width*width }
        set { super.width = sqrt(newValue/M_PI) }
    }
}
var circle = FakedSquare()
circle.width        //return: 10
circle.area         //return: 314.1592653589793
circle.area = 100   //return: {{{width 5.641895835477563}}}
circle.width        //return: 5.641895835477563
Square.area是一個 computed property,透過width計算area的值。TalkingSquare.area覆寫Square.area,是一個 property observer,監控父類別屬性的變化。所以當 TalkingSquare.area變化時,除了觸動自己的 property observer,還會觸動父類別的 computed property。FakedSquare.area是一個 computed property,覆寫祖父類別 computed property。所以當 circle.area變化時,不會觸動父類別TalkingSquare的 property observer。使用final修飾屬性或方法,子類別則無法進一步改寫。
class Base {
    final var foo: Int = 0
}
class Sub {
    override var foo: Int { //error: property does not override any property from its superclass
        willSet {}
        didSet {}
    }
}
class Base {
    func foo() {}
}
class Sub {
    override func foo() {}  //error: method does not override any method from its superclass
}
使用final修飾整個類別,子類別無法繼續繼承。
final class Base {
}
class Sub: Base {   //error: inheritance from a final class 'Base'
}
使用static定義類別方法與屬性。
class Base {
    static let max = 3
    static var cnt = 0
    static func usage() { println("\(Base.cnt)/\(Base.max)") }
    init?() {
        if Base.cnt >= Base.max {
            return nil
        }
        Base.cnt += 1
    }
}
var a = Base()  //return: __lldb_expr_965.Base
var b = Base()  //return: __lldb_expr_965.Base
var c = Base()  //return: __lldb_expr_965.Base
var d = Base()  //return: nil
Base.usage()     //output: 3/3
Swift 提供三種存取權限:
internal: for module or app onlypublic: for anyoneprivate: for the same fileclass Girl {
    var name: String
    private var weight: Int
    init(name: String, weight: Int) {
        self.name = name
        self.weight = weight
    }
}
var mary = Girl(name: "Mary", weight: 50)
mary.weight //return: 50
在 Swift 中就算屬性宣告成
private,還是可以透過.取得資訊。