带有嵌套完成块的 NSBlockOperation
NSBlockOperation with Nested Completion Block
应该有一个带有完成块的方法来执行:
[container insert:data
completion:^(NSDictionary *result, NSError *error) {
}];
我需要使用 NSOperation
(超过 GCD
分派块,因为我需要更多地控制操作流和取消)来实现并发。
现在,假设要执行一个正常的完成块,我可以使用 NSBlockOperation
like
- (NSOperation *)executeBlock:(void (^)(void))block
inQueue:(NSOperationQueue *)queue
completion:(void (^)(BOOL finished))completion
{
NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
completion(blockOperation.isFinished);
}];
[completionOperation addDependency:blockOperation];
[[NSOperationQueue currentQueue] addOperation:completionOperation];
[queue addOperation:blockOperation];
return blockOperation;
}
所以这样称呼它
[self executeBlock:^{
/// my sync code
} inQueue:operationQueue
completion:^(BOOL finished) {
}];
问题是那里有异步代码:
void (^completionBlock)() = ^void() {
// this is the NSOperation completion block where sync code is executed
};
和
void (^insertCompletionBlock)(NSDictionary *, NSError *) = ^void(NSDictionary *result, NSError *error) {
// this is the insert api completion block
};
所以拥有
[container insert:data completion:insertCompletionBlock];
如果我像这样进行嵌套调用
[self executeBlock:^{
[container insert:data
completion:^(NSDictionary *result, NSError *error) {
}];
} inQueue:operationQueue
completion:^(BOOL finished) {
}];
此 NSOperation
将立即结束,因为 insert:completion:
方法将在其调用后 return 具有完成块。
那么,如何序列化此执行以便在执行 insert:completion:
的嵌套完成块后调用 NSBlockOperation
?
[更新]
使用@Mozilla 的解决方案
我想出了一个自定义的 NSBlockOperation
,我曾经在上面添加了一些属性:
@interface MyCloudOperation: NSBlockOperation
@property(nonatomic,strong) id result;
@property(nonatomic,strong) NSError *error;
@end
@implementation MXMCloudOperation
@end
还有这个
MyCloudOperation *blockOp=[[MyCloudOperation alloc] init];
__weak MXMCloudOperation *weakBlockOp=blockOp;
[blockOp setCompletionBlock:^{
if(completion) completion(weakBlockOp.result,weakBlockOp.error);
}];
[blockOp addExecutionBlock:^{
dispatch_semaphore_t mutex = dispatch_semaphore_create(0);
void (^insertCompletionBlock)(NSDictionary *, NSError *) = ^void(NSDictionary *result, NSError *error) {
if(error) {
weakBlockOp.error=error;
NSLog(@"Error saving to %@ data\n%@", containerName,
error.localizedDescription);
} else {
weakBlockOp.result=result;
NSLog(@"Data %@ sent", result);
}
dispatch_semaphore_signal(mutex);
};
[container insert:data completion:insertCompletionBlock];
dispatch_semaphore_wait(mutex, DISPATCH_TIME_FOREVER);
}];
[operationQueue addOperation:blockOp];
这里我不喜欢的是引用我的 NSBlockOperation
来传递完成处理程序的参数,但我现在没有找到更好的解决方案。
我使用 dispatch_semaphore_t
解决了这个问题。
- (void)saveWebDataInternal:(ResponseModel *)data completion:(void(^)(NSArray *))completion
{
NSBlockOperation *op = [[NSBlockOperation alloc] init];
op.completionBlock = ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self loadCachedDataInternal:completion];
});
};
[op addExecutionBlock:^{
dispatch_semaphore_t mutex = dispatch_semaphore_create(0);
[self.cacheDAO asyncImport:data completion:^{
dispatch_semaphore_signal(mutex);
}];
dispatch_semaphore_wait(mutex, DISPATCH_TIME_FOREVER);
}];
// start operation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[op start];
});
}
应该有一个带有完成块的方法来执行:
[container insert:data
completion:^(NSDictionary *result, NSError *error) {
}];
我需要使用 NSOperation
(超过 GCD
分派块,因为我需要更多地控制操作流和取消)来实现并发。
现在,假设要执行一个正常的完成块,我可以使用 NSBlockOperation
like
- (NSOperation *)executeBlock:(void (^)(void))block
inQueue:(NSOperationQueue *)queue
completion:(void (^)(BOOL finished))completion
{
NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
completion(blockOperation.isFinished);
}];
[completionOperation addDependency:blockOperation];
[[NSOperationQueue currentQueue] addOperation:completionOperation];
[queue addOperation:blockOperation];
return blockOperation;
}
所以这样称呼它
[self executeBlock:^{
/// my sync code
} inQueue:operationQueue
completion:^(BOOL finished) {
}];
问题是那里有异步代码:
void (^completionBlock)() = ^void() {
// this is the NSOperation completion block where sync code is executed
};
和
void (^insertCompletionBlock)(NSDictionary *, NSError *) = ^void(NSDictionary *result, NSError *error) {
// this is the insert api completion block
};
所以拥有
[container insert:data completion:insertCompletionBlock];
如果我像这样进行嵌套调用
[self executeBlock:^{
[container insert:data
completion:^(NSDictionary *result, NSError *error) {
}];
} inQueue:operationQueue
completion:^(BOOL finished) {
}];
此 NSOperation
将立即结束,因为 insert:completion:
方法将在其调用后 return 具有完成块。
那么,如何序列化此执行以便在执行 insert:completion:
的嵌套完成块后调用 NSBlockOperation
?
[更新]
使用@Mozilla 的解决方案
我想出了一个自定义的 NSBlockOperation
,我曾经在上面添加了一些属性:
@interface MyCloudOperation: NSBlockOperation
@property(nonatomic,strong) id result;
@property(nonatomic,strong) NSError *error;
@end
@implementation MXMCloudOperation
@end
还有这个
MyCloudOperation *blockOp=[[MyCloudOperation alloc] init];
__weak MXMCloudOperation *weakBlockOp=blockOp;
[blockOp setCompletionBlock:^{
if(completion) completion(weakBlockOp.result,weakBlockOp.error);
}];
[blockOp addExecutionBlock:^{
dispatch_semaphore_t mutex = dispatch_semaphore_create(0);
void (^insertCompletionBlock)(NSDictionary *, NSError *) = ^void(NSDictionary *result, NSError *error) {
if(error) {
weakBlockOp.error=error;
NSLog(@"Error saving to %@ data\n%@", containerName,
error.localizedDescription);
} else {
weakBlockOp.result=result;
NSLog(@"Data %@ sent", result);
}
dispatch_semaphore_signal(mutex);
};
[container insert:data completion:insertCompletionBlock];
dispatch_semaphore_wait(mutex, DISPATCH_TIME_FOREVER);
}];
[operationQueue addOperation:blockOp];
这里我不喜欢的是引用我的 NSBlockOperation
来传递完成处理程序的参数,但我现在没有找到更好的解决方案。
我使用 dispatch_semaphore_t
解决了这个问题。
- (void)saveWebDataInternal:(ResponseModel *)data completion:(void(^)(NSArray *))completion
{
NSBlockOperation *op = [[NSBlockOperation alloc] init];
op.completionBlock = ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self loadCachedDataInternal:completion];
});
};
[op addExecutionBlock:^{
dispatch_semaphore_t mutex = dispatch_semaphore_create(0);
[self.cacheDAO asyncImport:data completion:^{
dispatch_semaphore_signal(mutex);
}];
dispatch_semaphore_wait(mutex, DISPATCH_TIME_FOREVER);
}];
// start operation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[op start];
});
}