2010-12-15

FeedManager の実装 - オフライン機能の実現 #3 (MacBloggerGlass)

前回の記事で書いたように(→「オフライン機能の実現 #2」)、フィードデータの保存、読み込みは、FeedManager と呼ぶ管理用のクラスを作り、ネットワークからのデータの取得と合わせて、その詳細を隠すことに決めた。

今日は、その FeedManager を作っている。残念ながら FeedManager はまだ完成していない。今回は、この実装の過程で気付いた注意点を 2 点ほど書き留めておく。

シングルトン・パターン

FeedManager にはシングルトン・パターンを使う。Cocoa でシングルトン・パターンを使うには少し注意が必要だ。簡単に言うと、NSObject で定義ずみのいくつかのメソッドを上書きしなければならない。また、シングルトン・パターンには付きもののファクトリーメソッドの定義にも Cocoa 独特の「お作法」がある。詳しくは公式ドキュメントの Cocoa Fundamentals Guide: Cocoa Objects: Creating a Singleton Instance を参照のこと。

この「Creating a Singleton Instance」には、シングルトン・パターンを実装するための実例が載っていて、そのまま流用できる。以下が、FeedManager 用に書き直したもの。sharedManager の戻り値以外は、実例そのままになっている。

+ (FeedManager *)sharedManager {
    if (sharedFeedManager == nil) {
        sharedFeedManager = [[super allocWithZone:NULL] init];
    }
    return sharedFeedManager;
}

+ (id)allocWithZone:(NSZone *)zone {
    return [[self sharedManager] retain];
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

- (void)release {
    // do nothing
}

- (id)autorelease {
    return self;
}

コールバック方式への対応

GData ライブラリではフィードデータの取得は非同期に実行される。このため、アプリ(のコントローラ)は、コールバックのメソッドを呼び出してもらうことでその完了を知る。データ取得が非同期に実行されるということは、取得したデータからオブジェクトを生成して返す GetFeedBlog のような関数を定義できないことを意味する。

@implementation FeedManager
- (void)trigger {
    [...snip...]
    FeedBlog *feedBlog = GetFeedBlog(account, password);
    [...snip...]
}
@end

非同期方式の場合は、取得開始のトリガーとなるコードと、取得したデータを受け取るコードの間で、引数と戻り値を使った直接的な情報の交換(通信)ができない。これを解決するための方法は 2 つある。1 つは、トリガーと受け取りのコードを同一のオブジェクトのメソッドとして実装し、オブジェクトのインスタンス変数を介して情報のやり取りを行うもの。以下のような感じのコードになる。

@interface FeedManger : NSObject {
    FeedBlog *feedBlog;
}
@end

@implementation FeedManager
- (void)trigger {
    Service *service = [...snip...]
    [service fetch];
    [...snip...]
}

- (void)callback:(id)dataObject {
    feedBlog = [[FeedBlog alloc] initWithDataObject:dataObject];
}
@end

GData ライブラリの GDataServiceBase クラスでは、上記とは別の方法をサポートしている。それは、サービス(フィードデータの取得)がコールバックに対して送り返すデータの一部として、クライアント(サービスを利用するコード)がユーザデータとして任意のオブジェクトを添付する仕組みだ。具体的には GDataServiceBase のインスタンスメソッド setServiceUserData だ。

GDataServiceBase に setServiceUserData でわたされたオブジェクトは、その後の同サービスオブジェクトに対するフィード取得のリクエストを処理する中で、コールバックに引数としてわたすオブジェクト(GDataServiceTicket)に封入される。コールバックメソッドでは、GDataServiceTicket の userData として、トリガーコードが添付したオブジェクトを受け取ることができる。おおよそ、こういう感じ(↓)。

@implementation FeedManager
- (void)trigger {
    GDataServiceBase *service = [...snip...]
    id userData = /* an arbitrary object */;
    [service setServiceUserData:userData];
    [service fetchFeedWithURL:[...snip...]]n;
}

- (void)callback:(GDataServiceTicket *)ticket [...snip...]] {
    id userData = [ticket userData];
    [...snip...]
}

この userData として、フィードデータ(から生成したオブジェクト)を受け取るオブジェクト(レシーバー)を添付すれば、callback の中でレシーバーにフィードデータをわたすことができる(レシーバーのメソッド経由で)。

FeedManager ではフィードデータから生成されるオブジェクトを保持するインスタンス変数を別の目的(オブジェクトが生成ずみかどうかの判別)で使っているため、GDataServiceBase (のサブクラス)に userData を添付する方法を採用している。

関連リンク

関連記事

0 件のコメント:

コメントを投稿