2013-08-17 3 views
18

완료 핸들러를 이해하려고 시도 중임 & 블록. 완성 처리기가없는 많은 프로그래밍 프로그래밍 블록을 사용할 수 있다고 생각하지만 완성 처리기는 블록을 기반으로한다는 것을 알고 있습니다. (따라서 기본적으로 완료 핸들러는 블록이 필요하지만 다른 방법은 필요하지 않습니다.) 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; 
    }]; 

우리가 드 responseData & urlResponse & 오류가 완료되면 물건 (TWRequest을 수행) 반환을하는 방법을 요구하고있다. 리턴 할 때만 테스트가 허용 된 블록을 실행하고 활동 표시기를 중지합니다. 완전한!

지금이 내가 일하는 다른 앱이 셋업,하지만 난 함께 조각을 넣어려고 :

  1. fetchUsersWithCompletionHandler 분명하다 : 여기
    @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); 
          }); 
         } 
        }); 
    } 
    

    내 이해입니다 performRequestWithHandler의 동급어
  2. 도중에 GCD 호출이 있기 때문에 불행히도 이것은 다소 복잡합니다 ...

기본적으로 요청이 실행되고 오류가 처리되고 데이터가 처리 된 다음 처리기가 검사됩니다. 내 질문은이 처리기가 어떻게 작동하는가? 나는 그것이 메인 큐로 돌아가서 usersArray를 돌려 보낼 것이 존재하는지 이해한다. 그러나 usersArray가 채워질 때까지 기다리는 것을 어떻게 알 수 있습니까? 내가 뭘 혼란스럽게 생각하는 그 방법은 :이 경우에는 블록 안에 다른 블록, dispatch_async 호출이있다. 내가 찾고있는 것은 실제로 물건을 만들고 응답 데이터와 urlResponse를 반환 할 때를 알고있는 로직이다. 나는 그것의 동일한 애플 리케이션을 안다,하지만 나는 performRequestWithHandler에 대한 코드를 볼 수 없다.

답변

29

기본적으로이 경우에는 그런 식으로 작동합니다

  1. 당신은 fetchUsersWithCompletionHandler 전화 : 어떤 스레드에서 당신이 (아마 주를 형성) 좋아.
  2. 그것은 NSURLRequest 초기화하고 호출한다.이 dispath_async 때문에 dispatch_async은 (dispatch_get_global_queue ... 기본적 블록을 생성하고, 배경 큐 처리에 대한 예약을 는
  3. 은 현재 스레드는 fetchUsersWithCompletionHandler 잎 : 방법. 배경 대기열이 때, 배경을 몇 가지 리소스
    ... 지금

  4. 그리고이 있습니다까지

    ...
    시간, 통과 (: 그것은 동기 요청을 수행 - 그래서 데이터를 기다리는 주) : 아 운드 큐는 예약 된 작업을 소모, 무료

    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에서 멈춤)를 더 진행할 수 있습니다. 이 경우 단순히 블록에서 반환됩니다.

편집 : 질문에 대해서는 당신은 질문 : 항상 바쁜 될 것입니다 주/배경 큐처럼하지

  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가 있습니다 void 반환 유형이 Handler로 typedefe'd 차단하고 같이 NSArray *을 받아 선언

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

매개 변수. 유형 Handler의 매개 변수 블록으로 받아 로컬 이름 handler을 사용하여 액세스를 허용

+(void)fetchUsersWithCompletionHandler:(Handler)handler 

:

나중에, 당신은 당신의 기능을 가지고있다.

단계 # 8 :

단지 바로 (당신은/C++ 기능 어떤 C를 호출하는 것처럼) handler 블록을 호출하고 매개 변수로 usersArray를 전달
handler(usersArray); 

.

+0

2 질문 : (1) 왜 3 단계에서 "백그라운드 대기열에 몇 가지 무료 리소스가있을 때까지 시간이 지났습니다." 왜 무료 자원이 없습니까? 메인 대기열과 7/8 단계에서도 동일합니다. 왜 무료 리소스가 없으므로 기다려야합니까? 그리고 가장 중요한 것은 어떻게 처리기가 usersArray를 다시 처리하도록 "처리"합니까? 코드의 어떤 부분이 처리기에게 누가 데이터를 보낸 사람에게 데이터를 보내도록 지시합니까? – marciokoko

+0

수정 된 답변보기 – deekay

+0

NSData를 NSString으로 변환 한 다음 NSData로 다시 변환 할 필요가 있습니까? – Rishab