【问题标题】:How to properly declare a computed property, when calculation uses background threads?当计算使用后台线程时,如何正确声明计算属性?
【发布时间】:2018-03-17 15:36:24
【问题描述】:

我正在尝试声明一个由块组成的计算属性,在后台线程中执行。 所以,当我处理这个属性时,它是 nil,因为计算在它没有准备好时返回结果。如何更好地纠正这个问题?谢谢!

enum Result<T> {
   case error(error: Error)
   case success(data: T)
}

var userID: Result<CKRecordID>? {

  var result: Result<CKRecordID>? = nil

  container.fetchUserRecordID { recordID, error in
    if let error = error { result = .error(error: error) }
    if let recordID = recordID { result = .success(data: recordID) }
  }

  return result
}

【问题讨论】:

  • 你不能用闭包代替吗?那么当 fetch 用户返回时,它会执行一个 completed() 闭包并返回记录 id?
  • 不能从属性异步返回值。
  • 理论上你可以应用模式来让它等待响应,但是异步检索机制的整个想法是确保你不会阻塞主线程等待响应。所以,简短的回答是你真的不应该使用计算属性来包装异步方法。坚持使用fetchUserRecordID。如果您想将其包装在某个方法中,该方法在完成处理程序闭包中返回您的 Result&lt;T&gt; 类型,那很好,但不要尝试使用计算属性。
  • 罗布是对的。这不是将此类代码放入属性的正确位置。对于并发和多线程,请务必学习GCD(Grand Central Dispatch)主题,以了解如何正确管理后台任务。

标签: swift thread-safety grand-central-dispatch computed-properties


【解决方案1】:

有一个直接的解决方案,例如使用 GCD 信号量。但是,整个方法一开始似乎并不正确,因为这可能会在某些情况下导致不必要的挂起甚至死锁(例如在主线程中调用此属性)。更好的方法是将该代码移动到具有适当完成处理程序的方法中。

请记住,计算属性并非旨在替换方法。如果作用域中有一些复杂的(尤其是异步的)计算,您几乎可以将该代码从属性移到方法中。

但无论如何:

var userID: Result<CKRecordID>? {

    var result: Result<CKRecordID>? = nil

    var sema = DispatchSemaphore(value: 0)

    container.fetchUserRecordID { recordID, error in
        if let error = error { result = .error(error: error) }
        if let recordID = recordID { result = .success(data: recordID) }

        sema.signal()
    }

    _ = sema.wait(timeout: .distantFuture)

    return result
}

这里有一个等待异步操作完成的 GCD 信号量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    相关资源
    最近更新 更多