iOS регулирует вызовы асинхронного API с помощью NSBlockOperation

Я хочу ограничить количество вызовов API во время полета до 2. Я могу создать NSOperationQueue и добавить блок в очередь, однако каждый вызов API имеет блок завершения, поэтому первоначальные вызовы ограничены, но я не знаю, как ограничить обработка очереди на основе выполнения блока завершения.

В приведенном ниже коде возможно одновременное выполнение более двух вызовов 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];
    }
}

Буду признателен за любые указатели на правильный шаблон для использования.

EDIT - на основе ответа Марка-Александра

Создали этот класс для инкапсуляции операций, является ли этот подход безопасным с точки зрения памяти, учитывая, что этот класс будет создан из dataAccessService и введен, а также блок завершения, имеющий ссылки на себя, и конец, называемый до выполнения блока завершения?

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

person Magic Bullet Dave    schedule 11.10.2016    source источник
comment
Судя по вашему коду, он не будет иметь более 2 параллельных вызовов API в любое время.   -  person Ruchira Randana    schedule 12.10.2016
comment
У NSOperation уже есть завершениеBlock, которое вы можете использовать. вам не нужно реализовывать свои собственные. Вместо этого просто сохраните результат в @property, а затем при вызове операции сделайте следующее: myOperation.completionBlock = ^{ /* Здесь у вас есть доступ к myOperation.results */ }. Имеет смысл?   -  person Marc-Alexandre Bérubé    schedule 12.10.2016


Ответы (1)


Для этого вам нужно создать подкласс NSOperation.

Вот полная документация, которая объясняет все, как создать подкласс NSOperation: https://developer.apple.com/reference/foundation/operation

Быстрые заметки:

  • Вашей операцией "старт" будет вызов invokeAPI.
  • Затем в блоке завершения invokeAPI вы отмечаете свою операцию как завершенную (см. ниже, обратите внимание, что вызовы willChange и didChange действительно важны)

[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
self.isExecuting = NO;
self.isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
person Marc-Alexandre Bérubé    schedule 11.10.2016
comment
Спасибо, Марк, ценю это. Изменили вопрос, включив в него подкласс NSOperation, будем признательны за любые комментарии относительно возможных утечек памяти или плохого доступа. - person Magic Bullet Dave; 12.10.2016