iOS27のFoundation.Dataのハッシュ計算
個人開発アプリのパフォーマンス劣化
個人のiPhoneをiOS27にアップデートした。 個人開発アプリの挙動を確かめてみると、ある画面でパフォーマンスが明確に劣化していた。 すでにXcode27にも対応済みで、iOS26の端末では実機でも快適に動作していた。
調べてみると、iOS27の端末では、FoudationフレームワークのData型のハッシュ値を計算するときの仕様が変わっている模様。
不具合の修正と合わせて、少し調べてみることにする。
そもそもなぜパフォーマンスが劣化したか
パフォーマンスが劣化していた原因は主に2点ある。
- アプリに
Hashableな値を頻繁にハッシュするコードが含まれていた Data型のハッシュ計算の仕様がiOS27から変化した
アプリにHashableな値を頻繁にハッシュするコードが含まれていた
アプリの潜在不具合として、Hashableな値を頻繁にハッシュするコードが含まれていた。
例としては以下のようなコードが該当する。
ListのIDとして\.selfを設定しており、値全体がIDとなってしまっている。
struct Item: Hashable {
let id: UUID
let title: String
let image: Data // 画像データ。数十〜数百 KB
}
struct ItemListView: View {
let items: [Item]
@State private var isScrolling = false
var body: some View {
List {
// ID が値全体なので、List を更新するたびに全行の Item がハッシュされる。
// その中で`image`プロパティも全バイト読まれる
ForEach(items, id: \.self) { item in
ItemRow(item: item, isScrolling: isScrolling)
}
}
// スクロールの開始・終了のたびに body が評価し直され、List の更新が走る
.onScrollPhaseChange { _, phase in
isScrolling = phase.isScrolling
}
}
}
struct ItemRow: View {
let item: Item
let isScrolling: Bool
var body: some View {
HStack {
if let image = UIImage(data: item.thumbnail) {
Image(uiImage: image)
.resizable()
.frame(width: 44, height: 44)
}
Text(item.title)
}
.opacity(isScrolling ? 0.8 : 1)
}
}
実際には、入れ子になっているstructの子の型がData型になっており、大きなデータサイズを持ち得るプロパティがあることに気づけなかった。
List表示では、一意なidさえ渡せればよいため、Item.idを利用するのが正しい。
Data型のハッシュ計算の仕様がiOS27から変化した
iOS18からこのアプリを運用しており、当初からHashableな値を頻繁にハッシュするコードが存在していたが、これまでは表面化されていなかった。
その要因は、iOS26までの仕様と、iOS27での変化にある。
iOS26までは、Data型は先頭80Bのみを利用してハッシュを生成していた。
iOS27では、600KBでも全体をハッシュするようになっていた。
おそらく、全体をハッシュする仕様に変更されたのだと考えられる。
これによって、これまで一定であったハッシュの計算コストが、画像データのファイルサイズに比例して増加するようになってしまった。
実際に、Data.hashValueの計算時間を測定すると、以下のようになった。
| ファイルサイズ | iOS26.5 | iOS27.0 |
|---|---|---|
| 80B | 0.06μs | 0.06μs |
| 1KB | 0.05μs | 0.06μs |
| 60KB | 0.05μs | 32.1μs |
| 600KB | 0.05μs | 319.63μs |
ハッシュの衝突が正確に防げるようになっている改善であるが、頻繁にハッシュが計算されてしまう実装の場合、この仕様変更が大きなパフォーマンス劣化に繋がる可能性がある。
まとめ
iOS27移行では、Data型のハッシュの計算仕様が変更されており、データ全体を見るようになっている。
これによって、頻繁にハッシュ値を計算する処理では大きなパフォーマンスの劣化を招くことがあるため気をつける必要がある。
そして当然ではあるが、Listを始めとしたコンポーネントのidに対しては、必ずユニークなIDを渡すように注意したい