dispatch_async никогда не заканчивает выполнение всего блока

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

+ (void)synchronizationTimerFired:(NSTimer *)theTimer
{
    if ((synchronizationUpNeededFlag) || (synchronizationDownNeededFlag))
    {
        if ((!synchronizationUpInProgressDepthQuantity) && (!synchronizationDownInProgressDepthQuantity))
        {
            dispatch_queue_t synchronizationQueue = dispatch_queue_create("synchronizationQueue",NULL);
            dispatch_async(synchronizationQueue, ^(void) {

                NSLog(@"Top");
                ...code...
                ...code...
                ...code...
                NSLog(@"Bottom");

            });
        }
    }

    // Check if there is no timer, or if it is not currently valid,
    // and yet if synchronization is turned on,
    // then establish a repeating timer to attend to synchronization related matters.
    if ((!synchronizationTimer) || (!synchronizationTimer.isValid))
    {
        if (synchronizationOnFlag)
        {
            synchronizationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(synchronizationTimerFired:) userInfo:nil repeats:YES];
        }
    }
}

В журнале написано "Верх" и больше ничего. Код в середине не имеет бесконечного цикла — он просто никогда не выполняется до конца. Я могу поставить точки останова в коде посередине, и там будет точка, в которой выполнение программы прервется, а после которой — нет. И есть точка прямо посередине, где иногда выполнение останавливается в точке останова, а иногда нет.

Мне кажется, что очередь отправки synchronizeQueue освобождается, но я не могу вызвать dispatch_retain, потому что компилятор жалуется, что dispatch_retain нельзя использовать в ARC. Что мне не хватает?

В ответ на вопросы людей о промежуточном коде выполнение программы останавливается в этом вызове метода (представленном одной из этих строк ... кода...) на строке, которая говорит if (fetchArray.count), прокомментирован ниже .

+ (NSDate *)latestParseReceivedDownUpdatedAtDateForCoreDataEntityNameString:(NSString *)coreDataEntityNameString
{
    NSDate *functionReturnValue = nil;

    // Create fetchRequest
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:coreDataEntityNameString];

    // Set sort descriptor
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"parseReceivedDownUpdatedAtDate" ascending:NO]]];

    // We are only interested in one result
    [fetchRequest setFetchLimit:1];

    // Execute fetchRequest
    NSError *fetchError = nil;
    NSArray *fetchArray = [JBSAPPDELEGATE.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];

    if (fetchArray == nil)
    {
        NSLog(@"Unresolved error %@, %@", fetchError, [fetchError userInfo]);
        abort();
    }

    // If there are any records at all in our persistent store, we'll have exactly one.
    // But that doesn't mean it won't be nil, as if that record has never come down from
    // parse it will be a nil date on the managed object.
    if (fetchArray.count) // PROGRAM EXECUTION STOPS EITHER HERE, OR JUST BEFORE HERE
    {
        NSManagedObject *managedObject = [fetchArray objectAtIndex:0];
        functionReturnValue = [managedObject valueForKey:@"parseReceivedDownUpdatedAtDate"];
    }

    return functionReturnValue;
}

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


person John Bushnell    schedule 02.11.2013    source источник
comment
Я думаю, вы должны опубликовать код между двумя NSLog.   -  person Gabriele Petronella    schedule 03.11.2013
comment
Очередь не освобождается, по крайней мере, с помощью показанного кода, пока не завершится асинхронный вызов. Замена кода в середине на sleep(2); и даже добавление synchronizationQueue=nil; после блока будут выполняться нормально. Так что перепроверьте свой другой код.   -  person Gerd K    schedule 03.11.2013
comment
Приведенный выше код является упрощением. Код между ними на самом деле несколько длинный. Я стараюсь не затягивать вопрос. Фактический код выглядит следующим образом: dispatch_queue_t synchronizeQueue = dispatch_queue_create(synchronizationQueue, NULL); dispatch_async(synchronizationQueue, ^(void) {[самосинхронизируем что-нибудь нужное]; });   -  person John Bushnell    schedule 03.11.2013
comment
@John Я понимаю, но в коде, который вы разместили до сих пор, нет ничего плохого, поэтому очень вероятно, что проблема в той части, которую вы пропустили.   -  person Gabriele Petronella    schedule 03.11.2013
comment
@John Послушай человека, опубликуй код между NSLogs   -  person IluTov    schedule 03.11.2013
comment
Я разместил код вызова метода в блоке выполнения, где происходит сбой. Если вы все еще хотите больше, спросите, и я дам это, но это будет много кода.   -  person John Bushnell    schedule 03.11.2013
comment
Насколько я знаю, если вы хотите использовать -executeFetchRequest: в фоновом режиме, вам нужен отдельный контекст.   -  person Gerd K    schedule 03.11.2013
comment
@ Герд К Хм. Я видел, как раньше люди использовали отдельный контекст в фоновом коде. Возможно, это проблема. Какой контекст я бы использовал?   -  person John Bushnell    schedule 03.11.2013
comment
Релевантно: developer.apple.com/library/ ios/documentation/cocoa/conceptual/   -  person Gabriele Petronella    schedule 03.11.2013


Ответы (2)


Является ли ваш manageObjectContext NSManagedObjectContext? Если да, создали ли вы контекст управляемого объекта в конкретном потоке, который вы используете для своей очереди отправки? Если вы читаете документы по NSManagedObjectContext, там написано:

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

person Duncan C    schedule 02.11.2013
comment
Я считаю, что это ответ, так как я использую управляемый ObjectContext, созданный в основном потоке. Это, безусловно, проблема, поэтому пока я отмечаю это как ответ. Поток для очереди отправки предположительно завершается из-за доступа к manageObjectContext, не созданному в текущем потоке. Спасибо, и спасибо Габриэле Петронелле, которая разместила ссылку на документ разработчика в комментариях к основному сообщению. - person John Bushnell; 03.11.2013

Бьюсь об заклад, вы создали себе тупик где-то.

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

person gnasher729    schedule 19.02.2014