絶品ゆどうふのタレ

ふと気づいたことを綴るだけのメモ

独自のオブジェクトをシリアライズできるようにする。

久しぶりに扱ってはまった。調べ直したのでメモっておく。


例えば、NSUserDefaultやSQLiteを使ってデータを保存したいとき、独自に作成したオブジェクトだとそのままでは保存できない。
それらのデータストアはNSStringやNSArray、NSDataなど特定のオブジェクト以外はデータを保持しておいてくれないので、そういう時には独自オブジェクトにNSCodingインターフェースを実装して、アーカイバを使ってNSDataにシリアライズしてから保存するようにする。


保存対象のオブジェクト:

@interface MyObject : NSObject<NSCoding> {
  NSString *aString;
  NSString *bString;
}
@end

@implementation MyObject
// ...
// encodeWithCoderとinitWithCoderを実装
- (void) encodeWithCoder:(NSCoder *)coder {
  // シリアライズ時に呼び出される。
  [coder encodeObject:aString forKey:@"aString"];
  [coder encodeObject:bString forKey:@"bString"];
}

- (id) initWithCoder:(NSCoder *)coder {
  // デシリアライズ時に呼び出されるイニシャライザ
  if (self = [super init]) {
    // オブジェクトの生成ではないので、retainを忘れないように。
    aString = [[coder decodeObjectForKey:@"aString"] retain];
    bString = [[coder decodeObjectForKey:@"bString"] retain];
  }
  return self;
}
// ...
@end

こんな感じで実装しておく。
それで、使い方は以下の通り。例えばNSUserDefaultsを使う場合。

// 配列の中にぶち込んであっても大丈夫。
NSArray *array = [NSArray arrayWithObjects:
                             [[MyObject allow] init],
                             @"hogehoge",
                             nil
                 ];
// NSKeyedArchierでシリアライズ
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:array];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"array"];

// NSKeyedUnarchiverで、デシリアライズ
NSData *deData = [[NSUserDefaults standardUserDefaults] objectForKey:@"array"];
NSArray *deArray = [NSKeyedUnarchiver unarchiveObjectWithData:];

for (id myObj in deArray) {
  NSLog(@"%@", [myObj className]); // MyObject, NSStringと出る。
}

ってな感じで、シリアライズとでシリアライズして保存に使える。NSArrayごといけちゃうので、便利。