首页 iOS.& Swift Books 结合:Swift的异步编程

10
调试 由佛罗特州撰写

Understanding the event flow in asynchronous programs has always been a challenge. It is particularly the case in the context of Combine, as chains of operators in a publisher may not all emit events at the same time. For example, operators like throttle(for:scheduler:latest:) will not emit all events they receive, so you need to understand what’s going on. Combine provides a few operators to help with debugging your reactive flows. Knowing them will help you troubleshoot puzzling situations.

打印事件

The print(_:to:) operator is the first one you should use when you’re unsure whether anything is going through your publishers. It’s a passthrough publisher which prints a lot of information about what’s happening.

即使是这样的简单情况:

let subscription = (1...3).publisher
  .print("publisher")
  .sink { _ in }

输出非常详细:

publisher: receive subscription: (1...3)
publisher: request unlimited
publisher: receive value: (1)
publisher: receive value: (2)
publisher: receive value: (3)
publisher: receive finished

Here you see that the print(_:to:) operators shows a lot of information, as it:

  • 在收到订阅时打印并显示其上游发布者的描述。
  • 打印订户的需求请求,以便您可以看到正在请求有多少项。
  • 打印上游发布者发出的每个值。
  • 最后,打印完成事件。

There is an additional parameter that takes a TextOutputStream object. You can use this to redirect strings to print to a logger. You can also add information to the log, like the current date and time, etc. The possibilities are endless!

例如,您可以创建一个简单的记录器,该记录器显示每个字符串之间的时间间隔,以便您可以了解您的发布者发出值的快速值:

class TimeLogger: TextOutputStream {
  private var previous = Date()
  private let formatter = NumberFormatter()

  init() {
    formatter.maximumFractionDigits = 5
    formatter.minimumFractionDigits = 5
  }

  func write(_ string: String) {
    let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
    guard !trimmed.isEmpty else { return }
    let now = Date()
    print("+\(formatter.string(for: now.timeIntervalSince(previous))!)s: \(string)")
    previous = now
  }
}

在代码中使用非常简单:

let subscription = (1...3).publisher
  .print("publisher", to: TimeLogger())
  .sink { _ in }

结果显示每条打印行之间的时间:

+0.00111s: publisher: receive subscription: (1...3)
+0.03485s: publisher: request unlimited
+0.00035s: publisher: receive value: (1)
+0.00025s: publisher: receive value: (2)
+0.00027s: publisher: receive value: (3)
+0.00024s: publisher: receive finished

如上所述,这里的可能性是完全无穷无尽的。

表演事件 - 表演副作用

除了打印信息外,在特定事件上执行操作通常很有用。我们打电话给这一点 执行副作用,作为“在侧面”的操作,不要直接影响流的进一步发布,但可以具有修改外部变量的效果。

let request = URLSession.shared
  .dataTaskPublisher(for: URL(string: "//www.ohdvia.icu/")!)

request
  .sink(receiveCompletion: { completion in
    print("Sink received completion: \(completion)")
  }) { (data, _) in
    print("Sink received data: \(data)")
  }
.handleEvents(receiveSubscription: { _ in
  print("Network request will start")
}, receiveOutput: { _ in
  print("Network request data received")
}, receiveCancel: {
  print("Network request cancelled")
})
Network request will start
Network request cancelled
let subscription = request
  .handleEvents...
Network request will start
Network request data received
Sink received data: 153253 bytes
Sink received completion: finished

使用调试器作为最后的度假胜地

最后的手段操作员是你在调试器中某些时候内存的情况下拉的情况,因为没有别的东西帮助你弄清楚了什么是错的。

.breakpoint(receiveOutput: { value in
  return value > 10 && value < 15
})

关键点

  • Track the lifecycle of a publisher with the print operator,
  • Create your own TextOutputStream to customize the output strings,
  • Use the handleEvents operator to intercept lifecycle events and perform actions,
  • Use the breakpointOnError and breakpoint operators to break on specific events.

然后去哪儿?

你发现如何跟踪你的出版商正在做的事情,现在是时候......对于计时器!继续前一章,以了解如何以常规间隔触发事件。

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。