Как предотвратить отправку группы, чтобы не вылетать? - PullRequest
1 голос
/ 26 мая 2019

Я использую приведенный ниже код для ожидания выполнения асинхронных задач. Работает пару раз и вылетает. updateFromTable всегда вызывает callback(), так что групповые вызовы сбалансированы, но все равно происходит сбой.

- (void)updateFromTable:(Table *)table env:(Env *)env callback:(void (^)(void))callback {
    [someasync usingBlock:^{
        callback()
    }];
}

- (NSString * _Nullable)process {
    JSL * __weak weakSelf = self;
    NSString __block *ret = nil;
    dispatch_group_enter(_dispatchGroup);
    dispatch_async(_repQueue, ^{
        JSL *this = weakSelf;
        [this updateFromTable:[this->_env table] env:this->_env callback:^{
            ret = [some op .. ];
            dispatch_group_leave(this->_dispatchGroup);
        }];
    });
    dispatch_group_wait(_dispatchGroup, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC));
    info(@"%@", @"done");
    return ret;
}

Есть идеи, почему это происходит случайным образом и как это исправить? По сути, я пытаюсь выполнить несколько асинхронных задач, дождаться их завершения и продолжить выполнение остальных.


Ссылаясь: Как подождать dispatch_async, прежде чем продолжить?

1 Ответ

1 голос
/ 26 мая 2019

Вы не можете разыменовать ivars с ->, если this равно nil. Таким образом, типичное решение - создать надежную ссылку, которую нельзя освободить во время выполнения закрытия, и return, если nil:

- (NSString * _Nullable)process {
    typeof(self) __weak weakSelf = self;
    [self asynchronousMethodWithCompletion:^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        // can now safely use `strongSelf` here
    });

    ...
}

Это «weakSelf - strongSelf танец». Вы используете его в ситуациях, когда вам нужно убедиться, что self не nil, когда вы его используете, например, разыменование иваров (strongSelf->ivar).

Таким образом:

- (NSString * _Nullable)process {
    typeof(self) __weak weakSelf = self;
    NSString __block *ret = nil;
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(_repQueue, ^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        [strongSelf updateFromTable:[strongSelf->_env table] env:strongSelf->_env callback:^{
            ret = [some op .. ];
            dispatch_group_leave(group);
        }];
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    info(@"%@", @"done");
    return ret;
}

Несколько других наблюдений:

  • Группа отправки должна быть локальной переменной метода, а не ivar. В вашем коде больше ничего не нужно ссылаться на этот group.

  • Убедитесь, что ваши dispatch_group_leave вызовы не превышают количество dispatch_group_enter вызовов (т. Е. Этот блок обработчика завершения не вызывается несколько раз).

  • Я бы предложил подождать DISPATCH_TIME_FOREVER (при условии, что вы действительно хотите дождаться его завершения).

  • Кроме того, если это свойства (которые, как я предполагаю, основаны на подчеркивании), то использование self.env вместо self->_env безопаснее, поскольку не произойдет сбой, если self is nil, а скорее просто вернет nil.

Я должен признаться, что это все еще не выглядит правильным (например, если updateFromTable уже асинхронный, зачем беспокоиться об отправке этого асинхронно в _repQueue; если это синхронно, то опять же, зачем отправлять это асинхронно только для ожидания Это). Но невозможно комментировать дальше, не увидев реализацию updateFromTable.


Или, лучше, сделать метод асинхронным:

- (void)processWithCompletion:(void (^)(NSString *))callback {
    typeof(self) __weak weakSelf = self;
    dispatch_async(_repQueue, ^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        [strongSelf updateFromTable:[strongSelf->_env table] env:strongSelf->_env callback:^{
            NSString *ret = [some op .. ];
            callback(ret);
        }];
    });
}
...