Как заставить определенную часть функции ждать в iOS

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

-(void) shareOperation
{
__block NSString *resultText;
BOOL continueSharing;
 NSLock *conditionLock=[[NSLock alloc] init];
         dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            [conditionLock lock];
        [self performSomeAsynchronousOperation completionBlock:^(NSError *tError, bool status)
         {
             dispatch_async(dispatch_get_main_queue(),
                            ^{

                                if (status)
                                {
                                    resultText = @"Operation completed. Would you like to continue?";

                                }
                                else
                                {
                                    resultText = @" Operation failed. Would you still like to continue?";
                                }
                                UIAlertView *result = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:@"Cancel" otherButtonTitles:[NSArray arrayWithObjects:@"OK",nil] onDismiss:^(int buttonIndex)
                                                       {
                                                           NSLog(@"selected button index: %d",buttonIndex);
                                                           if (buttonIndex == 0)
                                                           {
                                                               continueSharing=YES;
                                                               [conditionLock unlock];
                                                               NSLog(@"We are continuing sharing :)");
                                                           }
                                                       }onCancel:^{
                                                           continueSharing=NO;
                                                            [conditionLock unlock];
                                                           NSLog(@"cancelled");
                                                       }]; [result show];

                            });
         }];
        });
    }


    //should continue only after earlier if block finishes, ie after getting the continueSharing value

    if (continueSharing)
    {
        [self performSomeAnotherAsynchronousOperation];
    }

}

person Francis F    schedule 17.01.2014    source источник
comment
Вы можете попробовать поместить оператор if в блок?   -  person Ashutosh    schedule 17.01.2014
comment
stackoverflow.com/questions/17920169/   -  person chandu    schedule 17.01.2014


Ответы (2)


Вместо использования блокировок (которые предназначены только для предотвращения одновременного доступа к какому-либо общему ресурсу) вы могли бы использовать семафоры (которые разработаны так, чтобы один поток мог ожидать сигнала от другого потока, который здесь необходим).

Итак, вы должны создать семафор:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

Блоки завершения предупредительного представления будут сигнализировать этому семафору:

dispatch_semaphore_signal(semaphore);

И где вы хотите дождаться этого сигнала (до performSomeAnotherAsynchronousOperation):

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

Я немного подправил ваш код, но самое главное, изменил его так, чтобы он не блокировал основную очередь (чего вы никогда не захотите делать), убедившись, что dispatch_semaphore_wait выполняется в фоновой очереди. Также обратите внимание, что dispatch_semaphore_signal не находится внутри оператора if. Это привело к:

- (void)shareOperation
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        __block BOOL continueSharing = NO;
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        [self performSomeAsynchronousOperationWithCompletionBlock:^(NSError *tError, bool status){
            dispatch_async(dispatch_get_main_queue(),^{
                NSString *resultText;

                if (status)
                    resultText = @"Operation completed. Would you like to continue?";
                else
                    resultText = @"Operation failed. Would you still like to continue?";

                UIAlertView *alertView = [UIAlertView alertViewWithTitle:nil message:resultText cancelButtonTitle:@"Cancel" otherButtonTitles:@[@"OK"] onDismiss:^(int buttonIndex) {
                    NSLog(@"selected button index: %d",buttonIndex);
                    if (buttonIndex == 0) {
                        continueSharing = YES;
                        NSLog(@"We are continuing sharing :)");
                    }
                    dispatch_semaphore_signal(semaphore);
                } onCancel:^{
                    dispatch_semaphore_signal(semaphore);
                    NSLog(@"cancelled");
                }];

                [alertView show];
            });
        }];

        // should continue only after earlier if block finishes, ie after getting the continueSharing value

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        if (continueSharing)
            [self performSomeAnotherAsynchronousOperation];
    });
}

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

person Rob    schedule 17.01.2014
comment
Вариант 3 не подходит для меня, так как в моем фактическом коде много вложенных операций, этот код был просто для того, чтобы показать проблему, я попробовал первый вариант, но получил [блокировку NSLock]: deadlock, я не знаком с семафором, так что теперь ищем, как это реализовать. Спасибо за ваш ответ. - person Francis F; 17.01.2014
comment
Я попробовал семафор, но пользовательский интерфейс с той же проблемой зависает, я думаю, что есть другая проблема, на самом деле мой метод [self PerformSomeAsynchronousOperation completeBlock...] должен вызывать всплывающее окно, и когда я нажимаю кнопку «ОК» во всплывающем окне, это вернет статус, тогда только он будет показывать предупреждение. Я думаю, что всплывающее окно также находится в режиме ожидания, поскольку оно никогда не появляется. Если да, то как мне поступить в этой ситуации? - person Francis F; 17.01.2014
comment
Как мне это сделать? Я попытался добавить свою начальную функцию внутрь dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); но это не помогло - person Francis F; 17.01.2014
comment
@Gamerlegend Хорошо, отступим: 1. Блокировки действительно не подходящее решение, поэтому я полностью удалил это из своего ответа. Простой NSLock не работает, но и пытаться это исправить не имеет смысла. 2. Учитывая ваши комментарии, я проверил свой пересмотренный ответ семафора, и он, похоже, работает, но я включил более полный пример кода для вашего обзора. Я почистил несколько вещей в вашем примере кода, так что взгляните. - person Rob; 17.01.2014
comment
3. Что касается вашего performSomeAsynchronousOperation вопроса о представлении конкурирующих предупреждений, трудно сказать без подробностей о том, как работает performSomeAsynchronousOperation, особенно когда вызывается обработчик завершения в отношении отображаемого предупреждения. Вызывается ли обработчик завершения до или после того, как вы отклоняете предупреждение о том, что представление предупреждения этого performSomeAsynchronousOperation отображается/закрывается? - person Rob; 17.01.2014
comment
PerformSomeAsynchronousOperation обычно написана в другом классе общего доступа, в настоящее время я делаю общий доступ к твиттеру в этой функции, поэтому предполагается, что он будет отображать всплывающее окно твиттера для публикации. Как только пост будет опубликован или отменен во всплывающем окне, обработчик завершения вернет статус true или false. - person Francis F; 17.01.2014
comment
@Gamerlegend Тогда я бы подумал, что вышеуказанный семафор справится со своей задачей. Это очень распространенный шаблон, хотя необходимо соблюдать осторожность при точном размещении вызовов signal и wait. - person Rob; 18.01.2014
comment
Спасибо, проверю, может что-то не так делаю. В любом случае, спасибо за все ваши усилия, я отмечу это как лучший ответ. - person Francis F; 19.01.2014
comment
Я не совсем уверен в проблеме, но когда я прокомментировал dispatch_async(dispatch_get_main_queue().. в вашем коде, он работал нормально, но позже мне пришлось раскомментировать его, чтобы он снова заработал. - person Francis F; 20.01.2014
comment
Я понял проблему, мы должны получить основную очередь, прежде чем вызывать оповещение. Я обновил ответ, чтобы он мог помочь другим. - person Francis F; 21.01.2014

Вы можете использовать блок задержки

 dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC);

    dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){

    })
person Divya    schedule 17.01.2014