异步json请求目标C



我正在开发我的第一个iPhone应用程序,我正在调用API以返回一些JSON,这些JSON填充了我的UI的不同元素。目前,我已经在一个助手类中实现了一个同步方法,我在viewDidLoad方法中调用它。

-(NSDictionary*) dataRequest: (NSString*)url withMethod:(NSString*)method
{
NSError *e;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:method];
NSURLResponse *requestResponse;
NSData *requestHandler = [NSURLConnection sendSynchronousRequest:request returningResponse:&requestResponse error:nil];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData: requestHandler options: NSJSONReadingMutableContainers error: &e];
return json;
}

这里的问题是我锁定了我的UI,我试图异步实现它,但在代码尝试填充UI元素很久之后,数据就被返回了。实现异步调用并用正确的数据填充UI元素的最佳和正确的方法是什么?

预计数据会在发出请求后很长时间(相对于发送请求后几乎即时执行下一行代码而言,很长时间)返回。诀窍是将UI的更新推迟到请求完成。

// optionally update the UI to say 'busy', e.g. placeholders or activity
// indicators in parts that are incomplete until the response arrives
[NSURLConnection sendAsynchronousRequest:request
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    // optionally update the UI to say 'done'
    if (!error) {
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData: requestHandler options: NSJSONReadingMutableContainers error: &e];
        // update the UI here (and only here to the extent it depends on the json)
    } else {
        // update the UI to indicate error
    }
}];

更抽象地——更正确地——考虑到所获取的数据很可能是应用程序模型的一部分。从服务器获取只是模型更改的一个原因。当模型因任何原因发生更改时,无论是通过用户操作还是此获取或其他事件,视图控制器的工作都是观察它是否发生了更改,并通知视图进行更新。

在某些情况下,发出同步请求是有意义的,但在同步请求完成时视图控制器正在等待(阻塞主线程)则没有意义。我写了一篇博客文章来帮助深入回答这个问题:http://jasoncross-ios-development.blogspot.com/2015/04/asynchronous-json-requests-in-objective.html.这篇文章还讨论了从web服务器为iOS应用程序使用JSON的其他问题:在不同的情况下需要考虑哪些设计模式。

iOS开发中最常见的模式之一是显示源自Web服务并通过JSON传输的数据。本文讨论了数据的同步和异步获取,以及其他相关的设计模式。

同步和异步http请求之间的区别是什么?在同步http请求中,当前执行的代码"阻塞"(停止并等待),直到请求完成。如果此代码在主线程上,则在进行网络调用时,应用程序将显示为冻结和损坏。这是一个坏主意,违背了最佳实践,应该始终避免。

相反,异步http请求允许当前执行的代码在请求启动和请求运行时继续执行。一旦请求完成,代码就会向侦听器"报告"。在NSURLConnection sendAsynchronousRequest:queue:completionHandler:的情况下,侦听器是作为方法参数传入块的方法的调用方。在从主线程发出网络请求的情况下,用户界面不锁定,应用程序继续响应用户手势,这是公认的最佳实践。

简而言之,这就是您需要知道的全部内容:使用异步方法。但这就引出了一个问题:如果不应该使用同步方法,为什么它仍然可用?答案是,在许多情况下,发出同步请求是有意义的。博客文章的其余部分详细介绍了其中的一些场景。

考虑使用"服务层"进行请求,如下所示:

模型类。这些通常反映JSON对象服务层。这些类了解API并进行网络调用以获取JSON。当网络调用完成时,JSON被转换为本机模型类(Objective-C对象)。查看控制器。这些类处理用户交互,并根据需要从服务层请求数据。

如果您的iOS应用程序只需要提取一次数据,而不关心更新,则服务层可以执行异步请求,并在请求完成时使用块更新调用方。主叫方可以根据以下内容向服务层请求数据:

[serviceLayer fetchDataFromAPIWithResponseBlock:^(NSArray * arrayOfObjects, NSError *error) {
if (nil != error) {
    // deal with error appropriately
}
else if (nil != arrayOfObjects) {
    // update the user interface
}
}]; 

但是,如果您的应用程序涉及服务器或客户端上发生更改的数据,那么您将需要一种方法来协调模型更改与用户界面的更新。在这种情况下,您应该考虑使用委托设计模式或观察者设计模式。如果使用委托模式,则视图控制器将是服务层的委托。当模型数据发生更改时,服务层会通知代理。在这种情况下,服务层可以使用同步请求,因为视图控制器在等待服务层的回复时没有阻塞。实现委托模式的另一种方法是使用NSNotificationCenter在模型数据更改时通知视图控制器。使用委托模式和通知之间最大的决定因素是,当模型数据发生变化时,需要通知多少视图控制器。如果只有一个视图控制器,则可以将其作为代理。如果有多个视图控制器,则可以通过通知通知所有视图控制器。

如果模型对象的各个属性正在更改(可能来自iOS应用程序中的其他地方),则应考虑在模型更改时使用键值观测(KVO)来更新视图。

更深入的讨论可以在上面提到的博客文章中找到。

相关内容

最新更新