cocoa asynchronous nsoperation nsoperationqueue

cocoa - NSOperation espera hasta que se ejecute el bloque asíncrono



asynchronous nsoperationqueue (2)

necesito poner operaciones asíncronas en una cola de operación, sin embargo, necesitan ejecutarse después de la otra

self.operationQueue = [NSOperationQueue new]; self.operationQueue.maxConcurrentOperationCount = 1; [self.operationQueue addOperationWithBlock:^{ // this is asynchronous [peripheral1 connectWithCompletion:^(NSError *error) { }]; }]; [self.operationQueue addOperationWithBlock:^{ // this is asynchronous [peripheral2 connectWithCompletion:^(NSError *error) { }]; }];

el problema es que, dado que peripheralN connectWithCompletion es asíncrono, la operación en cola finaliza y la siguiente se ejecuta, necesitaría sin embargo simular, que peripheralN connectWithCompletion es síncrono y esperar al final de la operación, hasta que se ejecute el bloque asíncrono

entonces necesitaría un comportamiento como este, solo usando la cola de operaciones

[peripheral1 connectWithCompletion:^(NSError *error) { [peripheral2 connectWithCompletion:^(NSError *error) { }]; }];


NSBlockOperation no puede manejar operaciones asincrónicas, pero no es tan difícil crear una subclase de NSOperation que pueda ...

Básicamente, debe crear una NSOperation que tome un bloque que tome otro bloque como un controlador de finalización. El bloque podría definirse así:

typedef void(^AsyncBlock)(dispatch_block_t completionHandler);

Luego, en el NSOperation de start la subclase NSOperation , debe llamar a su AsyncBlock pasándole un dispatch_block_t que se ejecutará cuando se haya ejecutado. También debe asegurarse de cumplir con NSOperation con las NSOperation isFinished y isExecuting (como mínimo) (consulte Propiedades compatibles con NSOperation en los documentos de NSOperation ); esto es lo que permite a NSOperationQueue esperar hasta que se complete su operación asincrónica.

Algo como esto:

- (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"]; }); }

Tenga en cuenta que _executing y _finished deberán definirse en su subclase en alguna parte, y deberá anular las propiedades isExecuting y isFinished para devolver los valores correctos.

Si junta todo eso, junto con un inicializador que toma su AsyncBlock , entonces puede agregar sus operaciones a la cola de esta manera:

[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(); }]; }];

Puse juntos una esencia de una versión simple de esto aquí: AsyncOperationBlock . Es solo una implementación mínima, pero debería funcionar (sería bueno si se isCancelled donde también se implementa, por ejemplo).

Copiado aquí para completar:

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


Lo que hice fue jugar con [myQueue setSuspended:YES] y [myQueue setSuspended:NO] antes y después, respectivamente.

Por ejemplo:

[myQueue addOperationWithBlock:^{ [myQueue setSuspended:YES]; [someBackendService doSomeAsyncJobWithCompletionBlock:^{ callback(YES, nil); [myQueue setSuspended:NO]; }); }];

El efecto logrado es que la cola se suspende antes de la tarea asincrónica, por lo tanto, aunque se devuelva el bloque de operación, solo comienza la siguiente operación (sujeto a maxConcurrentOperationCount por supuesto) cuando se llama al bloque de finalización de la tarea asincrónica.