Остановить выполнение потока до завершения асинхронного потока API

В настоящее время я просматриваю диапазон дат и кэширую EKEvents и EKReminders, связанные с каждой датой. Проблема, с которой я столкнулся, заключается в том, что когда я получаю EKReminder, API работает асинхронно. Проблема в том, что мне нужно получить доступ к содержимому этого потока из моей текущей очереди потока отправки. Я создал локальную переменную и __block'd ее, чтобы я мог назначить ей содержимое API async, а затем я завернул весь вызов fetch в вызов dispatch_sync, надеясь, что он задержит мою текущую очередь до тех пор, пока async не будет завершено, но этого не происходит. Как я могу остановить свой текущий поток, пока не будет завершен вызов асинхронного API?

Я предоставил два комментария ниже в коде, чтобы показать вам места, где мне нужно остановить выполнение кода и подождать.

- (void)setupCalendarCache {
    // Dispatch the queueing of previous days to a background thread
    dispatch_queue_t previousDaysCacheQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_async(previousDaysCacheQueue, ^{
        NSLog(@"Preparing to cache previous calendar days.");

        int reverseYearsToCache = (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE * 365) / 2;
        DEDateUtility *dateUtility = [[DEDateUtility alloc] init];
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

        dispatch_apply(reverseYearsToCache, previousDaysCacheQueue, ^(size_t i) {
            @synchronized (self.datesOnCalendar) {
                NSLog(@"synchronizing calendar dates.");

                // Setup start and end time for event & reminder fetching.
                NSDate *date = [dateUtility adjustDate:self.currentDate byNumberOfDays:i-1];
                NSLog(@"Working date %@", [date description]);

                [self.datesOnCalendar insertObject:date atIndex:0];

                // Start of the day
                NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
                dateComponents.hour = 0;
                dateComponents.minute = 0;
                dateComponents.second = 0;
                NSDate *startDate = [calendar dateByAddingComponents:dateComponents toDate:date options:0];

                // End of the day
                dateComponents.hour = 23;
                dateComponents.minute = 59;
                dateComponents.second = 59;
                NSDate *endDate = [calendar dateByAddingComponents:dateComponents toDate:date options:0];

                // Collections for the reminders and events fetched.
                NSArray *events = [[NSArray alloc] init];
                // Must be blocked so that the Block thread can access it even after this method is completed.
                __block __strong NSArray *remindersList = [[NSArray alloc] init];

                NSLog(@"Building predicates.");
                // Predicates for fetching the reminders and events.
                NSPredicate *fetchPredicateForEvents = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:[self.eventStore calendarsForEntityType:EKEntityTypeEvent]];
                NSPredicate *fetchPredicateForReminders = [self.eventStore predicateForIncompleteRemindersWithDueDateStarting:startDate ending:endDate calendars:[self.eventStore calendarsForEntityType:EKEntityTypeReminder]];

                // Fetch the events matching the aforementioned NSPredicate.
                events = [self.eventStore eventsMatchingPredicate:fetchPredicateForEvents];
                // Don't store a nil array in the dictionary.
                if (!events) {
                 events = [[NSArray alloc] init];
                    NSLog(@"No events found for this day.");
                } else {
                    NSLog(@"Found %d events.", [events count]);
                }

                // ISSUE: Need to block execution until the following dispatch_sync is completed. How can I do this?
                NSLog(@"Running reminders block.");
                dispatch_sync(previousDaysCacheQueue, ^ {
                    // Fetch the reminders matching the aforementioned NSPredicate.
                    [self.eventStore fetchRemindersMatchingPredicate:fetchPredicateForReminders completion:^(NSArray *reminders) {
                        if (reminders ) {
                            remindersList = reminders;
                            NSLog(@"Found %d reminders (%d)", [remindersList count], [reminders count]);
                            return;
                        }
                    }];
                });

                NSLog(@"Building dictionaries for events and reminders.");
                // CANT DO THIS. API async invocation is still taking place.
                //Place the events and reminders in the dictionary
                NSDictionary *eventsAndRemindersForDay = @{@"events": events, @"reminders" : remindersList};
                [previousEventsAndReminders setObject:eventsAndRemindersForDay forKey:[date description]];

                for (int index = 0; index < [remindersList count]; index++) {
                    EKReminder *reminder = remindersList[index];
                    NSLog(@"reminder: %@", reminder.title);
                }
            }
        });
        self.cachingStage += 1;
    });

}


person Johnathon Sullinger    schedule 13.07.2013    source источник
comment
Я хочу дождаться завершения [self.eventStore fetchRemindersMatchingPredicate: Завершение ^ ()]. Блок завершения является асинхронным, и я хочу заставить мою очередь ждать, пока не будет завершен блок завершения асинхронного выполнения.   -  person Johnathon Sullinger    schedule 14.07.2013


Ответы (1)


Переместите код, который идет после блока dispatch_sync(), в блок завершения для fetchRemindersMatchingPredicate:. Ожидание выполнения чего-либо до тех пор, пока асинхронная задача не будет выполнена, - это именно то, для чего нужен блок завершения.

Также не делайте dispatch_sync() вызовов текущей очереди. Это гарантирует взаимоблокировку последовательной очереди и имеет высокую вероятность, даже если очередь является параллельной.

person jscs    schedule 13.07.2013
comment
Я внесу изменения, хотя мне не очень нравится помещать внутрь него код, который не принадлежит этому блоку завершения (например, добавление событий, которые я получил, в словарь в моем блоке завершения напоминаний). Тем не менее, я продолжу и сделаю попытку, чтобы мой проект продвигался вперед. В образовательных целях, с моей стороны, что вы подразумеваете под взаимоблокировкой последовательной очереди? Я думал, что весь смысл dispatch_sync () в том, что ваша текущая очередь остановится до завершения dispatch_sync. Если я запустил dispatch_sync в другой очереди, будет ли текущая очередь ждать завершения? - person Johnathon Sullinger; 14.07.2013
comment
Последовательная очередь не запускает следующую задачу до тех пор, пока не будет завершена текущая (параллельная очередь может). Синхронная отправка задачи означает, что текущая задача не завершится, пока не будет завершена новая задача. Если вы объедините их, вы окажетесь в тупике - новая задача не может начаться, пока не закончится текущая задача, чего она не может сделать, пока не закончится новая задача. - person jscs; 14.07.2013
comment
Он действительно принадлежит этому Блоку. Вы не можете сделать это, пока не завершится выборка. Это ситуация, с которой блок завершения предназначен для обработки. - person jscs; 14.07.2013
comment
В документации GCD по очередям отправки говорится, что каждая очередь выполняется одновременно. Итак, если я помещу dispatch_sync в другую очередь, могу ли я гарантировать, что код, который он запускает, будет завершен до того, как он вернется к коду, выполняющемуся в очереди previousDaysCacheQueue? - person Johnathon Sullinger; 14.07.2013
comment
Вы разместили свое сообщение, пока я набирал свое лол. Ваше объяснение имеет смысл, спасибо за помощь. Я изменю свой код. Спасибо еще раз! - person Johnathon Sullinger; 14.07.2013
comment
Да, очереди идут совершенно отдельно друг от друга. И, да, если вы dispatch_async(q1, ^{ [self carvePumpkin]; dispatch_sync(q2, ^{ [self findCandle]; }); [self illuminateJackOLantern];);, вы не зажжете Фонарь Джека до тех пор, пока не найдете свечу, но то же самое верно и для dispatch_async(q1, ^{ [self carvePumpkin]; [self findCandle]; [self illuminateJackOLantern];);. dispatch_sync() вам понадобится только в том случае, если вам нужно подождать чего-то, что должно происходят в другой очереди. Кроме того, это не меняет синхронный / асинхронный характер всего, что содержится ... - person jscs; 14.07.2013
comment
в своей задаче: dispatch_async(q1, ^{ [self carvePumpkin]; dispatch_sync(q2, ^{ dispatch_async(q3, ^{[self findCandle];}); }); [self illuminateJackOLantern];}); означает ожидание, пока блок, содержащий [self findCandle], не будет поставлен в очередь, а не до тех пор, пока он не запустится. Это относится непосредственно к использованию вами метода, который выполняет выборку в фоновом режиме. Сама отправка сообщения запускается, метод запускает свою собственную задачу, а затем dispatch_sync() возвращается, потому что метод вернулся. - person jscs; 14.07.2013
comment
Спасибо за разъяснения. Многому научился на этом, выручил тонну! - person Johnathon Sullinger; 14.07.2013
comment
Конечно, рад, что смог помочь. - person jscs; 14.07.2013