Блокировать блоки iOS синхронно

Как заставить блок выполнить синхронно или заставить функцию ждать обработчика перед оператором return, чтобы данные могли быть переданы обратно из блока?

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    return data;
}

Ответ 1

В этом случае вы можете использовать семафоры.

-(id)performRequest:(id)args
{
    __block NSData *data = nil;   
     dispatch_semaphore_t sem = dispatch_semaphore_create(0);
     [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
       data = [NSData dataWithData:responseData];
       dispatch_semaphore_signal(sem);
     }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    return data;
}

Семафор блокирует выполнение дополнительных операторов до получения сигнала, это гарантирует, что ваша функция не вернется преждевременно.

Ответ 2

Async почти всегда лучше. но если вы хотите синхронно:

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    while(!data) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
    return data;
}

Отказ от ответственности: CocoaCouchDeveloper говорит, что это, конечно, будет работать, только если блок завершения и runloop находятся в одном потоке. Я предположил, что, поскольку многие (большинство) обработчиков ЗАВЕРШЕНИЯ, я знаю работу таким образом, но это действительно в принципе.

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

Ответ 3

Вы можете просто добавить метод, который обрабатывает возвращенные данные и вызывает это в вашем блоке:

-(void)performRequest:(id)args{
    __block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
        [self processData:data]; //method does something with the data 
    }];
}

Ответ 4

Вы можете выполнять синхронный запрос в другом потоке, например, ниже кода

-(void)performRequest:(id)args
{

 NSURLResponse *response = nil;
 NSError *error = nil;
 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
 data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

Из основного потока вы можете назвать это

[self performSelectorInBackground:@selector(performRequest:) withObject:args];

или вы можете выполнить асинхронный запрос, используя следующий метод

[NSURLConnection alloc]initWithRequest:request delegate:self];

и реализовать методы делегирования для NSURLConnection

Ответ 5

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

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

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

Ответ 6

Вы могли бы так.

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

 [xyzclass performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse          *urlResponse, NSError *error) {

     dispatch_sync( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{

     data = [NSData dataWithData:responseData];

     });

 }];

return data;
}