Swiftのインスタンス生成と解放

  • 4
    いいね
  • 0
    コメント

Swiftのインスタンス生成と解放を理解するためにサンプルコードを書いてみました。

Xcode 8.2.1のPlaygroundで動作を確認しています。

1. 2つのインスタンスの生成

class Car {
    var fuel : Int = 0      // 搭載燃料(残り燃料)量

    // 給油関数 (引数:給油量)
    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

// fooCar と barCar の2つの自動車インスタンスを生成
var fooCar = Car()
var barCar = Car()

// 初期化直後の残り燃料を表示
print(fooCar.fuel)          // => 0
print(barCar.fuel)          // => 0

// fooCar と barCar に給油する
fooCar.refuel(fuel : 30)    // 30リットル給油
barCar.refuel(fuel : 50)    // 50リットル給油

// 給油後の残り燃料を表示
print(fooCar.fuel)          // => 30
print(barCar.fuel)          // => 50

【 ポイント 】

  1. 2つのインスタンス(車)が存在しているから残り燃料(fuel)は車ごとに異なる。

2. 2つの変数が1つのインスタンスを参照

class Car {
    var fuel : Int = 0
    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

var fooCar = Car()
var barCar = fooCar         // fooCarを代入する

print(fooCar.fuel)          // => 0
print(barCar.fuel)          // => 0

fooCar.refuel(fuel : 30)
barCar.refuel(fuel : 50)

print(fooCar.fuel)          // => 80
print(barCar.fuel)          // => 80

【 ポイント 】

  1. fooCarbarCarは一つの同じインスタンスを参照している。だからfooCarbarCarは常に同じ残り燃料となる。

3. インスタンスの生成と解放のタイミング

class Car {
    var fuel : Int = 0
    var color : String
    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }
    deinit {
        print("destruct \(self.color) car")
    }

    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

do {
    var fooCar = Car(color: "Red")      // => "create Red car"
    var barCar = Car(color: "Blue")     // => "create Blue car"

    print(fooCar.fuel)
    print(barCar.fuel)

    fooCar.refuel(fuel : 30)
    barCar.refuel(fuel : 50)

    print(fooCar.fuel)
    print(barCar.fuel)
}
                                        // => "destruct Blue car"
                                        // => "destruct Red car"

【 ポイント 】

  1. init() が2回呼ばれている。 => 2つのインスタンスが生成されている。
  2. deinit が2回呼ばれている。 => doブロックから出たタイミングで2つのインスタンスが解放されている。

4. 生存期間(Life-time)が異なる2つのインスタンス

class Car {
    var fuel : Int = 0
    var color : String
    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }
    deinit {
        print("destruct \(self.color) car")
    }

    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

do {
    var fooCar = Car(color: "Red")          // => "create Red car"

    do {
        var barCar = Car(color: "Blue")     // => "create Blue car"

        print(fooCar.fuel)                  // => 0
        print(barCar.fuel)                  // => 0

        fooCar.refuel(fuel : 30)
        barCar.refuel(fuel : 50)

        print(barCar.fuel)                  // => 50
    }                                       // => "destruct Blue car"

    print(fooCar.fuel)
}                                           // => "destruct Red car"

 

【 ポイント 】

  1. barCarが参照しているインスタンスは内側のdoブロックを出るタイミングで解放されている

  2. fooCarが参照しているインスタンスは外側のdoブロックを出るタイミングで解放されている


5. 2つの変数が1つのインスタンスを参照 その2

class Car {
    var fuel : Int = 0
    var color : String
    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }
    deinit {
        print("destruct \(self.color) car")
    }

    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

do {
    var fooCar = Car(color: "Red")  // => "create Red car"
    var barCar = fooCar

    print(fooCar.fuel)
    print(barCar.fuel)

    fooCar.refuel(fuel : 30)
    barCar.refuel(fuel : 50)

    print(fooCar.fuel)
    print(barCar.fuel)
}                                   // => "destruct Red car"

【 ポイント 】

  1. インスタンスは1つ(1回)しか生成されていない。
  2. その1つのインスタンスをfooCarbarCarの2つの変数から参照している

6. 関数引数が参照型のときの関数の外側と内側の変数のふるまい

class Car {
    var color : String
    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }
    deinit {
        print("destruct \(self.color) car")
    }
}

func paint(car: Car, color: String) {
    car.color = color
}

do {
    var fooCar = Car(color: "Red")      // => "create Red car"
    var barCar = fooCar

    print(fooCar.color)                 // => "Red"
    print(barCar.color)                 // => "Red"
    print(fooCar === barCar ? "同一である" : "同一でない")
                                        // => "同一である"

    paint(car: fooCar, color: "Yellow")

    print(fooCar.color)                 // => "Yellow"
    print(barCar.color)                 // => "Yellow"
}                                       // => "destruct Yellow car"

【 ポイント 】

  1. 関数 paint() の外側のfooCarbarCarも、そして関数 paint() の内側のcarも、たった一つのインスタンスを参照している。
  2. だから、どれか一つの参照型変数のプロパティを書き換えると、他の2つの変数のプロパティも書き換わっている。

7. 構造体への書き換え

struct Car {                            // 構造体(struct)で定義
    var color : String
    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }
    // 構造体はdeinitを記述できない(記述する必要はない)
}

func paint(car: inout Car, color: String) { // 参照渡し(inout)
    car.color = color
}

do {
    var fooCar = Car(color: "Red")      // => "create Red car"
    var barCar = fooCar

    print(fooCar.color)                 // => "Red"
    print(barCar.color)                 // => "Red"

    paint(car: &fooCar, color: "Yellow")

    print(fooCar.color)                 // => "Yellow"
    print(barCar.color)                 // => "Red"
}

【 ポイント 】

  1. 構造体(struct)は値型だからfooCarbarCarは異なる。
  2. 関数 paint() の内側でのcarの変更を関数の外側に引き継ぎたい、という理由で参照渡し(inout)を使っている。

8. NSCopyingプロトコルを継承したインスタンスの複製

import Foundation

class Car : NSObject, NSCopying {
    var fuel : Int = 0
    var color : String

    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }

    deinit {
        print("destruct \(self.color) car")
    }

    func copy(with zone: NSZone? = nil) -> Any {
        let copy = Car(color:color)
        copy.fuel = fuel
        return copy
    }

    func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

do {
    var fooCar = Car(color: "Red")
    fooCar.refuel(fuel : 30)                // => "create Red car"
    print(fooCar.fuel)                      // => 30

    do {
        var barCar = fooCar.copy() as! Car  // => "create Red car"
        print(barCar.fuel)                  // => 30
        print(fooCar === barCar ? "同一である" : "同一でない")
                                            // => "同一でない"

        barCar.refuel(fuel : 50)
        print(barCar.fuel)                  // => 80
    }                                       // => "destruct Red car"

    print(fooCar.fuel)                      // => 30
}                                           // => "destruct Red car"

9. Carをクラス(参照型)から構造体(値型)に書き換え

struct Car {
    var fuel : Int = 0
    var color : String

    init(color : String) {
        self.color = color
        print("create \(self.color) car")
    }

    mutating func refuel(fuel : Int) -> Int {
        self.fuel = self.fuel + fuel
        return fuel
    }
}

do {
    var fooCar = Car(color: "Red")
    fooCar.refuel(fuel : 30)            // => 30
    print(fooCar.fuel)

    do {
        var barCar = fooCar
        print(barCar.fuel)              // => 30
        barCar.refuel(fuel : 50)
        print(barCar.fuel)              // => 80
    }

    print(fooCar.fuel)                  // => 30
}