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

Моя функция:

- (void)myFunction {

    NSString *myString = @"Hello world";

    dispatch_group_t group = dispatch_group_create();

    NSLog(@"1 entering the dispatch group");

    dispatch_group_enter(group);
    [self doSomething:myString completion:^{
        dispatch_group_leave(group);
        NSLog(@"2 we have left the dispatch group");
    }];
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"3 notifying that the dispatch group is finished");
    }];
    NSLog(@"4 all process are complete, we are done");
}

Вывод я хочу через лог операторов = 1,2,3,4

Вывод получаю через лог операторов = 1,4,2,3

Почему мой код пропускает группу рассылки и печатает 4 до 2 и 3? Любой совет относительно того, что я делаю неправильно, приветствуется. Спасибо!

< Сильный > Update :

Вот мой метод doSomething. Мой код продолжает зависать при вызове dismiss.

doSomething() {

    viewController.dismiss(animated: false completion: { [weak self] in 
        doMoreCode()
    })
}
2
user3353890 25 Апр 2017 в 01:07

2 ответа

Лучший ответ

Ничто здесь на самом деле не блокирует. dispatch_group_notify просто говорит: «Когда группа закончит, запустите это». Инструмент, который вы намеревались использовать, был dispatch_group_wait. Если вы хотите 1,2,3,4, то вы имели в виду это:

- (void)myFunction {

    NSString *myString = @"Hello world";

    dispatch_group_t group = dispatch_group_create();

    NSLog(@"1 entering the dispatch group");

    dispatch_group_enter(group);
    [self doSomething:myString completion:^{
        NSLog(@"2 we have left the dispatch group");
        dispatch_group_leave(group);
    }];

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    NSLog(@"3 notifying that the dispatch group is finished");

    NSLog(@"4 all process are complete, we are done");
}

myFunction, конечно, нельзя вызвать в главной очереди в iOS (поскольку он блокируется, и вы никогда не должны блокировать основную очередь). И он также не должен вызываться в той же очереди, которую doSomething:completion: использует для своего обработчика завершения (поскольку эта очередь будет заблокирована в dispatch_group_wait).

Помните, что dispatch_queue_notify просто добавляет блок в очередь для запуска в будущем. Так что немного неясно, как вы ожидаете, что 3 и 4 будут работать (в моем примере я просто свернул их, но, возможно, вы ищете что-то еще).

Другой подход состоит в том, чтобы не блокировать приложение, а просто запланировать запуск вещей, когда это необходимо. В этом случае вы можете использовать основную очередь. Это будет выглядеть так:

- (void)myFunction {

    NSString *myString = @"Hello world";

    dispatch_group_t group = dispatch_group_create();

    NSLog(@"1 entering the dispatch group");

    dispatch_group_enter(group);
    [self doSomething:myString completion:^{
        NSLog(@"2 we have left the dispatch group");
        dispatch_group_leave(group);
    }];
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"3 notifying that the dispatch group is finished");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"4 all process are complete, we are done");
    });
}

Обратите внимание, что в обоих примерах я записываю 2 перед вызовом dispatch_group_leave. В этом примере я также регистрирую две вещи, которые будут выполняться в главной очереди (по порядку) после создания группы. В этом случае myFunction немедленно вернется (чтобы его можно было запустить в главной очереди), но все должно распечатываться по порядку.

3
Rob Napier 24 Апр 2017 в 22:23

Предполагая, что doSomething выполняется асинхронно, вы можете подождать группу, но обычно лучше использовать асинхронные шаблоны, например, позвольте myFunction немедленно вернуться, но предоставьте свой собственный обработчик завершения для этого метода:

- (void)myFunctionWithCompletion:(void (^)())completion {
    NSString *myString = @"Hello world";

    dispatch_group_t group = dispatch_group_create();

    NSLog(@"1 entering the dispatch group");

    dispatch_group_enter(group);
    [self doSomething:myString completion:^{
        dispatch_group_leave(group);
        NSLog(@"2 we have left the dispatch group");
    }];
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"3 notifying that the dispatch group is finished");
    });
}

И используйте это так:

[self myFunctionWithCompletion:^{
    NSLog(@"4 all process are complete, we are done");
}];

// note, it's not done when it gets here, though, because it's asynchronous

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

- (void)myFunctionWithCompletion:(void (^)())completion {
    NSString *myString = @"Hello world";

    [self doSomething:myString completion:^{
        NSLog(@"The asynchronous doSomething is done");
        completion();
    }];
}
1
Rob 24 Апр 2017 в 22:33
43598488