2016-07-26 34 views
0

我有一个Authenticator类具有sendEmailForPasswordRecovery方法实施asynchronous呼叫发送电子邮件给firebase用户密码恢复。试图了解异步通话行为和主队列在快速

func sendEmailForPasswordRecovery(email: String, completion: CallBackWithError) { 
     FIRAuth.auth()?.sendPasswordResetWithEmail(email, completion: { (error: NSError?) in 
      completion(error) 
     }) 
    } 

我打电话这个functionUIViewController

Authenticator().sendEmailForPasswordRecovery(email, completion: { (error: NSError?) in 
     print("operation completed. error: \(error)") 
     self.completion?() 
}) 

完成块简单地调用这个函数。它只是隐藏一个弹出图,淡出blurEffect移除从其父视图

func removeForgotPasswordScreen() { 
     UIView.animateWithDuration(0.5, animations: { 
      self.blurEffectView.alpha = 0 
      self.containerForEmail.alpha = 0 
     }) { (_: Bool) in 
      self.containerForEmail.hidden = true 
      self.blurEffectView.removeFromSuperview() 
     } 
    } 

,但在执行Authenticator().sendEmailForPasswordRecovery我可以看到,错误是在控制台为零。但是,视图在40-50秒之后才会消失。但是当我在dispatch_async内完成完成时,我立刻得到了我的结果。

Authenticator().sendEmailForPasswordRecovery(email, completion: { (error: NSError?) in 
    // self.completion?() <----- this was causing delay 

    dispatch_async(dispatch_get_main_queue(), { 
      self.completion?() <------ Now it updates immidiately 
    }) 
}) 

火力地堡sendPasswordResetWithEmail有签名:

public func sendPasswordResetWithEmail(email: String, completion: FIRSendPasswordResetCallback?) 

和它说

@param完成可选;当 请求结束时调用的块。在将来的主线程上异步调用 。

我不明白的是为什么PopUp在第一个延迟后消失了,dispatch_assync是怎么做的。

+1

一个快速建议 - 我推荐阅读关于Grand Central Dispatch(GCD),这是iOS开发中用于并发处理的系统。 –

+0

你什么时候设置完成变量 - 你确定它在第一种情况下设置正确吗?在removeForgotPasswordScreen中添加一个断点,以查看何时实际调用了该断点以及从哪个线程。 – fishinear

+0

@MattLeFleur感谢您的建议。这非常有帮助。 – Ccr

回答

1

设置动画的意见,你应该使用主队列,并sendPasswordResetWithEmail在主队列中不调用你的块,并强调:在今后的主线程异步调用。

dispatch_async将排队执行块划分成线程的队列中,但此线程不保证主线程或具有低优先级,如dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)){},所以我猜sendPasswordResetWithEmail实现它的方法使用DISPATCH_QUEUE_PRIORITY_DEFAULT或其他低优先级,这使得你的代码不能保证立即执行,实际上执行的时间并不准确。下面是一个测试,标签的动画有时会在几秒钟后执行,有时不会执行。

注意事项:处理或设置UI必须在主线程中,这只是为了测试。

class ViewController: UIViewController { 
let label = UILabel() 

override func viewDidLoad() { 
    super.viewDidLoad() 
    self.view.backgroundColor = UIColor.whiteColor() 
    self.label.frame = CGRectMake(0, 0, 100, 21) 
    self.label.text = "Label" 
    self.label.center = self.view.center 
    self.view.addSubview(self.label) 

    let button = UIButton(type: .System) 
    button.setTitle("Button", forState: .Normal) 
    button.frame = CGRectMake(CGRectGetMinX(self.label.frame), CGRectGetMaxY(self.label.frame) + 20, 50, 20) 
    button.addTarget(self, action: NSSelectorFromString("handleButtonPressed:"), forControlEvents: .TouchUpInside) 
    self.view.addSubview(button) 
} 

func handleButtonPressed(sender: UIButton) { 
    // the block will execute not in main queue 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
     NSThread.sleepForTimeInterval(1) 
     // because the queue is not main queue, the animation not execute immediatelly, even not execute. 
     UIView.animateWithDuration(3, animations: { 
      var origin = self.label.frame.origin 
      origin.y += 100 
      self.label.frame.origin = origin 
      }, completion: { (complete) in 
       self.label.hidden = true 
     }) 
    } 
} 
} 

但是,如果您更改为主队列,则动画将按您的希望时间执行。

func handleButtonPressed(sender: UIButton) { 
    // in main queue 
    dispatch_async(dispatch_get_main_queue()) { 
     NSThread.sleepForTimeInterval(1) 

     UIView.animateWithDuration(3, animations: { 
      var origin = self.label.frame.origin 
      origin.y += 100 
      self.label.frame.origin = origin 
      }, completion: { (complete) in 
       self.label.hidden = true 
     }) 
    } 
} 

所以,你应该执行所有的代码,在主队列(主线程)中与UI进行比较。

希望答案可以帮到你。这是GCD's guide,它仔细和完整地解释。