Ответ 1
NSBlockOperation
не может обрабатывать асинхронные операции, но не так сложно создать подкласс NSOperation
, который может...
В принципе, вам нужно создать NSOperation
, который принимает блок, который принимает другой блок как обработчик завершения. Блок может быть определен следующим образом:
typedef void(^AsyncBlock)(dispatch_block_t completionHandler);
Затем в вашем методе NSOperation
subclass start
вам нужно вызвать ваш AsyncBlock
, передав ему dispatch_block_t
, который будет вызываться, когда он будет выполнен. Вы также должны быть уверены, что останетесь KVO
совместимым с NSOperation
isFinished
и isExecuting
свойствами (как минимум) (см. KVO-Compliant Properties в документах NSOperation
); это то, что позволяет NSOperationQueue
дождаться завершения асинхронной операции.
Что-то вроде этого:
- (void)start {
[self willChangeValueForKey:@"isExecuting"];
_executing = YES;
[self didChangeValueForKey:@"isExecuting"];
self.block(^{
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
[self didChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_finished = YES;
[self didChangeValueForKey:@"isFinished"];
});
}
Обратите внимание, что _executing
и _finished
нужно будет определить в вашем подклассе где-нибудь, и вам нужно переопределить свойства isExecuting
и isFinished
, чтобы вернуть правильные значения.
Если вы поместите все это вместе с инициализатором, который берет ваш AsyncBlock
, то вы можете добавить свои операции в очередь следующим образом:
[self.operationQueue addOperationWithBlock:^(dispatch_block_t completionHandler) {
[peripheral1 connectWithCompletion:^(NSError *error) {
// call completionHandler when the operation is done
completionHandler();
}];
}];
[self.operationQueue addOperationWithBlock:^(dispatch_block_t completionHandler) {
[peripheral2 connectWithCompletion:^(NSError *error) {
// call completionHandler when the operation is done
completionHandler();
}];
}];
Я собрал суть простой версии этого здесь: AsyncOperationBlock.
Это только минимальная реализация, но она должна работать (было бы неплохо, если isCancelled
, где также реализовано, например).
Скопировано здесь для полноты:
AsyncBlockOperation.h:
#import <Foundation/Foundation.h>
typedef void(^AsyncBlock)(dispatch_block_t completionHandler);
@interface AsyncBlockOperation : NSOperation
@property (nonatomic, readonly, copy) AsyncBlock block;
+ (instancetype)asyncBlockOperationWithBlock:(AsyncBlock)block;
- (instancetype)initWithAsyncBlock:(AsyncBlock)block;
@end
@interface NSOperationQueue (AsyncBlockOperation)
- (void)addAsyncOperationWithBlock:(AsyncBlock)block;
@end
AsyncBlockOperation.m:
#import "AsyncBlockOperation.h"
@interface AsyncBlockOperation () {
BOOL _finished;
BOOL _executing;
}
@property (nonatomic, copy) AsyncBlock block;
@end
@implementation AsyncBlockOperation
+ (instancetype)asyncBlockOperationWithBlock:(AsyncBlock)block {
return [[AsyncBlockOperation alloc] initWithAsyncBlock:block];
}
- (instancetype)initWithAsyncBlock:(AsyncBlock)block {
if (self = [super init]) {
self.block = block;
}
return self;
}
- (void)start {
[self willChangeValueForKey:@"isExecuting"];
_executing = YES;
[self didChangeValueForKey:@"isExecuting"];
self.block(^{
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
[self didChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_finished = YES;
[self didChangeValueForKey:@"isFinished"];
});
}
- (BOOL)isFinished {
return _finished;
}
- (BOOL)isExecuting {
return _executing;
}
- (BOOL)isAsynchronous {
return YES;
}
@end
@implementation NSOperationQueue (AsyncBlockOperation)
- (void)addAsyncOperationWithBlock:(AsyncBlock)block {
[self addOperation:[AsyncBlockOperation asyncBlockOperationWithBlock:block]];
}
@end