4

特定のas 識別子NSOperationQueueを追加できるように拡張しました。NSBlockOperationNSString

識別子の値はNSMutableArrayレジストリとして保持されます。これがレジストリの実装方法です。

-(void)addOperation:(NSOperation *)operation withID:(NSString*)operationID 
{
    @synchronized(self.queueReference) 
    {
        [self.queueReference addObject:operationID]; // <-- just a mutable array
    }

    [operation setCompletionBlock:^(){
        @synchronized(self.queueReference) {
            [self.queueReference removeObject:operationID];
        }
    }];

    [self addOperation:operation];  
}

基本的に、特定の操作が終了したときにレジストリをクリーニングする完了ブロックを追加しています。

ただし、これは機能しますが、キューにさらに細分性を追加する必要があります。

私はブロック操作でのみキューを使用し、ブロックの実行中に、実行がNSNotificationどのように行われたかに応じて、リスナーに異なるものを送信する場合があります。

私が達成しようとしていたこと:

NSBlockOperation発信者が特定の識別子をキューに追加しようとしました。キューにそのような識別子が既にある場合は、ブロックを追加せず、呼び出し元のクラス自体をリスナーとして設定します。

何が欠けている ?識別子を確認するだけでは十分ではありません。NSBlockOperationすでにディスパッチされているNSNotificationが、完了ブロックがまだ呼び出されていない場合があります。

したがって、呼び出し元クラスはキューに問い合わせます。これは、識別子がレジストリに存在することを示しており、呼び出し元は、既に送信されているために決して到着しない通知をリッスンするように誤って設定されています。

代わりに、シナリオは次のようになります。発信者はキューに問い合わせます。これは、「識別子はレジストリにある」と言っていますが、NSNotification送信されます。そして、呼び出し元がNSBlockOperationキューに入れられました。

レジストリのチェックは、次の簡単な方法で行われます。

-(BOOL)hasOperationWithID:(NSString*)operationID 
{
    @synchronized(self.queueReference) 
    {
        return [self.queueReference containsObject:operationID];
    }
}

しかし、この時点では、そのような方法を拡張する方法についてはあまり考えていません。私が取り組んでいるコードは一種の「学術的」なものであり、特定の目的には役立たず、私が実験しようとしているだけです。したがって、私はコード内で非常に柔軟です。しかし、これは私にとってまったく新しいテーマなので、提案された実装のマイナス面についてできるだけ具体的に教えてください.

4

1 に答える 1

0

現在のシステムには3つの基本的なイベントがあるようです。

  • 操作がキューに追加されます
  • 操作は実行中に通知を送信します
  • 操作完了ブロックが呼び出されます

NSNotificationsキュー自体がブロックによって送信される可能性のあるものを明示的にリッスンしない限り、それらがまだ発生したかどうかを知る方法はありません。しかし、それがリッスンしたとしても、オブザーバーNSNotificationsが呼び出される順序は決定論的ではありません。つまり、キューが通知をリッスンし、そのコールバックをエンキュー/デキュー操作とインターロックしたとしても、別のクライアントがそのリッスンを開始するには遅すぎる可能性があり(最終的には)NSNotification、操作を誤って拒否することになります。

この代替案を検討してください。完了ブロックを使用して識別子リストを管理する代わりに、通知自体を使用します。キューに通知の送信を処理させます。言い換えれば、3番目のイベントを取り除き、通知送信に識別子リストの保守のための二重の義務を持たせましょう。これを行うために私が思いついた最も簡単な方法は次のようになりました。

ヘッダ:

//
//  SONotifyingOperationQueue.h
//  NotifyingOpQueue
//

typedef void (^SOSendNotificationBlock)(NSDictionary* userInfo);
typedef void (^SONotifyingBlock)(SOSendNotificationBlock sendNotificationBlock);

@interface SONotifyingOperationQueue : NSOperationQueue

- (BOOL)addOperationForBlock:(SONotifyingBlock)block withNotificationName:(NSString*)notificationName;

@end

実装

//
//  SONotifyingOperationQueue.m
//  NotifyingOpQueue
//

#import "SONotifyingOperationQueue.h"

@implementation SONotifyingOperationQueue
{
    NSMutableSet* _names;
}

- (BOOL)addOperationForBlock: (SONotifyingBlock)block withNotificationName: (NSString*)notificationName
{
    notificationName = [[notificationName copy] autorelease];

    BOOL shouldAdd = NO;
    @synchronized(self)
    {
        _names = _names ? : [[NSMutableSet alloc] init];

        if (![_names containsObject: notificationName])
        {
            [_names addObject: notificationName];
            shouldAdd = YES;
        }
    }

    if (shouldAdd)
    {
        NSBlockOperation* blockOp = [[[NSBlockOperation alloc] init] autorelease];

        __block SONotifyingOperationQueue* blockSelf = self;

        SOSendNotificationBlock notificationBlock = ^(NSDictionary* userInfo){
            @synchronized(blockSelf)
            {
                [blockSelf->_names removeObject: notificationName];
                // Sending the notification from inside the @synchronized makes it atomic
                // with respect to enqueue operations, meaning there can never be a missed
                // notification that could have been received.
                [[NSNotificationCenter defaultCenter] postNotificationName: notificationName object: blockSelf userInfo: userInfo];
            }
        };

        dispatch_block_t executionBlock = ^{
            block(notificationBlock);
        };

        [blockOp addExecutionBlock: executionBlock];
        [self addOperation: blockOp];
    }

    return shouldAdd;
}

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

@end

このアプローチは、元のアプローチにいくつかの変更を加えます。まず、ここのAPIは、ではなくブロックを追加しますNSOperations。サブクラスでも同じことができますが、NSOperationコードが多くなり、全体的なパターンは変わりません。また、識別子と通知名の概念をマージします。操作が複数の異なるを送信できる場合NSNotifications、これは変更なしでは機能しませんが、全体的なパターンは同じになります。このパターンの重要な機能は、ID /名チェックがそれ自体を送信する通知と連動することです。これにより、誰かが新しいブロック/操作をキューに追加したり、同じID/名前の別の操作を追加したりする場合に強力な保証が提供されます。まだ通知を発行していないため、新しい操作は追加されません。解雇された場合、前のブロックがまだ完了していなくても、追加されます。

ここでオブジェクトを持つNSOperationことがなんらかの重要性を持っている場合は、ここのメソッドに、指定されたブロックに対して作成された操作を返すようにすることもできます。

HTH。

于 2012-12-21T13:53:22.213 に答える