アーカイブ・アンアーカイブ

オブジェクトを単純なデータに永続化する処理、その逆に永続化されたデータからオブジェクトを作り直す処理はオブジェクト指向言語には必ずといって良いほど必要な処理だ。

JavaC#は直列化/非直列化(シリアライズ/デシリアライズ)と呼ぶが、iOSではオブジェクトアーカイブ/アンアーカイブと呼ぶ。※1
そのアーカイブ/アンアーカイブだが、Objective-Cのプロトコルを実装することで実現する。

NSCodingプロトコル

NSCodingプロトコルはオブジェクトのアーカイブ/アンアーカイブのためのインタフェースである。※2

@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end

使い方は簡単であり、自ら起こしたクラスではNSCodingプロトコルを実装するだけである。逆説的に言えば自ら起こしたクラスは自らがNSCodingプロトコルを実装しないと永続化ができないということだ。

Possession.m

最近お気に入りのiOSプログラミング 第2版からの引用(そればっかり)のPossessionクラスの実装は以下のようになっている。 (NSCodingプロトコルのメソッドのみ)

- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:possessionName forKey:@"possessionName"];
    [encoder encodeObject:serialNumber forKey:@"serialNumber"];
    [encoder encodeObject:dateCreated forKey:@"dateCreated"];
    [encoder encodeObject:imageKey forKey:@"imageKey"];
    
    [encoder encodeInt:valueInDollars forKey:@"valueInDollars"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    if (self) {
        [self setPossessionName:[decoder decodeObjectForKey:@"possessionName"]];
        [self setSerialNumber:[decoder decodeObjectForKey:@"serialNumber"]];
        [self setImageKey:[decoder decodeObjectForKey:@"imageKey"]];
        [self setValueInDollars:[decoder decodeIntForKey:@"valueInDollars"]];
        dateCreated = [[decoder decodeObjectForKey:@"dateCreated"] retain];
    }
    return self;
}

アーカイブ時にはencodeObjectメソッドを使って必要なデータ(全てのプロパティ又はフィールド)の永続化を処理し、アンアーカイブ時にはdecodeObjectメソッドでデータからオブジェクトを処理する。スカラ型はencodeObject/decodeObjectメソッドは使えないので、それぞれ型毎に用意されたメソッドを使う。

この方法が他の言語の同様の処理と決定的に違うのは、NSDictionaryのように保存するデータを識別するための名前をキーとすることだ。これにより、この手の処理で避けられないメソッドの実行順に依存することを避けることができる。

あとinitWithCoderメソッドはその名前からも分るように、これはObjective-Cのイニシャライザとして扱うメソッドであり、上記の例でも同クラスのイニシャライザを読んでからデータのアンアーカイブを実行している。どんなクラスをアンアーカイブする場合はこのようにイニシャライザをオブジェクトの初期化に使用すべきだ。

Cocoaの基本クラスであるNSArray、NSDictionary、NSString、NSDate、NSNumber、NSData等はNSCodingを実装しており、特にNSArray等のコレクションクラスは格納されているオブジェクトにNSCodingプロトコルメッセージに送出してくれるため、アーカイブ/アンアーカイブが必要なオブジェクトは自身がNSCodingを実装する必要がある。


※1:Core Dataも永続化のための仕組みではあるがO-Rマッピングが主であるため直列化との比較対象とは考えていない
※2:乱暴に言うとObjective-CのプロトコルはJavaC#のインタフェースに相当する