在iOS

时间:2016-02-23 09:00:21

标签: ios json swift asynchronous alamofire

我从Web服务获取JSON格式的数据(新闻文章)。需要将获取的数据转换为Article对象,并且应该在数据库中存储或更新该对象。我正在使用Alamofire向服务器发送请求,使用Core Data进行数据库管理 我的方法是创建一个DataFetcher类来获取JSON数据并将其转换为Article对象:

class DataFetcher {

    var delegate:DataFetcherDelegate?

    func fetchArticlesFromUrl(url:String, andCategory category:ArticleCategory) {
        //convert json to article
        //send articles to delegate

        getJsonFromUrl(url) { (json:JSON?,error:NSError?) in
            if error != nil {
                print("An error occured while fetching json : \(error)")
            }
            if json != nil {
                let articles = self.getArticleFromJson(json!,andCategory: category)
                self.delegate?.receivedNewArticles(articles, fromCategory: category)
            }
        }
    }

获取数据后,我将其发送到DataImporter类,将其存储在数据库中:

func receivedNewArticles(articles: [Article], fromCategory category:ArticleCategory) {

        //update the database with new articles
        //send articles to delegate
        delegate?.receivedUpdatedArticles(articles, fromCategory:category)
    }

DataImporter类将文章发送到其委托,在我的情况下是ViewController。当我只进行一次API调用(即fetchArticles)时,这种模式很好,但现在我需要再次调用API来获取类别。此调用需要在ViewController中调用fetchArticles之前执行 这是我的viewController的viewDidLoad方法:

override func viewDidLoad() {
        super.viewDidLoad()

        self.dataFetcher = DataFetcher()
        let dataImporter = DataImporter()
        dataImporter.delegate = self
        self.dataFetcher?.delegate = dataImporter

        self.loadCategories()
        self.loadArticles()
    }

我的问题是:

  1. 确保在另一个API之前执行API调用的最佳方法是什么?
  2. 我实现的模式是否很好,因为我需要为不同的API调用设置不同的方法?

1 个答案:

答案 0 :(得分:2)

  
      
  1. 确保在另一个API调用之前执行API调用的最佳方法是什么?
  2.   

如果您想确保两个或更多异步函数按顺序执行 ,您应该首先记住这一点:

  • 如果实现一个调用异步函数的函数,调用函数也会异步。

  • 异步函数应该有一种方法来通知来电者已完成。

如果你看一下网络函数getJsonFromUrl - 这是一个异步函数 - 它有一个完成处理程序参数,它是一个方法来通知调用者底层任务(网络请求)已完成。

现在,fetchArticlesFromUrl调用异步函数getJsonFromUrl,因此也变为异步。但是,在您当前的实现中,它无法向调用者发出其基础任务(getJsonFromUrl)已完成的信号。因此,您首先需要解决此问题,例如,通过添加适当的完成处理程序并确保最终将从正文中调用完成处理程序

您的函数loadArticlesloadCategories也是如此。我假设,这些是异步的,需要一种方法来通知调用者底层任务已经完成 - 例如,通过添加一个完成处理程序参数。

一旦你拥有了许多异步函数,就可以链接它们 - 也就是说,它们将被称为顺序

给出两个异步函数:

func loadCategories(completion: (AnyObject?, ErrorType?) -> ())
func loadArticles(completion: (AnyObject?, ErrorType?) -> ())

如下所示调用它们:

loadCategories { (categories, error) in
    if let categories = categories {
        // do something with categories:
        ...
        // Now, call loadArticles:
        loadArticles { (articles, error) in
            if let articles = articles {
                // do something with the articles
                ...
            } else {
                // handle error:
                ...
            }
        }
    } else {
        // handler error
        ...
    }
}
  
      
  1. 我实现的模式是否很好,因为我需要为不同的API调用设置不同的方法?
  2.   

恕我直言,你不应该将两个功能合并为一个,其中一个执行网络请求,另一个处理返回的数据。让它们分开。原因是,您可能希望明确指定"执行上下文" - 即调度队列,您希望在其中执行代码。通常,核心数据,CPU绑定功能和网络功能不应该或不能共享相同的调度队列 - 可能也是由于并发约束。因此,您可能希望通过指定调度队列的参数来控制代码的执行位置。

如果处理数据可能需要可感知的时间(例如> 100ms),请不要犹豫,并在专用队列(而不是主队列)上异步执行。链接几个异步函数,如上所示。

因此,您的代码可能包含四个异步函数,网络请求1,过程数据1,网络请求2,过程数据2.可能,您需要另一个专门用于将数据存储到Core Data中的函数。

其他提示:

除非有一个参数可以由调用者设置并明确指定"执行上下文" (例如,调度队列)应该调用完成处理程序,最好在并发全局调度队列上提交完成处理程序的调用。这样可以更快地执行并避免死锁。这与Alamofire相反,Alamofire通常在默认情况下调用主线程上的完成处理程序,并且容易出现死锁并且执行次优。如果您可以配置将执行完成处理程序的队列,请执行此操作。

优先在与主线程无关的调度队列上执行函数和代码 - 例如不是主队列。在您的代码中,似乎处理数据的大部分将在主线程上执行。只需确保UIKit方法将在主线程上执行。