Hatena::Groupiphone-dev

iPhoneアプリ開発まっしぐら★ このページをアンテナに追加 RSSフィード

引っ越し後の日記はコチラです

tokoromのその他の日記

2009-05-22

NSUserDefaultsはどこまで保存できるかチェック(2)

16:45 | はてなブックマーク - NSUserDefaultsはどこまで保存できるかチェック(2) - iPhoneアプリ開発まっしぐら★

前エントリからの引き続きです。


NSArrayの入れ子

前回、NSArrayの保存/読み込みを試しましたが、今回はそのNSArrayの要素としてさらにNSArrayを入れられるかどうかを試します。

書き込み
  // NSArrayの入れ子(3段)
  NSMutableArray* array1 = [NSMutableArray array];
  NSMutableArray* array2 = [NSMutableArray array];
  NSMutableArray* array3 = [NSMutableArray array];
  [array3 addObject:@"array3-1"];
  [array3 addObject:@"array3-2"];
  [array2 addObject:@"array2-1"];
  [array2 addObject:@"array2-2"];
  [array2 addObject:array3];
  [array1 addObject:@"array1-1"];
  [array1 addObject:array2];
  [defaults setObject:array1 forKey:@"ARRAYINARRAY"];

  if ( ![defaults synchronize] ) {
    NSLog( @"failed ..." );
  }
読み込み
  // NSArrayの入れ子の読み込み試験(記事用にベタ書きにします)
  NSArray* array1 = [defaults arrayForKey:@"ARRAYINARRAY"];
  for ( id object in array1 ) {
    if ( [object isKindOfClass:[NSArray class]] ) {
      for ( id object2 in object ) {
        if ( [object2 isKindOfClass:[NSArray class]] ) {
          for ( NSString* object3 in object2 ) {
            NSLog( object3 );
          }
        } else {
          NSLog( object2 );
        }
    }
    } else {
      NSLog( object );
    }
  }
実行結果
array1-1
array2-1
array2-2
array3-1
array3-2

まったく問題ないようです。

もちろんNSArrayの中にNSDateやNSNumber等を格納可能です。



独自Class

NSCodingプロトコルを実装したClassであれば自分で作成したClassも保存可能とのことです。

※正確に言えばNSCodingプロトコルを実装したClassアーカイブしてNSDataに変換することができるので、そのNSDataを保存しておく。

書き込み
  // 独自Classの保存
  TestClass* test = [[[TestClass alloc] init] autorelease];
  test.title = @"title";
  test.body = @"body";
  NSData* classData = [NSKeyedArchiver archivedDataWithRootObject:test];
  [defaults setObject:classData forKey:@"CLASS"];

  if ( ![defaults synchronize] ) {
    NSLog( @"failed ..." );
  }
読み込み
  // Classの読み込み試験
  NSData* classData = [defaults dataForKey:@"CLASS"];
  TestClass* test = [NSKeyedUnarchiver unarchiveObjectWithData:classData];
  NSLog( test.title );
  NSLog( test.body );
実行結果
title
body

できてますね。

とは言ってもこれを実現しているのはNSCodingプロトコルを実装している部分なのですが…

上記のTestClassのインターフェース/実装は以下のとおりです。

TestClass.h
@interface TestClass : NSObject <NSCoding>
{
 @private
  NSString* title_;
  NSString* body_;
}

@property(nonatomic, copy) NSString* title;
@property(nonatomic, copy) NSString* body;

@end
TestClass.m
#import "TestClass.h"

@implementation TestClass

@synthesize title = title_;
@synthesize body = body_;

- (void)dealloc {
  [title_ release];  
  [body_ release];  
  [super dealloc];
}

- (void)encodeWithCoder:(NSCoder*)coder {
  [coder encodeObject:self.title forKey:@"TITLE"];
  [coder encodeObject:self.body forKey:@"BODY"];
}

- (id)initWithCoder:(NSCoder*)coder {
  if ( (self = [super init]) ) {
    self.title = [coder decodeObjectForKey:@"TITLE"];
    self.body = [coder decodeObjectForKey:@"BODY"];
  }
  return self;
}

@end

ちょっと面倒ではありますが、encodeWithCoderとinitWithCoderを実装しさえすれば問題なく保存/読み込み可能です。


独自Classの入れ子

それでは、メンバ変数に独自Classを持つ独自Classも保存できるでしょうか。

書き込み
  // 独自Classの入れ子
  TestClass* owner = [[[TestClass alloc] init] autorelease];
  owner.title = @"owner's title";
  owner.body = @"owner's body";
  owner.child = [[[TestClass alloc] init] autorelease];
  owner.child.title = @"child's title";
  owner.child.body = @"child's body";
  NSData* classData2 = [NSKeyedArchiver archivedDataWithRootObject:owner];
  [defaults setObject:classData2 forKey:@"CLASS2"];

  if ( ![defaults synchronize] ) {
    NSLog( @"failed ..." );
  }
読み込み
  // Classの入れ子読み込み試験
  NSData* classData2 = [defaults dataForKey:@"CLASS2"];
  TestClass* owner = [NSKeyedUnarchiver unarchiveObjectWithData:classData2];
  NSLog( owner.title );
  NSLog( owner.body );
  NSLog( owner.child.title );
  NSLog( owner.child.body );
実行結果
owner's title
owner's body
child's title
child's body

こちらも問題なくできます。

childを加えたTestClassのインターフェース/実装は以下のとおりです。

TestClass.h
@interface TestClass : NSObject <NSCoding>
{
 @private
  NSString* title_;
  NSString* body_;
  TestClass* child_;
}

@property(nonatomic, copy) NSString* title;
@property(nonatomic, copy) NSString* body;
@property(nonatomic, retain) TestClass* child;

@end
TestClass.m
#import "TestClass.h"

@implementation TestClass

@synthesize title = title_;
@synthesize body = body_;
@synthesize child = child_;

- (void)dealloc {
  [child_ release];  
  [title_ release];  
  [body_ release];  
  [super dealloc];
}

- (void)encodeWithCoder:(NSCoder*)coder {
  [coder encodeObject:self.title forKey:@"TITLE"];
  [coder encodeObject:self.body forKey:@"BODY"];
  if ( self.child ) {
    NSData* childData = [NSKeyedArchiver archivedDataWithRootObject:self.child];
    [coder encodeObject:childData forKey:@"CHILD"];
  } 
}

- (id)initWithCoder:(NSCoder*)coder {
  if ( (self = [super init]) ) {
    self.title = [coder decodeObjectForKey:@"TITLE"];
    self.body = [coder decodeObjectForKey:@"BODY"];
    NSData* childData = [coder decodeObjectForKey:@"CHILD"];
    if ( childData ) {
      [child_ release];
      self.child = [NSKeyedUnarchiver unarchiveObjectWithData:childData];
    } 
  }
  return self;
}

@end

けっきょくのところ、encodeWithCoderの中で自分でアーカイブさえしてやれば、このようなClassの入れ子に関わらずなんでも有りってことですよね。

ゲスト