Swift 函数不会很快返回

Posted

技术标签:

【中文标题】Swift 函数不会很快返回【英文标题】:Swift function does not return soon enough 【发布时间】:2015-11-05 17:16:14 【问题描述】:

我正在尝试将我的应用程序移至 MVC,我有一个 Parse 查询已移至模型类中的一个函数,该函数返回一个 Bool。

当按下下面的 ViewController 中的按钮时,应该运行模型函数“parseQuery”,返回一个布尔值,然后我需要使用该布尔值继续。目前,if 语句在函数完成之前执行,所以它总是检测到 false。

如何确保函数完成后if语句完成?

@IBAction func showAllExpiredUsers(sender: AnyObject) 

    var success = searchResults.parseQuery()

    if success 
        print("true")
     else 
        print("false")
    
    //I have also tried:

    searchResults.parseQuery()

    if searchResults.parseQuery() 
        print("true")
     else 
        print("false")
    

【问题讨论】:

你试过把函数放到viewDidLoad吗? parseQuery() 是您创建的函数吗?这不在解析 API 中 让我猜猜 - 函数是异步的? 谢谢大家,将它放在 viewDidLoad 中是行不通的,因为它需要在不同的时间调用才能返回更新的结果。 parseQuery() 是我的函数的名称,其中包含一个常规的“findObjectsInBackgroundWithBlock”。我还没有触及同步,所以我猜它默认是异步的。 【参考方案1】:

您有几个选择,但问题是由于异步调用造成的。

Parse 是否公开相同的函数,并带有完成块?

如果是,则将 Bool 的处理放在完成块中,在异步任务完成时调用。

如果不是,我怀疑,您可以创建一个 NSOperationQueue,maxConcurrency 为 1(因此它是串行的)并将调用调度到队列中

func addOperationWithBlock(_ block: () -> Void)

这是在队列中调用的。您需要全局存储成功 bool,以便您可以在第二个排队的块操作中访问它,以检查成功状态。

更新:

我没有使用 parse,但是检查 findObjectsInBackgroundWithBlock (https://parse.com/docs/ios/guide#queries) 的文档需要一个完成块,您可以在其中处理结果,更新您的布尔值。

我不确定您要做什么。您不需要拥有查询的成功状态。你可以检查

if (!error)  
   // do stuff 
 else  
  //error occurred - print("error \(error.localizedDescription)"

检查示例。

您需要了解的是线程。异步任务提供了一个完成块,因为它是异步的,它被分派到另一个线程进行处理。我不确定您对线程了解多少,但有一种叫做线程池的东西。该线程池由队列访问。线程池由操作系统管理,并确保可用线程可以被需要完成工作的队列使用。当用户与应用程序交互时,这(以及所有 UI 工作)都在主线程上完成。

因此,只要某些处理会干扰可能的交互或 UI 更新,就应该从主线程分派(Grand Central Dispatch)或排队(NSOperationQueue,建立在 GCD 之上)。

无论如何,这就是为什么 findObjectsInBackgroundWithBlock 调用从主线程分派出去的原因,否则它会阻塞主线程直到它完成,从而破坏用户的体验。此外,如果主线程被阻塞超过 1 分钟(我上次检查时),操作系统的看门狗会杀死你的进程。

所以是的,将布尔值分配给块的返回,将获得函数的返回,这发生在完成块完成之前。完成块是您在函数完成后编写一些要完成的事情的地方。因此,查询被分派到另一个线程并开始处理,发送此工作进行处理的线程继续执行其余的工作。所以之后直接检查布尔值是行不通的,因为另一个线程不完整。就算其他线程及时完成了,后台线程和主线程是怎么连接的?

这是块(函数指针)的美妙之处,它更加简洁和优化,并且保持代码紧凑。旧的方式,仍然用于一些旧的框架,是委托,它将调用代码与回调分离并添加委托依赖。积木很漂亮。

同样重要的是要注意完成块并不总是在主线程上调用。在许多情况下,由您将工作分派回主线程,以处理需要使用完成块中可用的对象完成的任何 UI 工作。

【讨论】:

很好的回复,谢谢。您能解释一下“公开相同的功能”是什么意思吗?我的函数是一个常规的 findObjectsInBackgroundWithBlock,我尝试从完成块中更新 Bool,但这发生在紧要关头之后。我一直在关注 addOperationWithBlock,希望这会有所帮助,但它还没有工作 我非常感谢详细的回复,它让事情变得更加清晰。听起来我可能需要将查询保留在控制器而不是模型中。我需要返回布尔值的原因实际上是说如果为真则显示警报,如果为假则显示错误警报,问题是我无法从模型中成功调用警报,我假设这是不可能的,但如果是然后让我知道。再次感谢。 我刚刚尝试了一项实际可行的工作。我只是简单地调用了该函数并让该函数更新了一个全局 Bool。然后在执行 if/else 之前运行延迟函数两秒钟,它起作用并触发了警报……很高兴看到它起作用,但不幸的是它并不真正可行。 @BenSullivan - 抱歉延迟回复。因此,在架构方面,区块可以沿链向下传递。所以一个常见的例子是你的控制器调用模型上的一个接口,它需要一个完成块。您可以向控制器接口添加一个完成块,以便您的 VC 调用控制器上的函数,传入一个使用完成块中可用变量的完成块,例如响应、错误,然后显示警报视图。您的控制器可以与模型交互,将从 VC 发送的完成块直接传递给模型的函数。 当模型完成时,它会使用参数调用完成,这将触发从 VC 通过控制器传递的块。您还可以从控制器向模型发送一个块,即带有更多参数,因此您可以在控制器(例如网络对象)内部拦截模型的回调,然后在完成所需的操作后从 VC 调用该块到,最后调用 VC 完成块并显示警报。例如。与服务器同步数据。 UI控制>网络控制器>将下载的数据传递给模型>成功>通知服务器>调用VC完成块【参考方案2】:

查询可能需要一些时间来运行,并且应该在后台线程中运行,并使用回调函数在响应完成时处理响应。

看Documentation

具体看query.findObjectsInBackgroundWithBlock代码:

var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock 
  (objects: [PFObject]?, error: NSError?) -> Void in

  if error == nil 
    // The find succeeded.
    print("Successfully retrieved \(objects!.count) scores.")
    // Do something with the found objects
    if let objects = objects as? [PFObject] 
      for object in objects 
        print(object.objectId)
      
    
   else 
    // Log details of the failure
    print("Error: \(error!) \(error!.userInfo!)")
  

上面的代码会在从 Parse 获取结果时执行查询并运行块中的代码。这称为异步任务,更多信息请查看this guide

【讨论】:

谢谢,这就是我目前的做法,但是该块还没有很快完成。

以上是关于Swift 函数不会很快返回的主要内容,如果未能解决你的问题,请参考以下文章

Swift:如何在异步 urlsession 函数中返回一个值?

Swift:如何在异步 urlsession 函数中返回一个值?

Swift 5.2 UIAccessibility.isAssistiveTouchRunning 永远不会返回 true,也不会触发 UIAccessibility.assistiveTouchSt

Swift4中的completionHandler返回字符串

当内容为 nil 时,Swift 保护语句不会返回

返回视图时Swift Timer不会重新启动