Swift:類與物件

weixin_34320159發表於2016-04-21
Classes and Objects

github:Swift基礎例項
github:SwiftBasicTableView

  1. 類的建立
    使用關鍵字 class ,後面跟上類名稱,來建立一個類。
class Shape {
    var numberOfSides = 0
    let numberOfPoint = 3
    func simpleDescription() ->String {
        return "A shape with \(numberOfSides) sides"
    }
    func simplePoint(point:Int) ->Int {
        return point
    }
}

通過在類名後加圓括號() 來建立類的例項。用點語法(dot syntax),也就是物件加點呼叫 來訪問例項中的屬性和方法:

var shape = Shape()
shape.numberOfSides = 1
var shapeDes = shape.simpleDescription()
print(shapeDes)
  1. 構造器
    上面的類 Shape 少了一個重要的東西:當一個例項被建立的時候,用來構造這個類的初始化方法(可以稱為構造器)。用 init 來生成一個這樣的方法:
class NameShape {
    var numberOfSides: Int = 0
    var name :String
    init(name:String) {
        self.name = name
    }
    func simpleDescription() ->String {
        return "A shape with a name \(name)"
    }
}
var nameShape = NameShape(name: "ssss")
var initName = nameShape.simpleDescription()
  • 在建立類的例項的時候,它的一個屬性name在初始化方法中被初始化
  • 每一個屬性如果不是可選型別(加 ? 進行宣告),都 必須 賦值---無論是 numberOfSides 還是初始化方法中的 name
  1. 析構器
    在物件釋放時,可以在析構器中做些清理工作,用關鍵字 deinit 來生成一個析構器:
class NameShapeTwo {
    var numberOfSides: Int
    init(number:Int) {
        print("init called")
        self.numberOfSides = number
    }
    deinit {
        print("deinit called")
        numberOfSides = 0
    }
    func addNumbers(number:Int) {
        numberOfSides += 3
    }
}
var shapeTwo :NameShapeTwo? = NameShapeTwo(number: 3)
print("numberOfSides : \(shapeTwo!.numberOfSides)")
shapeTwo = nil
  • 輸出順序為:
    init called numberOfSides : 3 deinit called
  1. 類的繼承
    子類包含父類的名字,用冒號 : 將子類名和父類名隔開。如果子類要重寫/使用 父類已經定義過的方法,子類 必須 在該方法前加 override 關鍵字,如果不寫 override 編譯器會報錯。而且編譯器還會檢測那些帶有 override 的方法,判斷這些方法是否是真的重寫父類中的方法:
class Square: NameShape {
    var sideLength: Double
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    
    func area() ->Double {
        return sideLength * sideLength
    }
    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
var squareTest = Square(sideLength: 3.2, name: "subclass test")
squareTest.area()                 //10.24
squareTest.simpleDescription()    //"A square with sides of length 3.2."
squareTest.name                   //"subclass test"

同時需要注意:
1.子類可以設定自己宣告的屬性 2.子類可以改變在父類中定義的屬性; 3.子類可以呼叫父類的方法,比如上面構造器方法 init;

  1. getter 和 setter
    除了儲存屬性,其它屬性可以有一個 getter 和一個 setter 方法
class EquilateralTriangle: NameShape {
    var sideLength: Double = 0.0
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    var perimeter: Double {
        get {
            return sideLength*3.0
        }
        set {
            sideLength = newValue / 3.0
        }
    }
    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)    //"9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)   //"3.3"
  • perimetersetter 方法中,perimeter的新值有個預設名字 newValue,你也可以在 set 之後明確的指定一個名字,把名字放在()中,例如:set(perimeter) { sideLength = perimeter / 3.0}
  • 從上面的 getset方法和 OC 的方法有很大的區別,我們可以試著和 OC "相似"的方法來實現 sideLengthgetset方法:
class EquilateralTriangleOne: NameShape {
 
 init(sideLength: Double, name: String) {
     self.tempLength = sideLength
     super.init(name: name)
     numberOfSides = 3
 }
 
 var tempLength: Double = 0.0
 var sideLength: Double {
     get {
         print("getget")
         return self.tempLength
     }
     set {
         print("setset")
         self.tempLength = newValue
     }
 }
 override func simpleDescription() -> String {
     return "An equilateral triangle with sides of length \(self.sideLength)."
 }
}
var triangleOne = EquilateralTriangleOne(sideLength: 3.5, name: "b triangle")
print(triangleOne.sideLength) // getget 3.5
triangleOne.sideLength = 3.6  // setset
print(triangleOne.sideLength) // getget 3.6
  1. willSet 和 didSet
    如果你不需要計算一個屬性,但是仍需要在這個屬性被設定新值 之前之後,執行一些程式碼(暫且稱為程式碼A),那麼可以使用 willSetdidSet。在 構造器 之外,只要這個屬性的值發生改變,程式碼A 都會被執行,類似觀察者模式中的 property observe
class TriangleAndSquare {
    
    var triangleL: EquilateralTriangle {
        willSet {
            print("triangleL willSet")
            squareL.sideLength = newValue.sideLength
        }
       didSet {
            print("triangleL didSet")
        }
    }
    var squareL: Square {
        willSet {
            print("squareL willSet")
            triangleL.sideLength = newValue.sideLength
        }
        didSet {
            print("squareL didSet")
        }
    }
    init(size: Double, name:String) {
        // 少初始化任何一個物件,都會報錯 
        // return from initializer without initializing all stored properties
        // 類似於 oc 的用來儲存資料的 model,需要物件序列化一樣
        squareL = Square(sideLength: size, name: name)
        triangleL = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 5.0, name: "another shape")
print(triangleAndSquare.squareL.sideLength)       // 5.0
print(triangleAndSquare.triangleL.sideLength)     // 5.0
triangleAndSquare.squareL = Square(sideLength: 10.0, name: "lager shape") //squareL willSet   squareL didSet
print(triangleAndSquare.triangleL.sideLength)     // 10.0
  • 注意上面的註釋部分
  1. 操作符?
    用操作符? 來表示一個值是可選的,如果 ? 前面的值是 nil,那麼 ? 後面的所有操作都會被忽視,然後這個表示式的值會變為 nil:
let optionalSquare : Square? = Square(sideLength:5.3, name: "optionalSquare")
var optionalLength = optionalSquare?.sideLength
  • 如果 optionalSquarenil,那麼就不會呼叫sideLengthoptionalLength的值變為 nil

相關文章