[Swift 3.1] 全ての数値型に失敗可能変換イニシャライザーが追加されました

2017.03.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

数値型に新しくイニシャライザーが追加された

こんにちは。モバイルアプリサービス部の加藤潤です。
Xcode 8.3、Swift 3.1が正式にリリースされましたね!

Swift 3.1では全ての数値型に失敗可能変換イニシャライザーが追加されました。
proposalはSE-0080が該当します。

Proposed solutionにある通り、数値型にinit?(exactly:)という失敗可能イニシャライザーが追加されました。

//  Conversions from all integer types.
init?(exactly value: Int8)
init?(exactly value: Int16)
init?(exactly value: Int32)
init?(exactly value: Int64)
init?(exactly value: Int)
init?(exactly value: UInt8)
init?(exactly value: UInt16)
init?(exactly value: UInt32)
init?(exactly value: UInt64)
init?(exactly value: UInt)

//  Conversions from all floating-point types.
init?(exactly value: Float)
init?(exactly value: Double)
#if arch(i386) || arch(x86_64)
init?(exactly value: Float80)
#endif

何が変わったのか

Swiftは型に厳密な言語であるため、適宜型変換が必要になります。
これまでも以下のようにイニシャライザーで型変換を行なっていたと思います。

例えば、Double型からInt型への変換は以下のように行います。

// Swift 3.1以前からある変換イニシャライザー
let intValue = Int(1.2345) // 1

Int型では小数を表現することができないため端数処理が行われます。
また、以下のようにInt型の表現範囲を超えた値を変換しようとした場合、実行時エラーとなります。

// fatal error: Double value cannot be converted to Int because the result would be greater than Int.max
let intValue = Int(Double.greatestFiniteMagnitude)

上記の変換をSwift 3.1で追加された失敗可能変換イニシャライザーを使うと以下のようになります。

// Swift 3.1で追加された、より厳密な変換イニシャライザー
let intValue = Int(exactly: 1.2345) // nil
let intValue = Int(exactly: 1.1)    // nil
let intValue = Int(exactly: 1.0)    // Optional(1)
let intValue = Int(exactly: Double.greatestFiniteMagnitude) //nil

1.23451.1のように端数処理を行わないとInt型で表現できないような値は変換に失敗し、nilになります。 また、Double.greatestFiniteMagnitudeのようにInt型の表現範囲を超えた値の変換は実行時エラーにはならず、nilになります。
対して1.0はInt型で正確に表現可能であるため、Optional(1)となります。

どのような場面で使用するのか

Motivation にあるように、JSONのように緩く型付けされたデータからモデルオブジェクトを初期化する際に使用することが想定されているようです。
より型に厳密なイニシャライザーを使用することで意図しない値でモデルが初期化されることを防ぐことができます。

これまでのコードへの影響

新しくイニシャライザーが追加されましたが、これまでのイニシャライザーも継続して使えるため、既存のコードへの影響はありません。

参考資料