iOS 使用 NSBlockOperation 限制异步 API 调用

标签 ios objective-c asynchronous nsoperationqueue nsblockoperation

我想将飞行中的 API 调用次数限制为 2。我可以创建一个 NSOperationQueue 并将该 block 添加到队列中,但是每个 API 调用都有一个完成 block ,因此初始调用是有限但我不知道如何根据完成 block 的执行来限制队列的处理。

在下面的代码中,随时可能有超过 2 个调用 API。

NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
service.requestQueue.maxConcurrentOperationCount = 2;

for (int i = 0; i < 100; i++)
{
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

        [self invokeAPI:kAPIName completion:^BOOL(APIResult *result) {

            // Do stuff
        }

         [requestQueue addOperation:operation];
    }
}

非常感谢任何指向正确使用模式的指示。

编辑 - 基于 Marc-Alexandre 的回答

已经创建了这个类来封装操作,从内存的角度来看是安全的方法,因为这个类将从 dataAccessService 创建并注入(inject),以及引用自身的完成 block ,以及完成完成 block 执行之前调用?

@interface MAGApiOperation : NSOperation

@property (nonatomic, strong) id<MAGDataAccessServiceProtocol> dataAccessService;
@property (nonatomic, copy) NSString *apiName;
@property (nonatomic, copy) BOOL (^onCompletion)(APIResult *);

+ (instancetype)apiOperationWithName:(NSString *)apiName dataAccessService:(id<MAGDataAccessServiceProtocol>)dataAccessService completion:(BOOL (^)(APIResult *))onCompletion;

@implementation MAGApiOperation

@synthesize executing = _isExecuting;
@synthesize finished = _isFinished;

#pragma mark - Class methods

/// Creates a new instance of MAGApiOperation
+ (instancetype)apiOperationWithName:(NSString *)apiName dataAccessService:(id<MAGDataAccessServiceProtocol>)dataAccessService completion:(BOOL (^)(APIResult *))onCompletion {

    MAGApiOperation *operation = [[self alloc] init];
    operation.apiName = apiName;
    operation.dataAccessService = dataAccessService;
    operation.onCompletion = onCompletion;

    return operation;
}

#pragma mark - NSOperation method overrides

- (void)start {

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    if (!self.isCancelled)
    {
        [self invokeApiWithName:self.apiName completion:self.onCompletion];
    }
}

- (void)finish {

    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];

    _isExecuting = NO;
    _isFinished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

#pragma mark - Private methods

/// Invokes the api with the name then executes the completion block
- (void)invokeApiWithName:(NSString *)apiName completion:(BOOL (^)(VAAInvokeAPIResult *))onCompletion {

        [self.dataAccessService invokeAPI:kAPIName completion:^BOOL(APIResult *result) { {

            [self finish];

            return onCompletion(result);
        }];
}

最佳答案

您需要子类化 NSOperation 才能做到这一点。

这里是完整的文档,解释了如何将 NSOperation 子类化:https://developer.apple.com/reference/foundation/operation

快速笔记:

  • 您的“开始”操作将是 invokeAPI 调用。
  • 然后在您的 invokeAPI 完成 block 中,您将自己的操作标记为已完成(见下文,注意 willChange 和 didChange 调用非常重要)

[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
self.isExecuting = NO;
self.isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];

关于iOS 使用 NSBlockOperation 限制异步 API 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39982899/

相关文章:

iphone - 捏合以增大/缩小按钮的大小

ios - 强制iOS应用程序的数据库升级

iphone - 合并暂定定义时 iOS 对齐丢失

ios - 如何在 Swift 2.0 中获取以分钟为单位的时差

asynchronous - gunicorn不会同时处理并发请求

ios - 在文档目录中下载或移动文件异步 - iOS

ios - UIView 的调试 View 中的 frame 属性在哪里?

iphone - 如何排除连接2个NSArray?

ios - UIButton 图像没有改变

ios - UISearchDisplayController.displaysSearchBarInNavigationBar 将搜索栏定位在窗口中间