等待NSURLSessionDataTask返回

时间:2016-06-04 10:07:24

标签: ios objective-c nsurlconnection nsurlsession

我是Objective C和iOS开发的新手。我正在尝试创建一个可以发出http请求并在标签上显示内容的应用。

当我开始测试时,我注意到标签是空白的,即使我的日志显示我有数据。显然,这是因为标签文本更新后响应未准备就绪。

我在顶部放了一个循环来解决这个问题,但我几乎肯定必须有一个更好的方法来解决这个问题。

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;
                      }
                  }];
}

请随时评论我的新代码。风格,使用不当,流量不正确;反馈在这个学习阶段非常重要,所以我可以成为一个更好的开发者:)

再次感谢回复。

3 个答案:

答案 0 :(得分:1)

你知道吗,用AFNetworking来挽救你的生命。

或者只是修改你的HttpRequest的sendRequestFromURL:

- (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];   
}

并像这样调用

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

答案 1 :(得分:0)

如果你想轻松一点,那么请不要为电话webservice分别上课。只需在viewController.m中创建meethod即可。我的意思是在您的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];

}

答案 2 :(得分:0)

重写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];
}

然后,当您调用sendRequestFromURL时,在请求准备好作为完成块时传入您要运行的代码:

[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.

上面的代码使用没有参数的完成块,因为您的代码将响应文本保存到实例变量中。将响应数据和NSError作为参数传递给完成块更为典型。请参阅@ Yahoho对sendRequestFromURL版本的回答,该版本采用带有结果字符串和NSError参数的完成块。)

(注意:我在SO post编辑器中编写了上面的代码。它可能有一些语法错误,但它是作为指南,而不是可以复制/粘贴到位的代码。目标-C块语法有点讨厌我通常至少有一半的时间第一次弄错了。)