NSOperation等待异步块执行

Pet*_*isu 17 cocoa asynchronous nsoperation nsoperationqueue

我需要将异步操作放入操作队列,但是,它们需要在另一个之后执行

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

    }];

}];
Run Code Online (Sandbox Code Playgroud)

问题是,由于peripheralN connectWithCompletion是异步的,队列中的操作结束而下一个执行,我需要模拟,peripheralN connectWithCompletion是同步的并等待操作结束,直到异步块执行

所以我需要这样的行为,只使用操作队列

    [peripheral1 connectWithCompletion:^(NSError *error) {

            [peripheral2 connectWithCompletion:^(NSError *error) {

            }];

    }];
Run Code Online (Sandbox Code Playgroud)

Mik*_*e S 24

NSBlockOperation无法处理异步操作,但创建一个子类并不是那么难NSOperation......

基本上,您需要创建一个NSOperation带有一个块的块,该块将另一个块作为完成处理程序.该块可以像这样定义:

typedef void(^AsyncBlock)(dispatch_block_t completionHandler);
Run Code Online (Sandbox Code Playgroud)

然后,在你的NSOperation子类的start方法中,你需要调用你的AsyncBlock传递它将dispatch_block_t在执行完成时调用它.你还需要确保留KVO兼容NSOperationisFinishedisExecuting特性(至少)(见KVO兼容属性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"];
    });
}
Run Code Online (Sandbox Code Playgroud)

请注意,_executing并且_finished需要在某个子类中定义,并且您需要覆盖isExecutingisFinished属性以返回正确的值.

如果将所有这些放在一起,以及带有你的初始化程序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();
    }];
}];
Run Code Online (Sandbox Code Playgroud)

我在这里整理了一个简单版本的要点: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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


mll*_*llm 9

我做的是分别[myQueue setSuspended:YES][myQueue setSuspended:NO]之前和之后一起玩.

例如:

[myQueue addOperationWithBlock:^{
    [myQueue setSuspended:YES];
    [someBackendService doSomeAsyncJobWithCompletionBlock:^{
        callback(YES, nil);
        [myQueue setSuspended:NO];
    });
}];
Run Code Online (Sandbox Code Playgroud)

实现的效果是队列在异步任务之前暂停,因此即使返回操作块,它也只会在maxConcurrentOperationCount调用异步任务的完成块时启动下一个操作(当然要受制于此).