LoginSignup
2
2

More than 5 years have passed since last update.

Swift で optional な値を optional な NSNumber に

Last updated at Posted at 2016-10-27

はじめに

あるクラスには多くの optional な NSNumber プロパティがあったとします。

extension MyDailyNumbers {
     @NSManaged var open: NSNumber?
     @NSManaged var high: NSNumber?
     @NSManaged var low: NSNumber?
     @NSManaged var closed: NSNumber?
     @NSManaged var volume: NSNumber?
}

そして別のクラスが 先のクラスのインスタンスをプロパティとして持ってるとします。さて、これに値を設定するメソッドを考えてみます。そのメソッドの引数の一つ一つは NSNumber ではなく Int などでなかった場合に、それらを一つづづ NSNumber に変換しなければいけません。NSNumber は optional な値を イニシャライザでとってくれないので、nil かどうかを区別してあげる必要があります。

スッキリしない解決案

そこで、if let を使ってこんな風に書いてしまうかもしれません。

class MyObject {
    var numbers: DailyNumbers

    // [snip]

    func setNumbers(open: Int?, high: Int?, low: Int?, closed: Int?, volume: Int?) {
        if let open = open {
            self.numbers.open = NSNumber(value: open)
        }
        if let high = high {
            self.numbers.high = NSNumber(value: high)
        }
        if let low = low {
            self.numbers.low = NSNumber(value: low)
        }
        if let closed = closed {
            self.numbers.closed = NSNumber(value: closed)
        }
        if let volume = volume {
            self.numbers.volume = NSNumber(value: volume)
        }
    }
}

上記のコードには大きな問題があります。例えばパラメータに nil が入っていた場合はそれに相当するプロパティは更新されず、前の値が残ってしまいます。それを仕様だと主張するのも一つの方法ですが、かといって、こんな風に書いたら、きっと目眩さえ感じるかもしれません。

    func setNumbers(open: Int?, high: Int?, low: Int?, closed: Int?, volume: Int?) {

        if let open = open {
            self.numbers.open = NSNumber(value: open)
        }
        else {
            self.numbers.open = nil
        }

        if let high = high {
            self.numbers.high = NSNumber(value: high)
        }
        else {
            self.numbers.high = nil
        }

        if let low = low {
            self.numbers.low = NSNumber(value: low)
        }
        else {
            self.numbers.low = nil
        }

        if let closed = closed {
            self.numbers.closed = NSNumber(value: closed)
        }
        else {
            self.numbers.closed = nil
        }

        if let volume = volume {
            self.numbers.volume = NSNumber(value: volume)
        }
        else {
            self.numbers.volume = nil
        }
    }

かといってこんなふうにも書きたくありません。

    func setNumbers(open: Int?, high: Int?, low: Int?, closed: Int?, volume: Int?) {
        self.numbers.open = (open != nil) ? NSNumber(value: open!) : nil
        self.numbers.high = (high != nil) ? NSNumber(value: high!) : nil
        self.numbers.low = (low != nil) ? NSNumber(value: low!) : nil
        self.numbers.closed = (closed != nil) ? NSNumber(value: closed!) : nil
        self.numbers.volume = (volume != nil) ? NSNumber(value: volume!) : nil
    }

as Number?

そこで、Int などは NSNumberas オペレータで変換できるので、以下のように as NSNumber? を使うとスッキリ書くことができます。as? NSNumber ではありません。

    func setNumbers(open: Int?, high: Int?, low: Int?, closed: Int?, volume: Int?) {
        self.numbers.open = open as NSNumber?
        self.numbers.high = high as NSNumber?
        self.numbers.low = low as NSNumber?
        self.numbers.closed = closed as NSNumber?
        self.numbers.volume = volume as NSNumber?
    }

本当は違う方法を記事として書こうとして、書いているうちにこの方法を思い出したのは秘密。決して gist とかを探索しないように。

環境に関する表記

Xcode Version 8.0 (8A218a)
Apple Swift version 3.0 (swiftlang-800.0.46.2 clang-800.0.38)
2
2
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
2
2