WWDC 2018総括
というわけで前回予告どおり,
というわけで
Swift 5は2019年前半
まず重要なのは,
Cの完全上位互換言語として登場したC++がなぜCを置き換えられなかったかと言えば,
というわけでSwift 5以降はソースではなくコンパイル済みのライブラリやフレームワークやモジュールを使えるようになるのですが,
そういうこともあって,
enumの自動列挙
その筆頭がこちら。enum
はenumerationつまり列挙型ですが,
enum DayOfTheWeek {
case sun, mon, tue, wed, thu, fri, sat
}
for day in DayOfTheWeek {
// error: type 'DayOfTheWeek.Type' does not
// conform to protocol 'Sequence'
}
こうした場合は,
enum DayOfTheWeek {
case sun, mon, tue, wed, thu, fri, sat
static var allCases:[DayOfTheWeek] {
return [sun, mon, tue, wed, thu, fri, sat]
}
}
for day in DayOfTheWeek.allCases {
今度はOKだが……
}
これを全自動でやろうというのがSE-0194で,
enum DayOfTheWeek : CaseIterable {
case sun, mon, tue, wed, thu, fri, sat
}
for day in DayOfTheWeek.allCases {
これでよし
}
CaseIterable
というプロトコル準拠を宣言する必要があるのは,Equatable
やHashable
と同様です。すべてのenum
が列挙可能ではないことは,enum
が単なる列挙型を超えた,enum
であることをコンパイル時に保証できるわけです。
ターゲット環境ごとの条件付きコンパイル
たとえばシミュレータ用と本番環境用でコードを切り替えたいとします。今までは#ifでそれを切り替える方法がなかったので,os()
とcpu()
を使ってしかたなく次のようにしていました。
#if (os(iOS) ¦¦ os(watchOS) ¦¦ os(tvOS))
&& (cpu(i386) ¦¦ cpu(x86_64))
print("Simulator")
#else
print("Device")#endif
SE-0190でhasTargetEnvironment()
が導入されたことにより,
#if hasTargetEnvironment(simulator)
print("Simulator")
#else
print("Device")
# endif
Hashableプロトコルの刷新
SE-0185がSwift 4.Equatable
とHashable
の自動生成がすでに使えるようになっています。ストアドプロパティすべてがEquatable
やHashable
に準拠している型なら,Equatable
やHashable
を付けるだけで……,
enum DayOfTheWeek : Int, Hashable {
case sun, mon, tue, wed, thu, fri, sat
}
==(_:_)や.hashValueを実装しなくても使えるようになります。
let officeHour:[DayOfTheWeek:ClosedRange<Int>] = [
.mon : (900...1700),
.tue : (900...1600),
.wed : (900...1500),
.thu : (900...1400),
.fri : (900...1300),
]
DayOfTheWeek(rawValue:0) == .sun
注意点としては,extension
ではダメで型宣言の時点で行わないといけないのと,Hashable
を付けた場合には自動的にEquatable
になる点です。実は後者の点は型によっては問題になります。たとえばFloatingPoint
,==
で比較したらfalse
が返るnan
という値がありますが,Hashable
はできます。筆者は現在Hashable
はSwiftに自動生成してもらっています。ではNaN
などの扱いをどうしているかというと,FloatingPoint
プロトコルでは.isEqual()
というメソッドの実装を必須にしたうえでそちらで対応しているわけです。
このように自動生成が可能な場合でも手動で実装すればそちらが優先で使われるのですが,Hashable
はその代表格です。たとえば,
struct City {
let name: String
let state: String
let population: Int
}
という形があったとします。この場合population
はハッシュ値生成には不要なので,name
とstate
のハッシュ値を何らかの形で合成して.hashValue
を生成すればよさそうに見えます。Swift 4.
var hashValue: Int {
return name.hashValue ^ state.hashValue}
しかしこれはあまり良いやり方だとは言えません。衝突を起こす例は簡単に作れますname
とstate
の値を入れ替えただけでそうなる)。つまりhashdosに対して脆弱ということで,
そこでSE-0206が提案したのは,Hashable
には.hash()
というメソッドが追加され,Hashable
にする場合にもそれを使えということになりました。
func hash(into hasher: inout Hasher) {
name.hash(into: &hasher)
state.hash(into: &hasher)}
見てのとおり,