iOS開発でデータの保存をする


##データを保存できる領域

データは基本的にフラッシュドライブに保存します。
フラッシュドライブにある領域は大きく以下の3つ。

* アプリ領域
* OS領域
* 共有領域

主に使用するのは「アプリ領域」になります。
（プログラムからアクセスできる領域）

###アプリ領域の構成

アプリで参照できるのはアプリごとに割り振られたホームディレクトリに限られており、`/Applications/<GUID>/`以下がアプリ領域となります。
（ホームディレクトリは`NSHomeDirectory()`関数で確認できます）

ホームディレクトリ以下はさらにいくつかのディレクトリに分かれていて、それぞれ目的が異なります。

| ディレクトリ | 説明 |
|-----------|------|
| /アプリ名.app | メインバンドル。アプリ自体のリソースファイルを保存する |
| /Documents | アーカイブなどファイル単位で永続化する場合に使用 |
| /Library/Caches | キャッシュ領域 |
| /Library/Preferences | アプリケーションの設定情報を保存するディレクトリ。NSUserDefaultsで使用 |
| /tmp | 一時ファイル保存場所 |

####アプリ名の取得方法

```objc
NSLog(@"AppName: %@", [[NSBundle mainBundle] bundlePath]);
```

####ホームディレクトリ以下へのアクセス

```objc
NSArray *paths = NSSearchPathForDirectoriesInDomains([ディレクトリタイプ], NSUserDomainMask, YES);
```

ディレクトリタイプに定数を渡すことで、上記のそれぞれのディレクトリパスを得ることができます。

* NSDocumentDirectory
* NSCachesDirectory


##オブジェクトをシリアライズして保存する

Objective-Cのオブジェクトをシリアライズして、それをファイルに保存する方法。
シリアライズはバイナリ化のこと。
iOSではアーカイブ、アンアーカイブと呼ぶ。
アーカイブするには`NSKeyedArchiver`クラス、アンアーカイブするには`NSKeyedUnarchiver`クラスを利用する。

###アーカイブできるオブジェクトの種類

* NSArray
* NSDictionary
* NSString
* NSDate
* NSNumber
* NSData
* NSURL
* UIView
* UIViewController
* ※NSCoding プロトコルに準拠しているクラス

##プロパティリストを使ってデータを保存する

プロパティリストは、「.plist」の拡張子を持つXML形式のファイル。
通称plistファイルとも。

###プロパティリスト形式のデータを保存する

```sample.m
- (void)saveToPlistWithArray:(NSArray *)array
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *directory = [paths objectAtIndex:0];
    NSString *filePath = [directory stringByAppendingPathComponent:@"data.plist"];
    
    BOOL successful = [array writeToFile:filePath atomically:NO];
    
    if (successful) {
        LOG(@"%@", @"データの保存に成功");
    }
}
```

###プロパティリスト形式のデータを読み込む

```sample.m
- (void)loadFromPlistWithArray
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *directory = [paths objectAtIndex:0];
    NSString *filePath = [directory stringByAppendingPathComponent:@"data.plist"];
    NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
    
    if (array) {
        for (NSString *data in array) {
            LOG(@"%@", data);
        }
    }
    else {
        LOG(@"%@", @"データがありません。");
    }
}
```


##NSUserDefaultsを使ってデータを保存する

NSUserDefaultsに保存できるデータの型は以下。

* NSString
* NSNumber
* NSData
* NSDate（日付）
* NSArray
* NSDictionary
* NSURL


|オブジェクトの型 | 保存メソッド | 取得メソッド |
|--------------|------------|------------|
| (id型) | setObject: forKey: | objectForKey: |
|NSString | setObject: forKey: | stringForKey: |
|NSArray | setObject: forKey: | arrayForKey: |
|NSArray(文字列) | setObject: forKey: | stringArrayForKey: |
|NSDictionary | setObject: forKey: | dictionaryForKey: |
|NSData | setObject: forKey: | dataForKey: |
|NSDate（日付） | setObject: forKey: | objectForKey: |
|NSInteger | setInteger: forKey: | integerForKey: |
|float | setFloat: forKey: | floatForKey: |
|double | setDouble: forKey: | doubleForKey: |
|BOOL | setBool: forKey: | boolForKey: |
|NSURL | setURL: forKey: | URLForKey: |


### NSUserDefaultsに保存されている値を確認する

以下のようにすることで、保存されているすべてのキーと値を確認することが出来ます。

```objc
NSDictionary *dict = [NSUserDefaults.standardUserDefaults dictionaryRespresentation];
NSLog(@"Defaults: %@", dict);
```

また、以下のようにすることで、該当アプリで設定したもののみを確認することが出来ます。

```objc
NSDictionary *dict = [NSUserDefaults.standardUserDefaults persistentDomainForName:NSBundle.mainBundle.bundleIdentifier];
NSLog(@"Defaults: %@", dict);
```


##Core Dataを使ってデータを保存する

※CoreDataに関しては別記事でまとめているので、以下を参照ください。

* [
[Objective-C] CoreDataを使う](http://qiita.com/edo_m18/items/8bc7615eb2f6d546705d)
* [
CoreDataのマイグレーションをしようとしたときのメモ（未解決あり）](http://qiita.com/edo_m18/items/717fe32d744a10df7179)

##参考記事

* [iOS でデータを永続化する方法](http://d.hatena.ne.jp/glass-_-onion/20110904/1315142404)