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+ ZFS APFS コメント
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での実行例(コード編集)
図2 IBM Swift Sandboxでの実行例(複数バージョンのSwiftを指定)
パソコンはもちろん、スマートフォンからでも。Swiftのバージョンを切り替えて試せる点も素晴らしい。
というわけで、次回はいよいよSwift 3の核心に迫っていく予定です。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)