2014-11-12 16 views
14

muszę umieścić operacji asynchronicznych w kolejce pracy, jednak muszą one wykonywać się po drugiejNSOperation czekać aż asynchronicznego bloku wykonuje

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) { 

    }]; 

}]; 

jest problem, ponieważ peripheralN connectWithCompletion jest asynchroniczny, dane działanie kolejka została zakończona, a następna jest wykonywana, musiałbym jednak symulować, że peryferyjne połączenie connectWithCompletion jest synchroniczne i czekać z końcem operacji, do momentu, w którym asynchroniczny blok wykonuje , więc potrzebowałbym takiego zachowania, tylko przy użyciu operacji kolejka

[peripheral1 connectWithCompletion:^(NSError *error) { 

      [peripheral2 connectWithCompletion:^(NSError *error) { 

      }]; 

    }]; 

Odpowiedz

19

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 
+0

Czy nie zerwie się z AppStore? jako _executing są prywatne API? –

+0

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. –

+0

Bardzo ładne i użyteczne – Yossi

3

Co zrobiłem było grać z [myQueue setSuspended:YES] i [myQueue setSuspended:NO] przed i po odpowiednio.

Na przykład:

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

Uzyskany efekt jest taki, że kolejki zawieszone przed zadaniem asynchronicznym więc nawet chociaż blok operacja jest zwrócony tylko rozpoczyna się następną operację (przedmiotem maxConcurrentOperationCount oczywiście) gdy zostanie wywołany blok zakończenia zadania asynchronicznego.

+1

To jest świetne rozwiązanie. Chciałem tylko docenić twoje wysiłki. Dzięki! – Elitecoder

Powiązane problemy