在类函数中使用异步调用/完成处理程序时,更新控制器中变量的最佳方法是什么? [英] When using asynchronous calls/completion handlers in a class function whats the best way to update variables in my controller?

查看:91
本文介绍了在类函数中使用异步调用/完成处理程序时,更新控制器中变量的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序对后端使用了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...

  1. 我的"cars"数组在视图控制器中,我想用Parse的结果对其进行更新.如果我将"getCars"函数放在我的"User"类中,则可以执行User.getCars,该方法将调用getCars方法,但是如何将结果附加回我的"cars"数组?我无法从完成处理程序中返回任何内容,因此无法将其传递回去,对吗?而且我无法在"getCars"的开头设置一个变量,该变量会在完成处理程序中进行更新,然后返回该变量,因为返回将在完成处理程序运行之前发生.

  1. 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:

  1. 将Web服务代码封装到一个类中.
  2. 将通用视图控制器代码封装到一个类中.
  3. 从使用它的每个视图控制器中调用封装的代码.

您用于提取汽车的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆