同步获取数据NSOperationQueue或NSURLSession

问题描述 投票:0回答:3

在我的应用程序中,当我们单击登录按钮时,我们必须进行 3 个服务器调用。

1) will make a server call to get an OAuth token for user credentials
2) with the token from (1) we will get user privileges
3) with the token from (1) and with the valid privilege from (2) we will get the data to be displayed on the page after login in a tableview. 

我对为此采取的方法感到困惑。使用操作队列添加依赖项或使用 NSURLSession 任务是一个好方法吗?

根据堆栈溢出解决方案之一 - 制作 NSURLSessionTasks 队列的最佳实践, NSURLSession 没有任何关于排序请求的逻辑,即使将每个主机的最大连接数设置为 1,它也只会调用先完成的内容的完成块

如果还有其他更好的方法请告诉我..

ios swift nsoperationqueue nsurlsessiondatatask
3个回答
1
投票

您可以使用

NSURLSession
任务。首先调用第一个 api 方法,您将在完成处理程序(块)中得到响应。现在将其存储在完成处理程序中的任何公共属性中(因为您想再次使用它)。从该完成处理程序调用第二个 api 方法,并从第二个方法的完成处理程序中,通过传递响应并使用存储了第一个 api 方法对象的公共属性来调用第三个 api 方法。

仅当响应到达时才会调用完成处理程序或块,因此通过这种方式您可以管理您的 api 调用。

希望这会有所帮助:)


1
投票

最终,使用“完成处理程序”传统方法的代码的简化版本可能如下所示:

fetchOAuthToken() { (token, error) in
    if let token = token {
        fetchPrivileges(token: token) { (privileges, error) in
            if let privileges = privileges {
               fetchData(token: token, privileges: privileges) { (data, error) in 
                   if let data = data {
                       // ... 
                   }
               }
            }
        }
    }
}

请注意,为了简洁起见,代码不包含错误处理,也没有取消的方法。

依赖关系是通过延续建立的 - 即完成处理程序。

另一种利用“类 Scala”future 的方法如下所示(使用 Promises 是非常相似的方法):

fetchOAuthToken().flatMap { token in
    fetchPrivileges(token: token).flatMap { privileges in
        fetchData(token: token, privileges).map { data in
            // ...
        }
    }
}.onFailure { error in
    print("Error: \(error)")
}

上面的语句创建了一个由三个任务组成的任务。

此方法包含完整的错误处理,即使它可能并不明显。生产版本与上面的这段代码没有太大区别 - 它可能只是添加了一种取消方法。

有一些第三方库实现了类似 Scala 的 futures 或 Promise。

添加取消的一种方法可能如下所示:

let cr = CancellationRequest()
fetchOAuthToken(ct: cr.token).flatMap { token in
    fetchPrivileges(token: token, ct: cr.token).flatMap { privileges in
        fetchData(token: token, privileges, ct: cr.token).map { data in
            // ...
        }
    }
}.onFailure { error in
    print("Error: \(error)")
}

稍后,您可以取消组合任务(无论当前正在执行什么):

cr.cancel()

注:

这个问题也可以

NSOperations
解决。但是,它需要
NSOperation
的三个子类,以及一两个线程安全辅助类,用于将 Op1 的结果“传输”到 Op2 的输入,并将 Op2 的结果“传输”到 Op3 的输入。我估计这将需要大约 500 行代码——对于 SO 的答案来说太多了;)

“类似 Scala”的 future 方法需要第三方库。取消需要另一个 - 或您自己的实现(这并不难),或一个提供所有功能的库。


0
投票

// 导入UIKit框架进行iOS应用开发 导入 UIKit

// 符合 UITableViewDelegate 和 UITableViewDataSource 协议的 ViewController 类 ViewController 类:UIViewController、UITableViewDelegate、UITableViewDataSource {

// Private instance of NewViewModel
private var newviewmodel: NewViewModel?

// IBOutlet for the UITableView in the storyboard
@IBOutlet weak var mTableview: UITableView!

// Override function called when the view loads
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Set up the table view and initialize the view model
    SetUpTableview()
    newviewmodel = NewViewModel()

    // Fetch todos using the view model and reload the table view on completion
    newviewmodel?.fetchTodos { [weak self] in
        DispatchQueue.main.async {
            self?.mTableview.reloadData()
        }
    }
}

// Function to set up the table view
func SetUpTableview() {
    mTableview.delegate = self
    mTableview.dataSource = self
    mTableview.register(UINib(nibName: "NewTableViewCell", bundle: nil), forCellReuseIdentifier: "NewTableViewCell")
}

// Number of sections in the table view
func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

// Number of rows in the table view, using the count from the view model
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return newviewmodel?.numberOfTodos ?? 0
}

// Configure and return a cell for the table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "NewTableViewCell", for: indexPath)  as! NewTableViewCell
    cell.Title.text = newviewmodel?.getTodoAtIndex(indexPath.row).title
    cell.CompleteLbl.text = "\(String(describing: newviewmodel?.getTodoAtIndex(indexPath.row).completed))"
    cell.Id.text = "\(newviewmodel?.getTodoAtIndex(indexPath.row).id ?? 0)"
    cell.UserName.text = "\(newviewmodel?.getTodoAtIndex(indexPath.row).userId ?? 0)"
    return cell
}

// Set the height for each row in the table view
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 200
}

}

© www.soinside.com 2019 - 2024. All rights reserved.