2014-01-21 3 views
3

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

Я понимаю, этот обработчик завершения, когда этот метод вызывающий:

-(void)viewDidLoad{ 
[newSimpleCounter countToTenThousandAndReturnCompletionBLock:^(BOOL completed){ 
     if(completed){ 
      NSLog(@"Ten Thousands Counts Finished"); 
     } 
    }]; 
} 

, а затем в вызываемом методе:

-(void)countToTenThousandAndReturnCompletionBLock:(void (^)(BOOL))completed{ 
    int x = 1; 
    while (x < 10001) { 
     NSLog(@"%i", x); 
     x++; 
    } 
    completed(YES); 
} 

Тогда я Сорта придумал с этим на основе многих SO сообщений:

- (void)viewDidLoad{ 
    [self.spinner startAnimating]; 
    [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) { 
     self.usersArray = users; 
     [self.tableView reloadData]; 
    }]; 
} 

, который перезагрузит табличное представление получателями данных после вызова это метод:

typedef void (^Handler)(NSArray *users); 

+(void)fetchUsersWithCompletionHandler:(Handler)handler { 
    NSURL *url = [NSURL URLWithString:@"http://www.somewebservice.com"]; 
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10]; 
    [request setHTTPMethod: @"GET"]; 
    **// We dispatch a queue to the background to execute the synchronous NSURLRequest** 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     // Perform the request 
     NSURLResponse *response; 
     NSError *error = nil; 
     NSData *receivedData = [NSURLConnection sendSynchronousRequest:request 
                returningResponse:&response 
                   error:&error]; 
     if (error) { **// If an error returns, log it, otherwise log the response** 
      // Deal with your error 
      if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 
       NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; 
       NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error); 
       return; 
      } 
      NSLog(@"Error %@", error); 
      return; 
     } 
     **// So this line won't get processed until the response from the server is returned?** 
     NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; 

     NSArray *usersArray = [[NSArray alloc] init]; 
     usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil]; 
     // Finally when a response is received and this line is reached, handler refers to the block passed into this called method...so it dispatches back to the main queue and returns the usersArray 
     if (handler){ 
      dispatch_sync(dispatch_get_main_queue(), ^{ 
      handler(usersArray); 
      }); 
     } 
    }); 
} 

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

В этом случае часть «завершения» зависит от того, является ли вызов NSURLRequest синхронным. Что, если это было асинхронно? Как я смогу удержаться от вызова блока до тех пор, пока мои данные не будут заполнены NSURLResponse?

ответ

3

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

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

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

self.usersArray = users; 

Вызов self заставит блок сохранить self (вызывающего объекта). Это может легко создать цикл сохранения. Как правило, вам нужно взять слабую ссылку на self так:

- (void)viewDidLoad{ 
    [self.spinner startAnimating]; 
    __weak typeof(self) weakSelf = self; 
    [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) { 
    typeof(self) strongSelf = weakSelf; 
    if (strongSelf) { 
     [strongSelf setUsersArray:users]; 
     [[strongSelf tableView] reloadData]; 
    } 
    }]; 
} 

Это довольно педантичный версия шаблона weakSelf/strongSelf, и это можно было бы сделать немного проще в этом случае, но она демонстрирует все которые вам могут понадобиться. Вы принимаете слабую ссылку на self, чтобы создать цикл сохранения. Затем, в полном блоке, вы берете сильную ссылку, чтобы self так, чтобы она не могла исчезнуть на вас в середине вашего блока. Затем вы убедитесь, что self на самом деле все еще существует, и только потом продолжайте. (С сообщениями nil является законным, вы могли бы пропустил strongSelf шаг в этом конкретном случае, и это было бы то же самое.)

+0

Спасибо @RobNapier, это именно тот ответ, который я искал. Я пытаюсь начать создавать свои собственные обработчики завершения. Я пытался исследовать встроенные в UIKit, такие как анимации UIView, но, конечно, они частные :). Последнее, когда вы говорите: «Если это было асинхронно, вам нужно сохранить блок в переменной экземпляра и вызвать это, когда асинхронная операция завершилась «как я узнаю, когда асинхронный оператор будет завершен, если эта операция не имеет обработчика завершения? Как обработка изображений? – marciokoko

+0

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

+0

Итак, возможно, я подумал о том, как вы на самом деле разрабатываете метод, который должен постоянно проверять результаты, чтобы предоставить обработчик завершения или обратный вызов пользователю этого API. И я думаю, ответ заключается в том, что это зависит от того, что ждет этот метод, и мне нужно будет создать соответствующий код, чтобы убедиться, что что-то было возвращено, чтобы обеспечить обработчик завершения, да?Например, я мог бы использовать NSTimer, который каждые 5 секунд проверяет, был ли возвращен NSURLResponse, а затем предоставлен обработчик завершения? Как пример цикла! – marciokoko

2

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

Ваш второй пример fetchUsersWithCompletionHandler: является асинхронным методом. Однако на самом деле он довольно субоптимален:

  1. Это должно как-то сигнализировать о том, что запрос может оказаться неудачным. То есть, либо предоставить дополнительный параметр обработчику завершения, например. "NSError* error или нам один параметр id result. В первом случае, либо ошибки или массива не nil, а во втором случае, единственный параметр результата может быть либо объектом ошибки (вроде NSError) или фактический результат (вроде NSArray)

  2. в случае, если ваш запрос терпит неудачу, вы пропустите сигнал об ошибке в колл-сайт

  3. есть код запахов:..

    На самом деле базовый сетевой код, реализованный системой, является асинхронным. Однако используемый метод удобного класса sendSynchronousRequest: является синхронным. Это означает, что в качестве детали реализации sendSynchronousRequest: вызывающий поток: заблокирован до тех пор, пока не появится результат ответа сети. И this_blocking_ занимает целую цепочку только для ожидания. Создание потока довольно дорогостоящее, и только для этой цели есть отходы. Это первый запах кода. Да, просто с использованием удобный метод класса sendSynchronousRequest: сам по себе плохой практический программист!

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

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

+0

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

+0

@marciokoko Если вы используете асинхронный сетевой запрос, вы в основном помещаете все, что нужно «продолжить» _after_ ответ доступен _into_ обработчик завершения этого асинхронного сетевого метода. – CouchDeveloper

+0

Да, но если я вставил строку, чтобы перезагрузить представление после асинхронного вызова NSURLRequest, он будет вызван, как только вызывается NSURLRequest, но пока еще не будет NSURLResponse. – marciokoko

Смежные вопросы