NSBlockOperation
nie może obsługiwać operacje asynchroniczne, ale to nie wszystko, że trudno utworzyć podklasę NSOperation
które mogą ...
Zasadniczo trzeba utworzyć NSOperation
która przyjmuje blok, który zaczyna inny blok jako program obsługi zakończenia. Blok może być zdefiniowana tak:
typedef void(^AsyncBlock)(dispatch_block_t completionHandler);
Następnie w start
metody Twojego NSOperation
podklas, trzeba zadzwonić do AsyncBlock
przekazując mu dispatch_block_t
która zostanie wywołana, kiedy to zrobić wykonywania. Musisz także zachować zgodność z wymaganiami: KVO
z atrybutami NSOperation
o wartości: isFinished
i isExecuting
(co najmniej) (patrz KVO-Compliant Properties w dokumentach NSOperation
); to właśnie pozwala NSOperationQueue
czekać, aż zakończy się operacja asynchroniczna.
coś takiego:
- (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"];
});
}
Zauważ, że _executing
i _finished
będą musiały zostać zdefiniowane w podklasie gdzieś, i trzeba zastąpić isExecuting
i isFinished
właściwości powrotu prawidłowych wartości.
Jeśli umieścisz to wszystko razem, wraz z inicjatora, który bierze swój AsyncBlock
, to można dodać swoje operacje do kolejki jak ta:
[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();
}];
}];
ułożyła sens prosta wersja tego tutaj: AsyncOperationBlock. To tylko minimalna implementacja, ale powinna zadziałać (byłoby miło, gdyby isCancelled
gdzie również zaimplementowane, na przykład).
Skopiowane tutaj dla kompletności:
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
Czy nie zerwie się z AppStore? jako _executing są prywatne API? –
Przykro mi, nie bardzo wiedziałem o tej części ... '_executing' i' _finished' to twoja własna ivars w tym przypadku. Dodałem kompletną implementację do odpowiedzi, aby zobaczyć, jak to wszystko działa. –
Bardzo ładne i użyteczne – Yossi