NSCoding、今さらながら色々問題に当たったので情報整理のためにメモ。
例によって、今回のサンプルをGitHubに上げました。
シリアライズ(アーカイブ)するならNSCodingプロトコルの実装は必須
自作のクラスをシリアライズ(アーカイブ)してファイルに保存するには、NSCoding
プロトコルで定義されている、以下のふたつのメソッドを実装する必要があります。
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end
encodeWithCoder:aCoder
メソッドは、シリアライズするタイミングで呼ばれます。
initWithCoder:aDecoder
メソッドは逆に、デシリアライズするタイミングで呼ばれます。対の関係です。
これらのメソッドの実装は必須です。
実装されていない場合、不正な呼び出しでアプリがクラッシュします。
実装する
encode
実装自体はシンプルです。
シリアライズされる際に、シリアライズに必要な要素を、引数で渡されたNSCoder
オブジェクトのencode****:forKey:
を呼び出します。
encode****
の部分には、encodeしたい要素に適切な型を指定します。(e.g. encodeObject
, encodeInteger
)
forKey
によってキーを指定してシリアライズ/デシリアライズすることができます。
サンプルコード
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
decode
デシリアライズは逆の処理になります。
取り出す際は、自作クラスのプロパティに、decodeされた情報を保持することで復元が完了します。
サンプルコード
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
これらの情報は、必要なだけシリアライズ/デシリアライズする必要があります。
(逆に必要ないものを省略すると復元されなくなります)
データを保存する
データをシリアライズしたら、あとはそれをファイルに保存します。
最終的なサンプルコードは以下の通りです。
(サンプルは保存してすぐ読み込むだけの意味のないサンプルです)
# import "NCTViewController.h"
# import "NCTSample.h"
static NSString *kFileName = @"/hoge.dat";
@implementation NCTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self saveData];
[self loadData];
}
- (void)loadData
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *directory = paths[0];
NSString *filePath = [directory stringByAppendingString:kFileName];
NSMutableData *data = [NSMutableData dataWithContentsOfFile:filePath];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSArray *dataArray = [decoder decodeObjectForKey:@"array"];
[decoder finishDecoding];
NSLog(@"Data array: %@", dataArray);
}
- (void)saveData
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *directory = paths[0];
NSString *filePath = [directory stringByAppendingString:kFileName];
NSMutableArray *dataArray = [NSMutableArray array];
for (int i = 0; i < 5; i++) {
NCTSample *data = [[NCTSample alloc] init];
data.name = [NSString stringWithFormat:@"My name is Bob%d", i];
data.age = i;
[dataArray addObject:data];
}
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[encoder encodeObject:dataArray forKey:@"array"];
[encoder finishEncoding];
[data writeToFile:filePath atomically:YES];
}
@end