A Day In The Life

とあるプログラマの備忘録

iOS でデータを永続化する方法

iOS データ設計入門の続きです。前回は iOS であつかうデータ全般について書きましたが今回はデータをフラッシュドライブに保存する方法について説明します。

データの永続化って何?

メモリにあるデータはアプリを終了すると消えてしまいます。
アプリを終了しても残しておきたいデータはフラッシュドライブに保存する必要があります。メモリにあるフラッシュドライブに保存することをデータの永続化といいます。永続化されたデータはフラッシュドライブが壊れない限り永続的に保存され残ります。以降 iOS でフラッシュドライブがどのように管理されているのかと、データを永続化するのにどのような方法があるのかについて説明していきます。

フラッシュドライブを構成する3つの領域

データを永続化する方法を説明する前に iOS でフラッシュドライブがどのように管理されているか見ていきましょう。
iOS ではフラッシュドライブは大きくわけて以下の3つの領域に分けて管理されています*1

  • アプリ領域
    アプリが自由に使うことが出来る領域。アプリごとに割り当てられていて他のアプリの領域を参照することは出来ない。
  • OS 領域
    OS が使用するデータが保存されている領域。OS 本体のファイルやアプリなど、基本的にアプリから参照することは出来ない。
  • 共有領域
    メディアライブラリ(写真、ビデオ、音楽)やイベント、連絡先など、すべてのアプリから参照することができる領域。ただしアプリから共有領域に書き込めるデータは以下の3種類に制限されている。

    種類 説明
    写真および動画 カメラで撮った画像データおよび動画
    UIImagePickerController や AssetsLibrary を使ってデータの参照と書き込みができる
    連絡先 電話帳のデータ
    Address Book を使ってデータの参照と書き込みができる
    イベント カレンダーにひもづいたイベントのデータ
    Event Kit を使ってデータの参照と書き込みができる


共有データとアプリ固有データの関係を図にすると以下のようになります。

共有データにデータを保存することも広い意味でデータの永続化になるのですが、使用頻度や用途が限定的であるため本記事では割愛します。詳細は以下を参照してください。

またアプリ間でデータを共有する方法としてペーストボードを使う方法があります。ペーストボードはメモリを使ってデータを共有する方法なので本記事では割愛します。詳細は以下を参照してください。

アプリ固有領域にデータを永続化する方法

アプリ固有領域にデータを永続化する主な方法は以下の通りです。

  • オブジェクトアーカイブ
    オブジェクトをバイナリ形式に変換してからファイルに永続化する。一般的にはオブジェクトシリアライズと呼ばれる。使用頻度は少ないが SDK の中で頻繁に使われている。データ永続化の中で一番基礎的な技術。
  • プロパティリスト
    プロパティリストと呼ばれる方式でデータを永続化する
    Property List Editor を使って簡単にデータの編集ができるのが特徴
  • NSUserDefaults
    アプリ固有の設定値を永続化することに特化したライブラリ。使用頻度は一番高い
  • Core Data
    Core Data と言われる O/R マッピングフレームワークを使用してデータを永続化する方法
    難易度が高いが使いこなせるようになると条件指定をしてデータを取得したりデータの一意性保証や Undo Redo などができるようになる。データは SQLite に保存される。

各永続化方法には、データの種類によって保存に向いている場合と向いていない場合があります。それを表にまとめると以下のようになります。

定数データ アプリの設定値 モデルオブジェクト
アーカイブ × × △(データ量少)
プロパティリスト △(ユーザが変更しないもの) ×
NSUserDefaults × ○(ユーザが変更するもの) ×
Core Data × × ○(データ量多)

※○…向いている △…特定条件のみ向いている ×…向いていない

iOS ではデータを保存できる場所が決められている

NSUserDefaults と Core Data はプログラマがデータの保存場所を意識する必要がありませんがオブジェクトアーカイビングとプロパティリストはプログラマがデータの保存場所を自分で管理する必要があります。
iOS でファイルを保存できる場所はセキュリティの関係上、各アプリのホームディレクトリ(/Applications//)以下に限られています。
ホームディレクトリは NSHomeDirectory() 関数で取得することができます。

NSLog(@"%@", NSHomeDirectory());

出力結果はこんな感じです。

/var/mobile/Applications/A11CE300-5F63-4918-B46C-2DFA6E18E0B7

A11 に続く文字列が GUID です。GUID はアプリによって変わります。
また iOS ではホームディレクトリ以下に専用のディレクトリがありそれぞれ役割が決まっています。

  • /アプリ名.app
    メインバンドルと呼ばれている。アプリのリソースファイルを保存するためのディレクトリ。読み取り専用。
  • /Documents
    アプリがファイルを作成して保存することができるディレクトリ。永続化したデータを格納する場合ここを使うのが一般的
  • /Library/Caches
    アプリが一時的に使う情報を保存するディレクトリ
  • /Library/Preferences
    アプリケーションの設定を保存するディレクトリ。NSUserDefaults のデータが保存される
  • /tmp
    一時ファイルを保存するディレクトリ。アプリが動作してないときに消される可能性がある

各ディレクトリのパスの取得方法は以下になります。

// アプリ名.app
NSLog(@"%@", [[NSBundle mainBundle] bundlePath]);
// Documents 第2引数と第3引数は固定
NSArray *paths = NSSearchPathForDirectoriesInDomains(
                                NSDocumentDirectory, 
                                NSUserDomainMask, YES);
NSLog(@"%@", paths[0]);
// Library/caches 第2引数と第3引数は固定
NSArray *paths2 = NSSearchPathForDirectoriesInDomains(
                                NSCachesDirectory, 
                                NSUserDomainMask, YES);
NSLog(@"%@", paths2[0]);
// tmp
NSLog(@"%@", NSTemporaryDirectory());

/Library/Preferences は NSUserDefaults が使うディレクトリなのでファイルパスの取得方法はありません。ここに直接アクセスしたくなるようなことはほとんどないと思います。
iOS でアクセスできるディレクトリとそのパスの取得方法を図にまとめるとこのようになります。
ディレクトリ構造

iOS 5からデータの保存場所についてガイドラインが追加されました

iOS 5から「The iOS Data Store Guidelines specify」というデータの保存場所についてのガイドラインが追加されました。このガイドラインに違反しているアプリはリジェクトされるので気をつけてください。iCloud 関連の規約変更だと思います。

The iOS Data Store Guidelines specify:

1. Only documents and other data that is user-generated, or that cannot otherwise be recreated by your application, should be stored in the /Documents directory and will be automatically backed up by iCloud.

2. Data that can be downloaded again or regenerated should be stored in the /Library/Caches directory. Examples of files you should put in the Caches directory include database cache files and downloadable content, such as that used by magazine, newspaper, and map applications.

3. Data that is used only temporarily should be stored in the /tmp directory. Although these files are not backed up to iCloud, remember to delete those files when you are done with them so that they do not continue to consume space on the user’s device.

要約するとこんな感じです。

  • /Documents ディレクトリにはユーザが自分の意思で保存したデータを保存すること
  • /Library/Caches ディレクトリには、あとから再びダウンロードして復旧可能なデータを置くこと
  • /tmp ディレクトリには一時的に使用するデータを保存すること

事例として、とあるアプリでサーバからダウンロードした設定情報を NSUserDefaults に保存していたらリジェクトされました。最終的にはサーバからダウンロードしたデータをメモリ上に保存するように修正して対応しました。

永続化方法の具体例

個々の永続化方法について別記事で説明していきます。

参考書籍

*1:3つの領域と書きましたが、実際に領域が物理的に分かれているわけではなく、データの管理方法として3つにわけて管理されているという意味です