异步设置变量并在视图控制器中使用

Set variable asynchronously and use within view controller

假设我有以下异步查询:

var kittens: [PFObject]!

self.tempView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "pushToView"))

var query = PFQuery(className: "Kittens")
query.findObjectsInBackgroundWithBlock({ (objects, error) in
    if let kittenObjects = objects as? [PFObject] {
        self.kittens = kittenObjects
    }
})

我有一个显示视图控制器的方法:

func pushToView() {
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let viewController: KittensViewController = mainStoryboard.instantiateViewControllerWithIdentifier("kittensViewController") as! KittensViewController
    viewController.kittens = self.kittens
    self.presentViewController(viewController, animated: true, completion: nil)
}

因为我在异步查询块内部设置 self.kittens 当我尝试访问呈现的视图控制器内部的 self.kittens 时显然不起作用(它始终为空)。

让这样的东西发挥作用的最佳方法是什么?

有几个选项。最主要的是您需要在查询完成时更新 KittensViewController

  • 在保存小猫数组的视图控制器中将 KittensViewController 设为 属性。查询完成后,您不仅会更新 self.kittens,还会更新 self.kittensViewController.kittens = kittenObjects。这不是最佳设计,但应该可以。

  • 更好的设计是在自己的控制器中进行查询 class,在完成时触发 NSNotification。然后,当新小猫到达并更新您的视图时,您可以在代码的任何地方收听。

查询抛出通知

    var query = PFQuery(className: "Kittens")
    query.findObjectsInBackgroundWithBlock({ (objects, error) in
        if let kittenObjects = objects as? [PFObject] {
             dispatch_async(dispatch_get_main_queue()) {
                 NSNotificationCenter.defaultCenter().postNotificationName("KittensQueryDidFinish", object:kittenObjects, userInfo:dataDict))
        }
    })

ViewController 注册通知并更新他的模型

在您的 viewDidLoad 中添加以下内容:

NSNotificationCenter.defaultCenter().addObserverForName("KittensQueryDidFinish", object: nil, queue: NSOperationQueue.mainQueue) { note in
     let kittens = note.object
     [weak self].kittens = kittens
}

通过这种方法,您可以随时随地获得更新。现在你必须在小猫已经加载时解决问题。因此,我建议您引入一个共享控制器对象(又名单例)来执行查询并存储小猫。然后你可以从任何地方访问小猫。

如果 var kittens: [PFObject]! 是你的 viewController 的 属性,我认为实现你想要的最简单的方法是这样声明它:

var kittens: [PFObject]! {
        didSet {
            dispatch_async(dispatch_get_main_queue(), { [weak self] in
                if let strongSelf = self {
                    strongSelf.pushToView()
                }
            })
        }

因此每次设置小猫时,它都会执行 pushToView() 功能。你将不得不改变这一行:

self.tempView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "pushToView"))

执行查询而不是pushToView()。例如:

self.tempView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "performFetch"))

当然,您需要创建函数:

func performFetch() {
  var query = PFQuery(className: "Kittens")
  query.findObjectsInBackgroundWithBlock({ (objects, error) in
      //should be created weak self to avoid retain cycles
      if let kittenObjects = objects as? [PFObject] {
          self.kittens = kittenObjects
      }
  })
}

但如果 kittens 不是 属性,您可以创建 performFetch() 函数,更改手势识别器并在使用 dispatch_async dispatch_get_main_queue() 设置 kittens 后添加 self.pushToView()(如在 didSet) 中,因为您需要更新主队列中的 UI。