インスタンス変数のoptional宣言について

  • 26
    Like
  • 0
    Comment
More than 1 year has passed since last update.

インスタンス変数を定義する際のoptionalの扱いについてまとめてみた。
optionalの基本的な動きについては、下記を参照。

optionalではない変数を宣言する

前提として、optionalではない変数はnilではない必要がある。
下記はビルドが通らない。

class Person {
    var name: String
    var age: Int
    var address: String
}

変数の初期化の方法としては、変数の宣言時に行う方法とinit()の内部で行う方法がある。
下記のように、すべての変数を初期化しないと実行はできない。

class Person {
    var name: String
    var age: Int = 30 // 宣言時に初期化
    var address: String

    init() {
        // init内で初期化
        self.name = ""
        self.address = "Tokyo"
    }
}

初期化の流れ

init()メソッドが複数ある場合は、すべてのメソッド内で初期化しておく必要がある。
convenienceinit()を定義してメソッドをオーバーロードすることも可能。

class Person {
    var name: String
    var age: Int = 30
    var address: String

    // convenienceをつける
    convenience init() {
        // 自分自身のinitを呼び出すことができる
        self.init(name: "", age: 25)
    }

    init(name: String) {
        // 変数を初期化
        self.name = name
        self.address = "Fukuoka"
    }

    init(name: String, age: Int) {
        // こっちのinit内でも初期化
        self.name = name
        self.age = age
        self.address = "Tokyo"
    }
}

また、全インスタンス変数の初期化が終わるまでメソッド呼び出しはできない。
インスタンス変数もメソッド呼び出しと同等で参照できない。

class Person {
    var name: String
    var age: Int = 30
    var address: String

    func lastName() -> String {
        return self.name
    }

    init() {
        var firstName = self.name // ビルドエラー
        self.lastName() // これもビルドエラー
        self.name = "joe"
        self.address = "Tokyo"
    }
}

親クラスが存在する場合なども、まずは自分のクラスで定義された変数を初期化する必要がある。

class Human {
    var gender: String?
}

class Person: Human {
    var name: String
    var address: String
    var age: Int = 30

    // 親クラスのinitをオーバーライド
    override init() {
        self.name = "joe"
        var gender = self.gender // 初期化完了前に変数を参照しているのでビルドエラー
        super.init() // これも初期化完了前に呼び出しているのでエラー
        self.address = "Tokyo"
    }
}

optionalな変数を宣言する

nilが許容されるため初期化は任意であり、必ずしも値が入っていなくても良い。
例えば、viewなどの初期化が終わらないとインスタンス化できない変数のみ!を付けて宣言する。

class SampleViewController: UIViewController {
    // nilにはしたくないが、インスタンス時には初期化できないので!を付けておく
    var sampleView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // 忘れずに初期化してあげないとバグを生む可能性が高い
        self.sampleView = UIView(frame: self.view.frame)
        self.view.addSubview(self.sampleView)
    }
}

初期化されないかもしれない変数については、基本的に?を付けて宣言する。

class SampleViewController: UIViewController {
    // 例えばサーバから取得した画像を保持する変数などは?にしておく
    var sampleImage: UIImage?

    override func viewDidLoad() {
        super.viewDidLoad()

        var url = NSURL(string: "http://example.png")
        var request = NSURLRequest(URL: url)
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
            (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in

            // 代入のタイミングは任意なので、参照はnilでも構わないようにしておく必要がある
            self.sampleImage = UIImage(data: data)
        }
    }
}

まとめ

  • optionalではない変数は、宣言時かイニシャライズ時に何かしらの値を代入しておく
  • optional変数は基本的に?で宣言する
  • viewなど、参照時に必ず値が代入されている予定の変数にのみ!を使用する
  • !で宣言した変数には、必ず値を代入する