LoginSignup
18
15

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-19

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
}
18
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
15