2013-08-17 3 views
18

Im пытается понять обработчики завершения & блоков. Я считаю, что вы можете использовать блоки для многих глубоких программных вещей без обработчиков завершения, но я думаю, что понимаю, что обработчики завершения основаны на блоках. (Так что в основном обработчики завершения требуют блоки, но не наоборот).Как обработчик завершения работает на iOS?

Так что я увидел этот код в интернете о старых рамках твиттере:

[twitterFeed performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { 
     if (!error) { 
      self.successLabel.text = @"Tweeted Successfully"; 
      [self playTweetSound]; 
     } else { 
      // Show alert 
     } 
     // Stop indicator 
     sharedApplication.networkActivityIndicatorVisible = NO; 
    }]; 

Здесь мы вызываем метод, который делает вещи (выполняет TWRequest) и возвращается, когда закончил с responseData & urlResponse & ошибки. Только когда он возвращает, он выполняет блок, который предоставляет тесты и останавливает индикатор активности. ИДЕАЛЬНО!

Теперь это установка у меня есть для другого приложения, которое работает, но я пытаюсь положить кусочки вместе:

@interface 
Define an ivar 
typedef void (^Handler)(NSArray *users); 
Declare the method 
+(void)fetchUsersWithCompletionHandler:(Handler)handler; 

@implementation 
+(void)fetchUsersWithCompletionHandler:(Handler)handler { 
    //...Code to create NSURLRequest omitted... 
    __block NSArray *usersArray = [[NSArray alloc] init]; 

    //A. Executes the request 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 

     // Peform the request 
     NSURLResponse *response; 
     NSError *error = nil; 
     NSData *receivedData = [NSURLConnection sendSynchronousRequest:request 
                returningResponse:&response 
                   error:&error]; 
     // Deal with your error 
     if (error) { 
      } 
      NSLog(@"Error %@", error); 
      return; 
     } 
     // Else deal with data 
     NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; 
     usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil]; 

     // Checks for handler & returns usersArray to main thread - but where does handler come from & how does it know to wait tip usersArray is populated? 
     if (handler){ 
      dispatch_sync(dispatch_get_main_queue(), ^{ 
      handler(usersArray); 
      }); 
     } 
    }); 
} 

Вот мое понимание:

  1. fetchUsersWithCompletionHandler, очевидно, гомолог выполнения RequestWithHandler
  2. , к сожалению, это немного сложнее, потому что есть вызов GCD на пути ...

Но, в основном, запрос выполнен и с ошибкой обрабатывается, данные обрабатываются, а затем проверяется обработчик. Мой вопрос: как эта часть обработчика работает? Я понимаю, что если он существует, он отправит обратно в основную очередь и вернет userArray. Но как он знает, ждать, пока userArray будет заполнен? Я предполагаю, что меня смущает тот факт, что метод: блок в этом случае имеет другой блок внутри него, вызов dispatch_async. Я предполагаю, что Im ищет логику, которая на самом деле делает вещи и знает, КОГДА возвращать responseData и urlResponse. Я знаю, что это не то же приложение, но я не вижу кода для выполненияRequestWithHandler.

ответ

29

В основном в данном случае это работает так:

  1. Вы называете fetchUsersWithCompletionHandler: от того, что нить вам нравится (вероятно, образуют основной один).
  2. Он инициализирует NSURLRequest, а затем вызывает:. dispatch_async (dispatch_get_global_queue ... , который в основном создает блок, и графики на обработку на фоне очереди
  3. Поскольку dispath_async, текущий поток покидает fetchUsersWithCompletionHandler: метод.

    ...
    проходит время, до фона очередь имеет некоторые свободные ресурсы
    ...

  4. И теперь, когда backgr ound очередь свободна, он потребляет запланированную операцию (Примечание: выполняет синхронного запроса - так он ждет данных):

    NSURLResponse *response; 
    NSError *error = nil; 
    NSData *receivedData = [NSURLConnection sendSynchronousRequest:request 
                  returningResponse:&response 
                     error:&error]; 
    ... 
    
  5. После приходят данные, то usersArray заполняется.

  6. Кодекс продолжает эту часть:

    if (handler){ 
        dispatch_sync(dispatch_get_main_queue(), ^{ 
         handler(usersArray); 
        }); 
    } 
    
  7. Теперь, если мы определили обработчик, он планирует блокировать для вызова на основной очереди. Это dispatch_sync, поэтому выполнение текущего потока не будет продолжаться до тех пор, пока основной блок не будет выполнен с блоком. На этом этапе фоновый поток терпеливо ждет.

    ...
    другой момент проходит
    ...

  8. Теперь главная очередь имеет несколько свободных ресурсов, поэтому она потребляет выше блока, и выполняет этот код (проходя ранее населенную usersArray к «обработчик»):

    handler(usersArray); 
    
  9. как только это будет сделано, он возвращается из блока и продолжает потреблять то, что он находится в главной очереди.

  10. Поскольку основной поток выполняется с помощью блока, также можно использовать фоновый поток (застрял в dispatch_sync). В этом случае он просто возвращается из блока.

Edit: Что касается вопросов, которые вы просили:

  1. Это не как основной очереди/фон будет всегда занят, это только может быть. (предполагая, что фоновая очередь не поддерживает параллельные операции, такие как основной). Представьте себе следующий код, который выполняется на основном потоке:

    dispatch_async(dispatch_get_main_queue(), ^{ 
         //here task #1 that takes 10 seconds to run 
         NSLog(@"Task #1 finished"); 
        }); 
        NSLog(@"Task #1 scheduled"); 
    
        dispatch_async(dispatch_get_main_queue(), ^{ 
         //here task #2 that takes 5s to run 
         NSLog(@"Task #2 finished"); 
        }); 
        NSLog(@"Task #2 scheduled"); 
    

Поскольку оба dispatch_async вызовы, планировать их для выполнения один за другим. Но задача №2 немедленно не будет обрабатываться главной очередью, так как сначала она должна покинуть текущий цикл выполнения, во-вторых, она должна сначала завершить задачу №1.

Так выход журнала будет так:

Task #1 scheduled 
Task #2 scheduled 
Task #1 finished 
Task #2 finished 

2.You имеют:

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

который объявляет блок typedefe'd в Handler, который имеет void тип возврата и принимает NSArray *, как параметр.

Позже, у вас есть функция:

+(void)fetchUsersWithCompletionHandler:(Handler)handler 

который принимает в качестве параметра блока типа Handler и разрешить доступ к нему с помощью локального имени handler.

И шаг # 8:

handler(usersArray); 

Который только непосредственно вызывает handler блока (как вы звонили/C++ функции любого C) и передает usersArray в качестве параметра к нему.

+0

2 вопроса: (1) почему вы говорите в шаге 3 «время проходит, пока у фоновой очереди нет свободных ресурсов». Почему у него НЕ есть свободные ресурсы? на этапах 7/8 с основной очередью. Почему у него НЕ есть свободные ресурсы и, следовательно, придется ждать? И самое главное, как обработчик «обрабатывает» userArray для его отправки? Какая часть кода сообщает обработчику отправить данные обратно тому, кто его вызвал? – marciokoko

+0

См. Мой отредактированный ответ. – deekay

+0

Что необходимо для преобразования 'NSData' в' NSString', а затем обратно в 'NSData'? – Rishab

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