書いて覚えるSwift入門

第18回APFSとSwift3

APFS―3度目の正直

いよいよSwift 3の解説……の前にOne More Thing。⁠新製品なきWWDC」前回言いましたが、実は正真正銘の新製品が1つありました。APFS。現在のHFS+に代わる新しいファイルシステムです。ファイルシステムというものの重要性を考えれば、これをスルーするわけにはいかないでしょう。

ファイルシステムの歴史

Apple製品のファイルシステムと言えば、1998年以来一貫してHFS+でした。Jobsの復帰は、技術的には買収元であるNeXTが買収先であるAppleを乗っ取っていく歴史でもあり、macOSは(9までの)Mac OSではなくNextStepの子孫なのですが、唯一乗っ取れなかったのがHFS+です。

HFS+が受けた最初の挑戦が、NextStep 由来のUFS(Unix File System)であるのはごく自然とも言えます。OS X登場当時はUFSがHFS+を置き換えると思われていたのですが、HFS+はその挑戦を退けます。HFS+にはユーザごとの所有権やパーミッションといった、OS XというUnixに必要な機能をすべて備えていたからです。UFSにあってHFS+にないのは、スパースファイルやファイル名の大文字小文字の区別ぐらいで、それらは必須ではなかったのです。

次の挑戦はZFSの登場でした。ZFSはこれまでのファイルシステムの常識を覆す画期的なファイルシステムでした。fsckを不要にするトランザクション、パーティションという概念を過去のものにするデータセット、ファイルシステム自体のundoを可能にするスナップショット、エラーを自動検知し、可能であれば自動修復するチェックサム、RAIDホールがないRAID-Z……「Z=最後のファイルシステム」という自信がその名に込められたZFSは、今は亡きSun Microsystemsの最後の遺産でもあります。

ZFSオープン化の理由

画期的だったのは技術にとどまりません。同社はZFSをオープンソースしたのです。おかげでSolaris以外のOSにも移植が進みました。最も熱心だったFreeBSDは、今やZFSが最大の売りの1つにもなっていますし、ライセンスがGPLと非互換ということで公式サポートできないはずのLinuxすら、カーネルモジュールをカーネル本体とは別配布にすることで利用可能になっています。そしてOS Xも、いったんは公式サポートしたのです。OS X v10.5にはリードオンリーとはいえZFSが追加されました。しかしAppleは、v10.6でZFSサポートを打ち切ってしまいます。

例によってAppleはその理由をつまびらかにしていませんが、最も人気がある仮説はSunがOracleに買収されたことで、OracleがAppleへのライセンスを蹴ったというものです。かなりの説得力もあり、筆者自身ほとんど説得されていた一方、ZFSがオープンソースであり、OS Xを含む複数OSのサポートがOpenZFSとして結実していることを考えると鵜呑みにし難くもあります。

HFS+がUFSとZFSを退けた話は、拙著コードなエッセイ[1]⁠』も取り上げたのでそちらでも確認していただくとして、APFSの登場で、なぜZFSではダメだったのか、筆者にもやっと納得できる理由が得られたように思います。

HFS+をZFSにライブアップグレードするのは不可能だからです。技術的にはさておき、既存のAppleデバイスのユーザが受け入れられる形では。

ファイルシステムのライブアップグレードは、大きなところでは二度行われています。1つは1998年の、MacにおけるHFSからHFS+へのアップグレード。もう1つはWindowsにおけるFATからNTFSへのアップグレード。どちらも共通しているのは、新ファイルシステムが旧ファイルシステムのすべての機能を上位互換としてサポートしていること、そしてアップグレードするのはメタデータのみで、データはそのままだということ表1⁠。

表1 ファイルシステムの機能比較
機能HFS+ZFSAPFSコメント
Copy-on-write×新世代FSの要
スナップショット×
クローン×
RAID××
データセット×既存FSのパーティション相当
チェックサム×APFSはメタデータのみ
暗号化x
SSD最適化
タイムスタンプ粒度ナノ秒ナノ秒
最長ファイル名UTF-16で255文字255バイトHFS+以上ZFSでは互換性不足
旧FSからのアップグレードNA×ZFSでは困難

たとえば最長ファイル名。ZFSは255バイトなのに対し、HFS+は(実はNTFSも)UTF-16で255文字。バイト長で倍も違います。255バイトでも長過ぎに一見思えますが、Twitterのつぶやきをそのままファイル名にペーストするユーザがいないと誰が断言できるでしょうか。

その一方、ZFSではストレージに書き込む情報すべてにチェックサムを付けていますが、APFSではメタデータのみ。もしZFS同様にAPFSもデータのチェックサムを付けようとするとどうなるか? ファイルシステムのアップグレードの際に、使用ブロックの情報をすべて読む必要があります。それはアップグレードに数分ではなく数時間を要するということであり、古くからのパソコンユーザなら何とか耐えられてもスマフォやタブレットのユーザにそこまでの忍耐力は期待できないでしょう。

APFSの真意

APFSのことを最初に耳にした時点での筆者の感想は、⁠それってZFSと何が違うの?」でした。とくにその要となっているのが表1で挙げたようにcopy-on-writeがZFSの真髄でもあります。しかしAppleははっきり表明しました。⁠18ヵ月後、すべてのAppleデバイスのデフォルトファイルシステムをAPFSに移行する。既存デバイスはそのままアップグレードする」と。

あらためて見てみると、ZFSとAPFSは要となっている技術は共通していても、ユースケースは180度異なるとも言えます。ZFSが威力を発揮するのは、サーバの大規模ストレージ。APFSはパーソナルデバイスの内部ストレージ。ZFSにとってのSSDの役どころはディスクアレイのキャッシュ、APFSにとってのSSDは唯一のストレージ。ZFSにとって暗号化は「あればうれしい」機能、APFSにとって暗号化は「欠かせない機能」……。

APFSはオープンソース化されない?

1つ気になるのは、⁠APFSはApple製品に最適化」されていることを強調していること。これまでAppleはOSの基礎部分をDarwinとしてオープンソースしてきました。HFS+まわりのコードもその一環として公開されています。ファイルシステムなきOSというのが現状ありえない以上当然とも言えますが、HFS+とAPFSのどちらでもブートできるとしたら、APFSのコードはDarwinに含める必要はなくなります。

残念ながらその可能性は低くないでしょう。残念でないことに、ファイルシステムの違いはネットが当然のように吸収してくれます。フロッピー、CD-ROM、DVD……リムーバブルドライブはMacからも消えてしまいました。そしてUSBメモリすら、iPhoneやiPadにはそのまま刺さらない。そしてそれをほとんど誰も気にしないとあっては、Apple製品に最適化されたファイルシステムをサポートするインセンティブはさほど高くはない。オープンソースなHFS+すら、Appleの外ではほとんどサポートされているとは言えないのに……。

それでも、Apple製品向けにソフトウェアを開発する我々は、APFSの機能を学ばざるを得ないでしょう。とくにクローンやスナップショットのようなHFS+にはない特長を活かすとしたら。1つ確かなのは、APFSのAPIはSwiftでも提供されるであろうということ。どんなふうになるか、今から楽しみです。

swift.version++ // 廃止される機能

それではいよいよSwift 3の紹介を。といっても、実はSwift 2.2をお使いの皆さんはすでに触れているといっても過言ではありません。Swift 3で廃止が予定されている機能に関しては、すでに警告が出るからです。Swift 3へすぐに移行せずにとりあえずSwift 2.3にとどまるにしろ、警告は今のうちに消しておきましょう。

++ / --

おそらく一番知られているのは[SE-0004]。ほとんどのケースで、+= 1とするだけで対処できるでしょう。どうしても欲しかったら、あらためてオレ演算子として定義してしまえば良いのです。Swift 3でも警告なしで使えます。

prefix func ++(i: inout Int) -> Int {
    i += 1
    return i
}

postfix func ++(i: inout Int) -> Int {
    let o = i
    i += 1
    return o
}

C-Styleのfor

[SE-0007]SequenceTypeあらためSequenceプロトコルがあるSwiftではほとんど不要でしょう。

for var i = 0; i < 10; i++ {
  let elem = array[i]
  // …
}

より、

for elem in array {
  // …
}

のほうがはるかにわかりやすいですし、iも欲しければ、

for (i, elem) in array.enumerate() {
  // …
}

とするだけです。

パラメータvar

[SE-0003]のパラメーター中のvar禁止とはどういうことかというと、こういうコードが書けなくなるということです。

Swift2
func gcd(var a: Int, var _ b: Int) -> Int {
    a = abs(a); b = abs(b)
    if (b > a) { (a, b) = (b, a) }
    while (b > 0) { (a, b) = (b, a % b) }
    return a
}

varしたかったら、ブロック内であらためてやれ、と。

Swift(2¦3)
func gcd(a: Int, _ b: Int) -> Int {
    var (x, y) = (abs(a), abs(b))
    if (x > y) { (x, y) = (y, x) }
    while (y > 0) { (x, y) = (y, x % y) }
    return x
}

カリー化構文

「インド人は右へ」ではなくて、[SE-0002]は次のようなコードが書けなくなるということです。

Swift2
func logWithBase(b:Double)(_ x:Double)-
>Double {
    return log(x)/log(b)
}

let log2 = logWithBase(2)
print(log2(8)) // 3.0

Swiftにはブロックがあるので、こう書けばOKですし、そのほうがわかりやすいでしょう。

Swift(2¦3)
func log(base b:Double)->(Double)->Double {
    return { x in log(x)/log(b) }
}

let log2 = log(base:2)
print(log2(8)) // 3.0

実際のところ、カリー化構文を――とくにプロダクションコードで――使っている方は少ないかと思われますが念のため。

パラメータのタプル渡し

ほとんど知られていなかった機能ですが、Swift 2では次のコードがOKでした。

Swift2
func distance(_ x:Double, _ y:Double)-
>Double {
  return sqrt(x*x + y*y)
}
let p = (3.0, 4.0)
print(distance(p)) // 5.0

パラメータ全体をタプルにして、そのタプルを1つ渡すとすべてのパラメータを渡したのと同等で、言語的には一貫してはいるのですが、バグを引き寄せやすい機能ということで、[SE-0029]で廃止されることになりました。

Swift(2¦3)
func distance(_ x:Double, _ y:Double)-
>Double {
  return sqrt(x*x + y*y)
}
let p = (3.0, 4.0)
print(distance(p.0, p.1)) // 5.0

次回に向けての予習

というわけでSwift 3で廃止される機能の紹介だけで今号の誌面が尽きてしまったようですが、オープンソース化されたおかげで、Swift 3の変更はGitHubのswift-evolutionでも事前に確認できます。さらにうれしいことに、すでに実装済みの機能に関しては、IBM Swift Sandboxで実際に動かしてみることもできます図1、図2⁠。

図1 IBM Swift Sandboxでの実行例(コード編集)
図1 IBM Swift Sandboxでの実行例(コード編集)
図2 IBM Swift Sandboxでの実行例(複数バージョンのSwiftを指定)
図2 IBM Swift Sandboxでの実行例(複数バージョンのSwiftを指定)

パソコンはもちろん、スマートフォンからでも。Swiftのバージョンを切り替えて試せる点も素晴らしい。

というわけで、次回はいよいよSwift 3の核心に迫っていく予定です。

Software Design

本誌最新号をチェック!
Software Design 2022年9月号

2022年8月18日発売
B5判/192ページ
定価1,342円
(本体1,220円+税10%)

  • 第1特集
    MySQL アプリ開発者の必修5科目
    不意なトラブルに困らないためのRDB基礎知識
  • 第2特集
    「知りたい」⁠使いたい」⁠発信したい」をかなえる
    OSSソースコードリーディングのススメ
  • 特別企画
    企業のシステムを支えるOSとエコシステムの全貌
    [特別企画]Red Hat Enterprise Linux 9最新ガイド
  • 短期連載
    今さら聞けないSSH
    [前編]リモートログインとコマンドの実行
  • 短期連載
    MySQLで学ぶ文字コード
    [最終回]文字コードのハマりどころTips集
  • 短期連載
    新生「Ansible」徹底解説
    [4]Playbookの実行環境(基礎編)

おすすめ記事

記事・ニュース一覧