オブジェクトとクラス【Swiftイントロダクション・第4回】

クラスを定義するには、クラスの名前をclassのあとに記述しします。プロパティの宣言は、定数や変数の宣言と同じように行います。関数やメソッドの宣言も同じように行います。

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

クラスのインスタンスを生成するにはクラス名の後に括弧()を付けます。インスタンスのプロパティやメソッドにアクセスするにはドットシンタックスを使います。

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

上記のshapeクラスには、インスタンスが生成された時にインスタンスの初期化をするイニシャライザがありません。initを使ってイニシャライザを足しましょう。

class Shape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        <strong>self</strong>.name = name
    }

    func simpleDescription() -> String {
        return "A \(name) with \(numberOfSides) sides."
    }
}

スクリーンショット

上記のコードで、selfがnameプロパティとイニシャライザの引数のnameを区別するのに使われていることに注目してください。クラスのインスタンスを生成する時、関数の呼び出しと同じようにイニシャライザに引数が渡されます。全てのプロパティに対して値が入っている必要があります。値は、宣言する時か初期化のタイミングで入れます。numberOfSidesは宣言時に、nameは初期化時に値が入れられています。

オブジェクトが解放される前に、クリーンアップをしたいときはdeinitを使ってデイニシャライザを生成します。

サブクラスでは、スーパークラスの名前をコロン(:)の後に記述します。クラスが標準ルートクラスをサブクラスしないといけないというルールはないので、必要に応じてスーパークラスを含めます。

//Shapeクラスをサブクラスする
class Square: Shape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    //スーパークラスの実装をオーバーライドしているのでoverrideをつける
    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

サブクラスで、スーパークラスの実装をオーバーライドするメソッドにはoverrideを付けます。overrideなしで実装をオーバーライドしようとするとコンパイルエラーになります。 スクリーンショット コンパイラはスーパークラスの実装をオーバーライドしていないのにoverrideとついているメソッドも検出します。 スクリーンショット

プロパティは値を保持するだけでなく、ゲッターとセッターを持つこともできます。

class Triangle: Shape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var totalSideLength: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "\(name) with sides of length \(sideLength)."
    }
}
//TriangleクラスをsideLength = 3.1, name = "a triangle"で初期化する
var triangle = Triangle(sideLength: 3.1, name: "a triangle")
//totalSideLengthのgetが実行される
print(triangle.totalSideLength)
//totalSideLengthのsetが実行されてsideLengthに新しい値が設定される
triangle.totalSideLength = 9.9
print(triangle.sideLength)
triangle.simpleDescription()

スクリーンショット

totalSideLengthのセッターの中で、新しい値にはnewValueという名前がついています。setのあとの括弧の中で、新しい名前をつけることができます。

EquilateralTriangleクラスの初期化には3ステップあることに注目してください。

  1. サブクラスで宣言されたプロパティに値を代入する
  2. スーパークラスのイニシャライザを呼ぶ
  3. スーパークラスで定義されたプロパティの値を変更する。メソッド、ゲッター、セッターを使った追加のセットアップがあればこのタイミングで行う。

もしプロパティを計算する必要はないものの、新しい値が設定される前と後でコードを実行させたい場合には、willSetとdidSetを使います。コードはイニシャライザ以外の場所で値が変更された場合に実行されます。例えば、下記のコードでは三角形の変の長さが正方形の辺の長さと必ず同じになるようにしています。

class TriangleAndSquare {
    var triangle: Triangle {
        willSet {
            //triangleの値がセットされたらsquareのsideLengthに同じ値をセットする
            square.sideLength = newValue.sideLength
        }
    }
    var square: Square {
        willSet {
            //squareの値がセットされたらtriangleのsideLengthに同じ値をセットする
            triangle.sideLength = newValue.sideLength
        }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = Triangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

オプショナル値を扱っている場合は?をメソッドやプロパティ、サブスクリプティングの前に置くことができます。もし?の前の値がnilだった場合、?のあとのコードは実行されず、式全体の値がnilになります。?の前の値がnilでない場合は、オプショナル値はアンラップされ、アンラップされた値に対して?の後の処理が行われます。どちらのケースでも、式全体の値はオプショナル値です。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength