2016-06-04 2 views
0

저는 일반적으로 Objective C 및 iOS 개발에 익숙하지 않습니다. http 요청을 만들고 레이블에 내용을 표시하는 응용 프로그램을 만들려고합니다.NSURLSessionDataTask가 돌아 오기를 기다리십시오.

테스트를 시작했을 때 로그에 데이터가 있다는 것을 나타 냈지만 레이블이 비어 있다고 나타났습니다. 분명히 이것은 라벨 텍스트가 업데이트 될 때 응답이 준비되지 않았기 때문에 발생합니다.

이 문제를 해결하기 위해 상단에 루프를 달았지만이 문제를 해결할 더 좋은 방법이 있어야합니다.

ViewController.m

- (IBAction)buttonSearch:(id)sender { 

    HttpRequest *http = [[HttpRequest alloc] init]; 
    [http sendRequestFromURL: @"https://en.wiktionary.org/wiki/incredible"]; 

    //I put this here to give some time for the url session to comeback. 
    int count; 
    while (http.responseText ==nil) { 
     self.outputLabel.text = [NSString stringWithFormat: @"Getting data %i ", count]; 
    } 

    self.outputLabel.text = http.responseText; 

} 

HttpRequest.h

#import <Foundation/Foundation.h> 

@interface HttpRequest : NSObject 

@property (strong, nonatomic) NSString *responseText; 

- (void) sendRequestFromURL: (NSString *) url; 
- (NSString *) getElementBetweenText: (NSString *) start andText: (NSString *) end; 

@end 

HttpRequest.m

@implementation HttpRequest 

- (void) sendRequestFromURL: (NSString *) url { 

    NSURL *myURL = [NSURL URLWithString: url]; 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL]; 
    NSURLSession *session = [NSURLSession sharedSession]; 


    NSURLSessionDataTask *task = [session dataTaskWithRequest: request 
              completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
               self.responseText = [[NSString alloc] initWithData: data 
                          encoding: NSUTF8StringEncoding]; 
              }]; 
    [task resume]; 

} 

덕분에 많은 매우 유용한 의견을 많이 읽은 후 도움말 :

업데이트

여기 제가 요점을 잃어버린 것을 깨달았다. 따라서 기술적으로 NSURLSessionDataTask는 비동기 적으로 호출을 할 대기열에 작업을 추가 한 다음 작업에서 생성 된 스레드가 완료되면 실행하려는 코드 블록과 함께 해당 호출을 제공해야합니다.

Duncan은 응답과 코드 주석에 많은 감사를드립니다. 그 점이 나를 이해하는 데 많은 도움이되었습니다.

제공된 정보를 사용하여 절차를 다시 작성했습니다. 그것들은 약간 장황하다. 그러나 나는 그것이 지금 전체 개념을 이해하는 것을 원했다.

HttpRequest.m

- (void) sendRequestFromURL: (NSString *) url 
       completion:(void (^)(NSString *, NSError *))completionBlock { 

    NSURL *myURL = [NSURL URLWithString: url]; 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL]; 
    NSURLSession *session = [NSURLSession sharedSession]; 


    NSURLSessionDataTask *task = [session dataTaskWithRequest: request 
              completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 

               //Create a block to handle the background thread in the dispatch method. 
               void (^runAfterCompletion)(void) = ^void (void) { 
                if (error) { 
                 completionBlock (nil, error); 
                } else { 
                 NSString *dataText = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; 
                 completionBlock(dataText, error); 
                } 
               }; 

               //Dispatch the queue 
               dispatch_async(dispatch_get_main_queue(), runAfterCompletion); 
              }]; 
    [task resume]; 

} 

ViewController.m이

- (IBAction)buttonSearch:(id)sender { 

    NSString *const myURL = @"https://en.wiktionary.org/wiki/incredible"; 

    HttpRequest *http = [[HttpRequest alloc] init]; 

    [http sendRequestFromURL: myURL 
        completion: ^(NSString *str, NSError *error) { 
         if (error) { 
          self.outputText.text = [error localizedDescription]; 
         } else { 
          self.outputText.text = str; 
         } 
        }]; 
} 

내 새로운 코드에 대한 의견을 보내 주시기 바랍니다 (내가 그들을 중첩보다는 코드 블록을 선언하고있다). 스타일, 잘못된 사용법, 잘못된 흐름; 피드백은 학습의이 단계에서 매우 중요하므로 더 나은 개발자가 될 수 있습니다.

다시 한번 답장을 보내 주셔서 감사합니다.

+0

'dataTaskWithRequest' 호출에서 제공하는 완료 핸들러 블록을 사용해야한다. 블록을'sendRequestFromURL'의 매개 변수에 완성 블록으로 추가하고, 요청이 끝났을 때 그 블록을 호출하십시오. 블록에서 레이블을 업데이트하기위한 코드를 작성하십시오 – luk2302

답변

0
가 완료 블록을 적용하려면 sendRequestFromURL 기능을 다시 작성

, 당신의 viewController.m에서 sendRequestFromURL를 작성하고 같은 완료 핸들러 뭔가 레이블의 텍스트를 업데이트 의미 : 당신이 sendRequestFromURL를 호출 할 때, 그리고

- (void) sendRequestFromURL: (NSString *) url 
    completion: (void (^)(void)) completion 
{ 
    NSURL *myURL = [NSURL URLWithString: url]; 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL]; 
    NSURLSession *session = [NSURLSession sharedSession]; 


    NSURLSessionDataTask *task = [session dataTaskWithRequest: request 
     completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 
     { 
     self.responseText = [[NSString alloc] initWithData: data 
      encoding: NSUTF8StringEncoding]; 
     if (completion != nil) 
     { 
      //The data task's completion block runs on a background thread 
      //by default, so invoke the completion handler on the main thread 
      //for safety 
      dispatch_async(dispatch_get_main_queue(), completion); 
     } 
     }]; 
     [task resume]; 
} 

을 통과 요청이 완료 블록으로 준비되면 실행하려는 코드에서 다음을 입력하십시오.

[self.sendRequestFromURL: @"http://www.someURL.com&blahblahblah", 
    completion:^
    { 
    //The code that you want to run when the data task is complete, using 
    //self.responseText 
    }]; 

    //Do NOT expect the result to be ready here. It won't be. 

위 코드는 completi 코드가 응답 텍스트를 인스턴스 변수에 저장했기 때문에 매개 변수가없는 블록에서. 응답 데이터와 NSError를 완료 블록에 매개 변수로 전달하는 것이 더 일반적입니다. 결과 문자열과 NSError 매개 변수로 완료 블록을 사용하는 sendRequestFromURL 버전에 대한 @ Yahoho의 대답을 참조하십시오.

(참고 : 위의 코드는 SO 포스트 편집기에서 작성한 것입니다. 구문 오류가있을 수 있지만 가이드가 아닌 코드를 복사하여 붙여 넣기 할 수 있습니다. Objective-C 블록 구문은 다소 다릅니다. 불쾌하고 보통 적어도 시간의 절반 이상은 처음에는 틀리게된다.)

+0

답변 던컨에 감사드립니다. 이것은 나를 많이 도왔다. 나는 의견을 사용하여 코드를 업데이트하고, 엿봄을 가지고 자유롭게 어떤 새로운 신자 실수든지 느끼고 –

1

너는 무엇을, 너의 생명을 구하기 위해 AFNetworking을 사용하여, 알아.

- (void)sendRequestFromURL:(NSString *)url completion:(void(^)(NSString *str, NSError *error))completionBlock { 

NSURL *myURL = [NSURL URLWithString: url]; 
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL]; 
NSURLSession *session = [NSURLSession sharedSession]; 


NSURLSessionDataTask *task = [session dataTaskWithRequest: request 
             completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
              dispatch_async(dispatch_get_main_queue(), ^{ 
               if (error) { 
                completionBlock(nil, error); 
               } else { 
                completionBlock([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], error); 
               } 
              }); 
             }]; 
[task resume]; 
} 

을하고 전화 webservice에 대해 별도의 클래스를하지 마십시오 다음 쉬운 방법을 원한다면이

[http sendRequestFromURL:@"https://en.wiktionary.org/wiki/incredible" completion:^(NSString *str, NSError *error) { 
    if (!error) { 
     self.outputLabel.text = str; 
    } 
}]; 
+0

귀하와 귀하의 답변은 매우 유사합니다. NSURLSessionDataTask의 완료 핸들러는 기본적으로 백그라운드 스레드에서 호출되기 때문에 기본 스레드에서 완료 블록을 호출하는 것이 좋습니다. 완료 블록에서 UI를 잊어 버리고 직접 시도하려고합니다. –

+0

덕분에 생각 나게하는,하지만 난 메인 스레드에서 completionBlock를 호출 않았다,'dispatch_async (dispatch_get_main_queue()^{ 경우 (오류) { completionBlock (전무, 오류) } 다른 { completionBlock ([[있는 NSString의 ALLOC] initWithData : 데이터 인코딩 : NSUTF8StringEncoding], 오류); } }); ' – Yahoho

+0

그래. (투표를했습니다.) 내가 어떻게 그리워했는지 모르겠습니다. –

0

처럼 호출 :

또는 단지는 HttpRequest에의 sendRequestFromURL을 수정합니다. 그냥 viewController.m에서 대신 메소드를 작성하십시오.내가

- (void) sendRequestFromURL: (NSString *) url { 

NSURL *myURL = [NSURL URLWithString: url]; 
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL]; 
NSURLSession *session = [NSURLSession sharedSession]; 


NSURLSessionDataTask *task = [session dataTaskWithRequest: request 
             completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 

              dispatch_async(dispatch_get_main_queue(), ^{ 

               self.responseText = [[NSString alloc] initWithData: data 
                          encoding: NSUTF8StringEncoding]; 

               self.outputLabel.text = self.responseText; 
              }) 

             }]; 
[task resume]; 

}