4

最近、特定のオブジェクトのインスタンスを 1 つだけ存在させ、特定の操作を実行するために必要な短い期間だけ存在させたいという問題に遭遇しました。その操作は非同期だったので、参照を保持していない場合、ARC は実行ループの最後で割り当てを解除します。私がそれに固執した場合、それを解放するためにいつ完了したかを知るために、デリゲートのコールバックまたは通知が必要になります。

このオブジェクトは、いくつかの画像やその他のデータをダウンロードしてディスクにキャッシュする必要がありました。キャッシュの制限は約 24 時間だったので、アイテムをキャッシュしていないときにメモリを浪費したくありませんでした。また、そこからのフィードバックは一切必要ありませんでした。私はそれがそのタスクを実行し、それ自体で完了することを望んでいました.

とても気に入ったデザインパターンを思いつきました。それ以来、私は他のいくつかのプロジェクトでそれを使用してきましたが、それがよく知られており、私が気付いていない分析されたパターン (自己破壊シングルトン???) であった場合、好奇心旺盛でした。現在目にしていない潜在的な落とし穴を認識できるように、知りたいです。

また、これが悪い設計である理由について、皆さんからの意見を聞くことにも非常に興味があります。

設計は次のようになります (これは ARC ですが、クラス メソッドを介してシングルトンを解放すると、非アークも機能します)。

グローバル静的オブジェクト (ずっと生きているわけではないため、実際にはシングルトンではありません)

    static MySelfDestructingClass* singleton;

単一のパブリック クラス メソッド

    + (void)downloadAndCacheDataIfNeeded
     {
        //Force synchronized access
        @synchronized(singleton){
            //We are already doing something, return
            if(singleton){
             return;
            }
             NSDate* lastCacheDate = [[NSUserDefaults standardDefaults] objectForKey:kKeyForLastUpdate];
            if([[NSDate date] timeIntervalSinceDate:lastCacheDate] > kCacheLimit){
              //Our cache is out of date, we need to update
                singleton = [[self alloc] init];
                [singleton downloadAndCache];
             }
        }
     }

インスタンス メソッドでは、リクエストが返されるように、オブジェクトを有効にする必要があります。

      - (void)downloadAndCache
        {
               //This would probably be a delegate, but for simplicity of this example it's a notification
               [[NSNotificationCenter defaultCenter] addObserver:self forNotificationWithName:NotificationSomeRequestDidSucceed selector:@selector(someCustomRequestDidSucceed:withData:) object:nil];
               [SomeCustomRequest makeRequestWithURL:@"http://www.someURL.com"];

        }

      - (void)someCustomRequestDidSucceed:(SomeCustomRequest *)request withData:(NSDictionary *)dictionary
        {

             //Do whatever we need to in order to save our data, or fire off image download requests etc...
             ....


            //Set our lastUpdated time in NSUserDefaults
            [[NSUserDefaults standardDefaults] setObject:[NSDate date] forKey:kKeyForLastUpdate];

            //Remove our observer
            [NSNotificationCenter defaultCenter] removeObserver:self name:NotificationSomeRequestDidSucceed object:nil];

            //Release ourselves (ok not really, but tell arc we can be released)
            singleton = nil;
        } 

このようにして、アプリケーションの他の場所で行う必要があるのは次のとおりです。

     [MySelfDestructingClass downloadAndCacheDataIfNeeded];

このオブジェクトは、必要に応じてダウンロードし、完了したら解放するか、まったく作成しません。また、データのダウンロードを 2 回開始することもありません。

この設計には拡張性と機能に制限があることは承知していますが、このような例や、私がこれを使用した他の例では、非常に便利であることがわかりました.

4

1 に答える 1

1

これはかなり一般的なブロックの使用です。似たようなことを考えてみてください(おそらく、複数の呼び出しを異なる方法で処理するでしょう...)

void ExecuteWithMySingleSelfDestructingObject(void(^block)(MySelfDestructingClass *object)) {
    static MySelfDestructingClass* singleton;
    @synchronized(singleton) {
        if (singleton) {
          // To get past the synchronization primitive, this must be a recursive call.
        }
        // Whatever other conditions you want to have (like your date check)
        singleton = [[MySelfDestructingClass] alloc] init];
        @try { block(singleton); }
        @finally { singleton = nil; }
    }
}

二重の例外処理に注意してください (try/finally と @synchronized が行うこと - 変更したい場合があります...

次に、ブロックでやりたいことを何でもします...

ExecuteWithMySingleSelfDestructingObject(^(MySelfDestructingClass *object){
    // Do whatever I want with the singleton instance that has
    // been given to me as <object>
});

もちろん、それはクラスメソッドかもしれません...

+ (void)performBlock:(void(^)(MySelfDestructingClass *object))block {
    static MySelfDestructingClass* singleton;
    @synchronized(singleton) {
        if (singleton) {
          // To get past the synchronization primitive, this must be a recursive call.
        }
        // Whatever other conditions you want to have (like your date check)
        singleton = [[self] alloc] init];
        @try { block(singleton); }
        @finally { singleton = nil; }
    }
}

[MySelfDestructingClass performBlock:^(MySelfDestructingClass *object){
    // Do whatever I want with the singleton instance that has
    // been given to me as <object>
}];

それが理にかなっていることを願っています (フリーハンドで入力したため、構文は異なる場合がありますが、理解できるはずです)。

于 2012-05-18T01:42:45.823 に答える