等待 NSURLSessionDataTask 回来
Wait for NSURLSessionDataTask to come back
我是 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;
}
}];
}
请随时对我的新代码发表评论。样式、用法不正确、流程不正确;在这个学习阶段,反馈非常重要,这样我才能成为更好的开发人员:)
再次感谢您的回复。
你知道吗,用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;
}
}];
如果你想要简单的方法,那么不要为调用 webservice
单独设置 class。只需在 viewController.m 中创建方法即可。我的意思是在 viewController.m
中写入 sendRequestFromURL
并在完成处理程序中更新标签的文本,例如
- (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];
}
重写您的 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 作为参数传递给完成块。有关 sendRequestFromURL 版本的@Yahoho 的回答,它采用带有结果字符串和 NSError 参数的完成块。
(注意:我在 SO post 编辑器中编写了上面的代码。它可能有一些语法错误,但它旨在作为指南,而不是您可以 copy/paste 到位的代码。 Objective-C 块语法有点令人讨厌,我通常至少有一半的时间是第一次弄错。)
我是 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;
}
}];
}
请随时对我的新代码发表评论。样式、用法不正确、流程不正确;在这个学习阶段,反馈非常重要,这样我才能成为更好的开发人员:)
再次感谢您的回复。
你知道吗,用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;
}
}];
如果你想要简单的方法,那么不要为调用 webservice
单独设置 class。只需在 viewController.m 中创建方法即可。我的意思是在 viewController.m
中写入 sendRequestFromURL
并在完成处理程序中更新标签的文本,例如
- (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];
}
重写您的 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 作为参数传递给完成块。有关 sendRequestFromURL 版本的@Yahoho 的回答,它采用带有结果字符串和 NSError 参数的完成块。
(注意:我在 SO post 编辑器中编写了上面的代码。它可能有一些语法错误,但它旨在作为指南,而不是您可以 copy/paste 到位的代码。 Objective-C 块语法有点令人讨厌,我通常至少有一半的时间是第一次弄错。)