在类函数中使用异步调用/完成处理程序时,更新控制器中变量的最佳方法是什么? [英] When using asynchronous calls/completion handlers in a class function whats the best way to update variables in my controller?
问题描述
我的应用程序对后端使用了Parse,并且我的应用程序中有很多异步的带有完成处理程序的调用.现在,我的应用有点混乱,因为我到处都有重复的函数来获取/设置Parse中的值,这是因为我不知道如何将函数放在类中,然后能够更新我所需要的任何东西.我正在我的控制器中做.
My app uses Parse for the backend and I have lots of asynchronous calls with completion handlers all over my app. Right now my app is a bit of a mess because I have repeated functions everywhere to get/set values in Parse, this is because I can't figure out how to put the function in my class and then be able to update whatever I'm doing in my controller.
示例:我有一个User类,该用户可以有几个关联的Car对象.理想情况下,我想我希望可以在整个应用程序中使用像"User.getCars"这样的类方法,但是我在几个不同的地方声明了以下函数.
Example: I have a User class and the user can have several associated Car objects. Ideally I think I would like a class method like "User.getCars" that I can use all over my app but instead I declare the below function in several different places.
var cars = [ParseCar]()
func getCar(){
let query = PFQuery(className:"Car")
query.whereKey("owner", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock
{
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
print("Successfully retrieved \(objects!.count) cars.")
for object in objects! {
self.cars.append(object as! ParseCar)
self.cars = objects! as! [ParseCar]
print("cars array is: \(self.cars)")
self.checkNumberOfCars()
}
} else {
showAlertPopup(self, titleText: "Error", messageText: "Network request to get users car failed")
print("Error: \(error!) \(error!.userInfo)")
}
}
}
所以我有2个具体问题...
So I have 2 specific questions...
-
我的"cars"数组在视图控制器中,我想用Parse的结果对其进行更新.如果我将"getCars"函数放在我的"User"类中,则可以执行User.getCars,该方法将调用getCars方法,但是如何将结果附加回我的"cars"数组?我无法从完成处理程序中返回任何内容,因此无法将其传递回去,对吗?而且我无法在"getCars"的开头设置一个变量,该变量会在完成处理程序中进行更新,然后返回该变量,因为返回将在完成处理程序运行之前发生.
My "cars" array is in the view controller and I want to update it with the results from Parse. If I were to put the "getCars" function in my "User" class I could do User.getCars which would call the getCars method but how do I append the results back to my "cars" array? I can't return anything from the completion handler so there's no way to pass it back, is there? and I can't set a variable at the beginning of "getCars" which gets updated in the completion handler then return it because the return will happen before the completion handler runs.
如果我返回了一组数据,则我的控制器"checkNumberofCars"中有一个函数可以对"cars"数组中的对象进行计数,如果有多个,它将触发一个弹出窗口,以便用户可以选择哪辆车他们想和他们一起工作.显然,自从在控制器中声明该类方法以来,我无法从该类方法中调用该函数,因此如何与我的控制器通信,一旦我拥有了一组汽车,便想运行"checkNumberofCars"函数?
If I get a set of data back I have a function in my controller "checkNumberofCars" which counts the objects in the "cars" array and if there are multiple it triggers a popup so the user can select which car they want to work with. Obviously I can't call that function from inside the class method since its declared in the controller so how do I communicate back to my controller that once I have the set of cars I then want to run the "checkNumberofCars" function?
关于处理这种一般情况的任何其他建议都很棒!
Any other advice on handling this general situation would be awesome!
推荐答案
从异步操作提供数据的最简单方法之一是使用闭包.这只是代码完成后调用的函数.例如:
One of the simplest ways to provide data from an asynchronous operation is to use a closure. This is just a function which your code calls when it's done. For example:
func getCar(completion: (ParseCar) -> Void) {
....
let someCar: ParseCar = ....
completion(someCar)
}
要管理复杂性,通常可以将问题分解成更简单的部分.没有正确的答案,这是一个建议:
To manage complexity, it is often effective to decompose the problem into simpler pieces. There is no right answer, here is one suggestion:
- 将Web服务代码封装到一个类中.
- 将通用视图控制器代码封装到一个类中.
- 从使用它的每个视图控制器中调用封装的代码.
您用于提取汽车的API可能如下所示:
Your API for fetching cars could look like this:
class CarsService {
func getCars(completion: ([ParseCar]?, NSError?) -> Void){
let query = PFQuery(className:"Car")
query.whereKey("owner", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if let objects = objects {
let output = [ParseCar]()
for object in objects {
output.append(object as! ParseCar)
}
completion(output, nil)
} else {
completion(nil, error)
}
}
}
}
API可以这样使用:
The API could be used like this:
let service = CarsService()
service.getCars() { cars, error in
if let cars = cars {
self.countCars(cars)
}
else if let error = error {
self.showCarsError(error)
}
}
您可以更进一步,将 countCars
和错误处理组合到另一个可重用的控制器中:
You could go one step further and combine countCars
and the error handling into another reusable controller:
class CarsController {
weak var viewController: UIViewController?
var service: CarsService
init(viewController: UIViewController, service: CarsService) {
self.viewController = viewController
self.service = service
}
func getCar(completion: (ParseCar?) -> Void) {
service.getCars() { cars, error in
if let cars = cars {
self.countCars(cars, completion: completion)
}
else if let error = error {
self.showError(error)
}
}
}
private func countCars(cars: [ParseCar], completion: (ParseCar?) -> Void) {
// Count cars and display prompt, e.g:
if cars.count == 0 {
completion(nil)
}
else if cars.count == 1 {
completion(cars.first)
}
else {
// Create UI to select car.
// Call completion callback with selected car:
completion(selectedCar)
}
}
private func showError(error: NSError) {
let alertViewController = // Create view controller...
viewController.presentViewController(alertViewController)
}
}
然后在多个视图控制器中重用此功能将相对容易:
It would then be relatively easy to reuse this functionality in multiple view controllers:
let carsService = CarsService()
carsController = CarsController(viewController: self, service: service)
carsController.getCar() { car in
print("Selected car = \(car)")
}
这篇关于在类函数中使用异步调用/完成处理程序时,更新控制器中变量的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!