【问题标题】:iOS code executed out of orderiOS 代码乱序执行
【发布时间】:2014-07-20 20:29:30
【问题描述】:

我正在尝试根据 TouchID 登录尝试的结果更改标签上的文本,但是会出现延迟。当我尝试导航到另一个视图时也会发生同样的事情,例如我的视频在此处显示https://www.youtube.com/watch?v=gQI-93u_B1A

假设我有一个开关来处理不同的登录错误可能性,在每种情况下,如果我尝试更改标签,则会出现这种延迟,如果我使用这些情况来更改字符串变量的内容并尝试分配这个变量的内容到函数末尾的标签,断点显示将变量分配给标签的行在评估登录尝试的块之前执行,这将是代码:

func requestUserAuthentication() {

    var myContext:LAContext = LAContext()
    var authError:NSError?
    var myLocalizedRasonMessage = "Please authenticate using your fingerprint"

    if (myContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error:&authError)) {
        myContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedRasonMessage) { success, error in

            if (success) {
                self.requestAuthenticationOutput = "Login successful"
            }

            else {
                switch error.code {
                case LAError.AuthenticationFailed.toRaw():
                    self.requestAuthenticationOutput = "Login failed"

                case LAError.UserCancel.toRaw():
                    self.requestAuthenticationOutput = "User canceled"

                case LAError.SystemCancel.toRaw():
                    self.requestAuthenticationOutput = "System canceled"

                case LAError.UserFallback.toRaw():
                    self.requestAuthenticationOutput = "User pressed \"Enter Password\""

                default:
                    self.requestAuthenticationOutput = "TouchID is not configured"
                }
            }

        }
    }
    else {
        switch authError!.code {
        case LAError.TouchIDNotAvailable.toRaw():
            self.requestAuthenticationOutput = "No Touch ID on device"
        case LAError.TouchIDNotEnrolled.toRaw():
            self.requestAuthenticationOutput = "No fingers enrolled"
        case LAError.PasscodeNotSet.toRaw():
            self.requestAuthenticationOutput = "No passcode set"
        default:
            self.requestAuthenticationOutput = "Something went wrong getting local auth"
        }
    }

    self.statusLabel.text = self.requestAuthenticationOutput
}

在案例中作为输出结果的唯一方式立即执行的是警报。 我是 iOS 编程新手,但我怀疑它与异步代码执行有关,但我并不完全了解如何使用 GCD/queues/etc。如何让函数中的最后一行等待第一部分完成执行?任何想法将不胜感激。 (我用的是swift,但是测试ObjC得到了同样的结果)

【问题讨论】:

    标签: ios concurrency swift


    【解决方案1】:

    每当您看到这样的 UI 发生愚蠢的事情时,您应该考虑一下您调用 UI 更改的线程。

    阅读有关 evaluatePolicy 的文档:...您会发现这个有用的说明:

    该方法不会阻塞。相反,调用者必须提供一个回复块,以便在评估完成时异步调用。该块在未指定的线程上下文中在框架内部的私有队列上执行。除此之外,不保证块在哪个队列、线程或运行循环上执行。

    因为所有 UI 调用都必须在主线程上进行,我想如果您将 UI 调用分派到主线程(通过 dispatch_async() 之类的东西),您会看到更好的结果。

    【讨论】:

      【解决方案2】:

      简短的回答是,您不能让代码等到指纹认证成功或失败(前提是设备有能力)。

      获得所需效果的一种方法是在成功/错误块中获得结果后更新 UI。由于此代码不在主 (UI) 线程上执行,因此代码需要使用 GCD 在主线程中执行。

      下面列出的是一个函数 updateStatusLabel - 它执行此操作。即使在主线程中执行此操作——在 API 不可用的情况下,它也是非常安全的。

      func updateStatusLabel()
      {
         dispatch_async(dispatch_get_main_queue(), 
            {
              self.statusLabel.text = self.requestAuthenticationOutput
            })
      }
      
      var myContext:LAContext = LAContext()
      var authError:NSError?
      var myLocalizedRasonMessage = "Please authenticate using your fingerprint"
      
      if (myContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error:&authError)) {
          myContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedRasonMessage) { success, error in
      
              // ...Other code ommitted to set self.requestAuthenticationOutput
      
              // Update UI
              self.updateStatusLabel()
          }
      }
      else {
          switch authError!.code {
          case LAError.TouchIDNotAvailable.toRaw():
              self.requestAuthenticationOutput = "No Touch ID on device"
          case LAError.TouchIDNotEnrolled.toRaw():
              self.requestAuthenticationOutput = "No fingers enrolled"
          case LAError.PasscodeNotSet.toRaw():
              self.requestAuthenticationOutput = "No passcode set"
          default:
              self.requestAuthenticationOutput = "Something went wrong getting local auth"
          }
      }
      
        self.updateStatusLabel()
      }
      

      【讨论】:

      • 非常感谢!我已经尝试过使用 dispatch_asyn,但是,除了分配标签文本的行之外,我已经将所有内容都包裹在其中,结果我应该做的完全相反。
      猜你喜欢
      • 2019-01-09
      • 1970-01-01
      • 2017-04-04
      • 2021-12-20
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多