我对NSOperationQueue进行了扩展,允许添加带有特定NSString作为标识符的NSBlockOperation。
标识符值保存在用作注册表的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但尚未调用完成块的情况。
因此,caller类询问队列,队列表明标识符存在于注册表中,然后caller错误地将自己设置为侦听永远不会到达的通知,因为它已经被发送了。
取而代之的情况是:调用者询问队列,队列显示'identifier在注册表中‘,但发送的是NSNotification。调用者将NSBlockOperation放入队列。
注册表的检查是通过一个简单的方法进行的:
-(BOOL)hasOperationWithID:(NSString*)operationID
{
@synchronized(self.queueReference)
{
return [self.queueReference containsObject:operationID];
}
}但在这一点上,我没有太多关于如何扩展这种方法的想法。我正在编写的代码是一种“学术”性质的代码,它没有什么特别的目的,它只是我在尝试实验。因此,我在代码中有很大的灵活性。但这对我来说是一个非常新的主题,所以请尽可能详细地说明建议实现的任何缺点。
发布于 2012-12-21 21:53:22
看起来您当前的系统有三个基本事件:
在queue
除非队列本身显式侦听可能由块发送的任何NSNotifications,否则它无法知道它们是否已经发生。但是,即使它监听,调用NSNotifications的观察者的顺序也是不确定的。换句话说,即使队列侦听通知并将其回调与入队/出队操作互锁,对于另一个客户端开始侦听该NSNotification来说,可能(并且最终会)仍然太晚,您可能会错误地拒绝某个操作。
考虑以下替代方案:不使用完成块来管理标识符列表,而是使用通知本身--让队列处理发送通知。换句话说,让我们去掉第三个事件,让通知发送对标识符列表的维护有双重作用。我想出的最简单的方法是:
标题:
//
// 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/name检查现在与通知发送本身互锁,这提供了一个强有力的保证,即如果有人向队列中添加新的块/操作,而具有相同id/name的另一个操作尚未触发其通知,则不会添加新操作,但如果通知已经触发,则会添加新操作,即使前面的块尚未完成。
如果在这里拥有NSOperation对象很重要,那么您也可以让这里的方法返回它为所提供的块创建的操作。
HTH。
https://stackoverflow.com/questions/13950349
复制相似问题