Возвращаемое значение для функции внутри блока

Я использую AFNetworking для получения данных с сервера:

-(NSArray)some function {
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
            NSArray *jsonArray =[JSON valueForKey:@"posts"];
        }
        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {}
}

Так что я пытаюсь сделать здесь, это вернуть jsonArray в функцию. Очевидно, что возврат не работает.

Ответ 1

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

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

- (void)goFetch:(id)caller
{
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {

        [caller takeThisArrayAndShoveIt:[JSON valueForKey:@"posts"]];
    }
    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {}
}

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

- (void)goFetch:(void(^)(NSArray *))completion
{
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        if( completion ) completion([JSON valueForKey:@"posts"]);
    }
    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {}
}

Ответ 2

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

typedef void (^Completion)(NSArray* array, NSError *error);

-(void)someFunctionWithBlock:(Completion)block {
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
            NSArray *jsonArray =[JSON valueForKey:@"posts"];
            if (block) block(jsonArray, nil);
        }
        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { 
            if (block) block(nil, error); 
        }
}

Тогда, когда вы называете это someFunction. Этот код также сделает правильную обработку ошибок для вас.

[yourClassInstance someFunctionWithBlock:^(NSArray* array, NSError *error) {
   if (error) {
      NSLog(%@"Oops error: %@",error.localizedDescription);
   } else {
      //do what you want with the returned array here.
   }
}];

Ответ 3

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

+(void)request:(NSString *)link parameters:(NSDictionary *)params  forInstance:(id)instance returns:(SEL)returnValue
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:link
      parameters:params
         success:^(AFHTTPRequestOperation *operation, id responseObject)
     {
         [instance performSelector:returnValue withObject: responseObject];
     }
         failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
         [instance performSelector:returnValue withObject:nil];
         //NSLog(@"Error: %@", error);
     }];
}

Ответ 4

Вы можете сделать это, если напишите такую обертку. В этой ситуации вам нужен цикл while, который будет ожидать ответа от блока.

Метод, который должен возвращать значение enum

- (RXCM_TroubleTypes) logic_getEnumValueOfCurrentCacheProblem
{
    RXCM_TroubleTypes result = RXCM_HaveNotTrouble;
    NetworkStatus statusConnection = [self network_typeOfInternetConnection];

    RXCM_TypesOfInternetConnection convertedNetStatus = [RXCM convertNetworkStatusTo_TypeOfInternetConnection:statusConnection];


    BOOL isAllowed = [self someMethodWith:convertedNetStatus];
    if (isAllowed){
        return RXCM_HaveNotTrouble;
    }else { 
        return RXCM_Trouble_NotSuitableTypeOfInternetConnection;
    }

   return result;
}

Метод, который вызывает метод делегата с блоком. И ждет ответа от него. Здесь я использую цикл while. Просто проверяйте каждый ответ 0,5 сек из блока

- (BOOL) isUserPermissioned:(RXCM_TypesOfInternetConnection)newType
{
    __block BOOL isReceivedValueFromBlock = NO;
    __block BOOL result = NO;
    __block BOOL isCalledDelegateMethod = NO;

    dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_sync(aQueue,^{

        while (!isReceivedValueFromBlock) {
            NSLog(@"While");
            if (!isCalledDelegateMethod){
                [self.delegate rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:newType
                                                                                   completion:^(BOOL isContinueWorkOnNewTypeOfConnection) {
                                                                                       result = isContinueWorkOnNewTypeOfConnection;
                                                                                       isReceivedValueFromBlock = YES;
                                                                                   }];
                isCalledDelegateMethod = YES;
            }
            [NSThread sleepForTimeInterval:0.5];

        }
    });
    return result;
}

Метод делегата в ViewController

- (void) rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:(RXCM_TypesOfInternetConnection)newType
                                                             completion:(void(^)(BOOL isContinueWorkOnNewTypeOfConnection))completion
{
    __weak ViewController* weak = self;

    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert"
                                                                       message:@"to continue download on the new type of connection"
                                                                preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *ok = [UIAlertAction actionWithTitle:@"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completion(YES);
        }];

        UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"NO" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            completion(NO);
        }];

        [alert addAction:cancel];
        [alert addAction:ok];
        [weak presentViewController:alert animated:YES completion:nil];

    });
}