The Swift Programming Language 勉強記録 - 3

なんとか第3回目。先が長いですね、困った(´ー`)
とりあえず"A Swift Tour"の終わりまでを目指します。

クラス

クラスを作成するときはこんな感じ。

class NamedShape {
    // property
    var numberOfSides: Int = 0
    var name: String
    
    // initializer:インスタンス作成時に呼ばれる
    init(name: String) {
        self.name = name
    }
    
    // deinitializer:ARC(Automatic Reference Counting)によって自動的に呼ばれる
    deinit {
        println("deinit")    
    }
    
    // method
    func simpleDescription() -> String {
        return "\(self.name) shape with \(numberOfSides) sides."
    }
}

// インスタンス生成
let namedShape = NamedShape(name: "HOGE")
// メソッドコール
namedShape.simpleDescription()
// "HOGE shape with 0 sides."

namedShape.numberOfSides = 9
namedShape.simpleDescription()
// "HOGE shape with 9 sides."

initはJavaC++コンストラクタのようなもので、インスタンス生成の際に呼ばれ、初期化処理などを記述する。
すべてのプロパティ値は宣言時に初期化するか、init内で代入してやる必要がある。
deinitというのはインスタンスへの参照がなくなったときにARCによって自動的に呼ばれるもので、何かしら後始末したい処理を書いたりする(ARCは自動でメモリ管理を行ってくれる仕組み)。

継承

クラスの継承は次のように行う。

// NamedShapeクラスを継承したCircleクラスを定義
class Circle: NamedShape {
    
    var radius: Double
    
    init(radius: Double, name: String) {
        self.radius = radius
        // 親で定義しているinitを呼び出して親の持っているnameにセット
        super.init(name: name)
    }
    
    // 面積を求める
    func area() -> Double {
        return radius * radius * 3.14
    }
    
    // 説明を出力(オーバーライド)
    override func simpleDescription() -> String {
        return "\(self.name) circle with radius \(self.radius)"
    }
}

let testCircle = Circle(radius: 1.2, name: "My circle")
testCircle.area() // 4.5216
testCircle.simpleDescription() // "My circle circle with radius 1.2"

Getter と Setter

プロパティに対するゲッター・セッターを次のような形で持たせることができる。

// 正三角形クラス
class EquilateralTriangle: NamedShape {

    var sideLength: Double = 0
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    
    // 周囲を求める
    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        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")
triangle.perimeter // 9.3
triangle.perimeter = 9.9
triangle.perimeter // 9.9

perimeterプロパティ宣言時に、ブロックを使ってget{...}とset{...}を持たせてあげた上で、参照や代入を行うとget・set内の処理が走る。

なお、セッターの内部ではnewValueという変数を自動的に持つことになり、プロパティへ代入した値がこの変数に入る。
この変数名はset(foo){...}とすることにより、明示的に指定できる。

// setを以下のように書き換えてもOK
set(foo) {
    sideLength = foo/3.0
}

willSet と didSet

willSetとdidSetについて。
willSetはプロパティに対して値がセットされる前に実行され、didSetは値がセットされた後に実行される。

class Person {
    var name: String = "" {
        willSet {
            println("willSet! name property is \(name)")
        }
        didSet {
            println("didSet! name property is \(name)")
        }
    }
}
let taro = Person()
taro.name = "Taro" // この前後でwillSet, didSetが呼ばれる

// Console Output
// willSet! name property is
// didSet! name property is Taro

上の例では、willSetはnameプロパティに値が入る前に呼ばれるので初期値の空のまま出力。
didSetはnameプロパティに値が入った後に呼ばれるので"Taro"が入っている。

メソッドと関数の違い

メソッドと関数には重要な違いがある。
関数の引数名は関数内部でのみ使用され、関数を呼び出すときには使用しない。
一方メソッドメソッド内部でも、メソッドを呼び出す際にも使用される(ただし、第一引数だけはメソッドを呼び出す際には使用されず、内部のみでの使用)。

class Person {
    func greet(name: String, age: String, from: String) -> String {
        return "I am \(name) from \(from). \(age) years old."
    }
}

let ken = Person()
// 第一引数のみ内部、第二引数以降外部
ken.greet("Ken", age: "10", from: "Japan")

なお、内部で使用する引数名と、呼び出し時に使用する引数名を分けることもできる。

class Person {
    func greet(name: String, numberOfAge age: String, from: String) -> String {
        return "I am \(name) from \(from). \(age) years old."
    }
}

let ken = Person()
// numberOfAgeという外部のパラメータ名をつけた
// ageは内部パラメータ名となる
ken.greet("Ken", numberOfAge: "10", from: "Japan")

Optional型

optional valuesも使用できる。
使いたい場合は次のように記述する。

var testCircle: Circle? = Circle(radius: 1.2, name: "My circle")
testCircle?.area()
testCircle?.simpleDescription()
testCircle = nil
testCircle?.area() // nil

Optional型の変数がnilの場合、その後はプロパティにアクセスしたり、関数を呼び出したりした際にnilを返すようになる。

つづく。