2010-12-13

GData オブジェクトを保存する - オフライン機能の実現 #1 (MacBloggerGlass)

今回から、オフライン機能を実現するための作業を開始する。まずは、フィードから構築したオブジェクトの保存と復元について実験する。

GData オブジェクトの保存と復元

MacBloggerGlass では内部データとして GData ライブラリのオブジェクト(以下では GData オブジェクトと呼ぶ)をそのままの形で保持している。モデルオブジェクトで覆うことで、表示のために加工が必要なデータ(ID や日付け)はキャッシュしているものの、記事のタイトルや内容は GData オブジェクトのデータをそのまま使っている。このためオフライン機能を実現するためには、GData オブジェクトの保存と復元が必要だ。

GData オブジェクト自体には Cocoa アプリでそのまま保存、復元できるための仕組み(NSCoding プロトコルの実装)はないが、その代わりに NSXMLDocument として参照することができるように作られている。

NSXMLDocument は NSCoding プロトコルこそ実装していないものの、NSData 化するためのメソッドが用意されており、これを抱えるクラスで NSCoding を実装することは難しくない。

GData オブジェクト (GDataObject とその派生クラスたち)を NSData 化するための手順は以下のようになる:

  1. GData オブジェクトから NSXMLDocument オブジェクトを取り出す。 (XMLDocument メソッド)
  2. 取り出した NSXMLDocument を NSData 化する。 (XMLData メソッド)

また、NSData から GData オブジェクトを復元する手順は以下の通り:

  1. NSData から NSXMLDocument オブジェクトを作る。 (initWithData:options:error: メソッド)
  2. NSXMLDocument からルート要素を取り出す。 (rootElement メソッド)
  3. 取り出したルート要素で GData オブジェクトを初期化する。 (initWithXMLElement:parent: メソッド)

実験的な実装

環境設定パネルのモデルとなっている FeedBlog は内部にブログ一覧のフィード(GDataFeedBlog)を抱えるオブジェクトで、初期化に GDataFeedBlog のオブジェクトを指定する。今回は、このクラスを使って GDataFeedBlog の保存と復元を作り込むことにする。

インタフェース部では NSCodig プロトコルを実装することを宣言する。

@interface FeedBlog : NSObject <NSCoding> {
    NSMutableArray *entries;
    GDataFeedBlog *gDataFeed;
}

実装部では、NSCoding プロトコルの 2 つのメソッドを実装する。

// for conformance to NSCoding protocol
- (id)initWithCoder:(NSCoder *)aDecoder {
    [super init];
    NSData *data = [aDecoder decodeDataObject];
    NSXMLDocument *xmldocument =
    [[NSXMLDocument alloc] initWithData:data
                                options:NSXMLDocumentTidyXML
                                  error:NULL];
    NSXMLElement *xmlroot = [xmldocument rootElement];
    GDataFeedBlog *aFeed =
    [[GDataFeedBlog alloc] initWithXMLElement:xmlroot parent:nil];
    return [self initWithGDataFeed:aFeed];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    NSData *data = [[gDataFeed XMLDocument] XMLData];
    [aCoder encodeDataObject:data];
}

NSCoding プロトコルを実装したオブジェクトは、保存時に NSKeyedArchive を使って NSData 化する。逆に、NSData オブジェクトからの復元には NSKeyedUnarchive を使う。

以下は環境設定パネルのコントローラクラス(PreferenceController)の実装の一部で、FeedBlog を NSData 化したものを User Defaults に保存している。本来、ここに保存するようなデータではないが、とりあえずこれで FeedBlog の保存と復元を確認することができる。

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [...snip...]
    // feed data
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.feed];
    [defaults setObject:data forKey:BGKeyFeedBlogData];

復元時は User Defaults から NSData オブジェクトを取り出し、NSKeyedUnarchive にかけている。

    // load feed blog data
    NSData *data = [self getUserDefaults:BGKeyFeedBlogData];
    @try {
        self.feed = [NSKeyedUnarchiver unarchiveObjectWithData:data];        
    }
    @catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        self.feed = [[[FeedBlog alloc] init] autorelease];
    }
    @finally {
    }

次の一手

保存と復元の仕組みはわかったので、次はアプリとして、どこに、どのような形式でデータを書き出すかを考えなくてはならない。

MacBloggerGlass は、Cocoa のドキュメントアーキテクチャを使ったアプリではない。フィードを XML として書き出すことを選んだとしても Finder からそれをダブルクリックしてアプリを開くというタイプのものではない。ユーザ体験から見たアプリのタイプとしては、Mail.app に近いものを想定している。~/Library の下にアプリ専用のフォルダを作って、そこにブログごとのフィードデータを置くというような方式になるか。

書き出しの形式に関しては、まずは XML (のテキスト)で良いだろう。パフォーマンス上の問題が出てこない限り、人が読める形にしておくとデバッグをふくめて、何かと便利だから。ただ、データをブログごとに分けるのは当然として、同じブログのすべての記事を 1 つにまとめるのか、それとも一定の数で分割するのかについては検討する必要がある。

参考文献

Cocoa Programming for Mac OS X
Aaron Pablo Hillegass
Addison-Wesley Professional ( 2008-05-15 )
ISBN: 9780321503619

Chapter 10 Archiving では NSCoder を使ったオブジェクトの保存と復元のサンプルが載っている。

Objective-C逆引きハンドブック
林 晃
シーアンドアール研究所 ( 2010-02-26 )
ISBN: 9784863540514

CHAPTER 10 に XML の操作に関する項目が載っている。NSXMLDocument の NSData 化は 187、ルート要素の取り出しは 188。

関連リンク

関連記事

0 件のコメント:

コメントを投稿