Ios 在苹果公司';s Foundation/Swift/Objective-C,runLoop.run如何阻塞,但仍然允许DispatchWorkItems处理?
为什么这段代码会这样执行?注意测试代码中的注释,这些注释指示通过和失败的行 更具体地说,Ios 在苹果公司';s Foundation/Swift/Objective-C,runLoop.run如何阻塞,但仍然允许DispatchWorkItems处理?,ios,swift,grand-central-dispatch,foundation,nsrunloop,Ios,Swift,Grand Central Dispatch,Foundation,Nsrunloop,为什么这段代码会这样执行?注意测试代码中的注释,这些注释指示通过和失败的行 更具体地说,RunLoop.current.run(直到:Date(timeintervalncenow:0.01))如何在那里等待,同时仍然允许处理DispatchWorkItem,{[weak self]in self?.name=newName}?如果线程正在运行循环中等待,那么线程如何处理任何工作项 (如果问题没有意义,请纠正我的理解) 班级人员{ 私有(集合)变量名称:String=“” func update
RunLoop.current.run(直到:Date(timeintervalncenow:0.01))
如何在那里等待,同时仍然允许处理DispatchWorkItem
,{[weak self]in self?.name=newName}
?如果线程正在运行循环中等待,那么线程如何处理任何工作项
(如果问题没有意义,请纠正我的理解)
班级人员{
私有(集合)变量名称:String=“”
func updateName(到newName:String){
DispatchQueue.main.async{self?.name=newName}中的[weak self]
}
}
类PersonTests:XCTestCase{
func testUpdateName(){
设sut=Person()
sut.updateName(改为:“Bob”)
xctasertequal(sut.name,“Bob”)//失败:`sut.name`仍然是“”`
assertEventually{sut.name==“Bob”}//通过
}
}
最终功能资产(
超时:时间间隔=1,
断言:()->Bool
) {
让timeoutDate=Date(timeIntervalSinceNow:timeout)
while Date()while
循环使执行停止,但是run
命令不只是等待,而是处理该线程的run循环上的事件,包括GCD源、计时器、调度块等的处理
FWIW,当您处理异步方法时,您可以:
使用完成处理程序
通常,如果您有一个异步方法,为了解释对象的状态(例如,何时关闭微调器,让用户知道何时完成),您需要提供一个完成处理程序。(这是假设简单的async
是一些更复杂的异步模式的简化。)
如果您真的希望有一个异步方法来异步改变对象,并且您的应用程序当前不需要知道何时完成,那么将该完成处理程序设置为可选:
func updateName(to name: String, completion: (() -> Void)? = nil) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
self?.name = name
completion?()
}
}
然后,您可以在单元测试中使用期望值,这是测试异步方法的标准方法:
func testUpdateName() {
let e = expectation(description: "Person.updateName")
let person = Person()
person.updateName(to: "Bob") {
e.fulfill()
}
waitForExpectations(timeout: 1)
XCTAssertEqual(person.name, "Bob")
}
使用“阅读器”
前一点是关于测试异步方法的一般观察。但是,如果您确实有一个异步改变对象的方法,您通常不会直接公开改变的属性,而是可以使用“reader”方法以线程安全的方式获取属性值。(例如,在读写器模式中,您可以异步更新,但您的读写器将等待任何挂起的写入首先完成。)
这样,考虑使用读写器模式:<代码>人>代码>:
class Person {
// don't expose the name at all
private var name: String = ""
// private synchronization reader-writer queue
private let queue = DispatchQueue(label: "person.readerwriter", attributes: .concurrent)
// perform writes asynchronously with a barrier
func writeName(to name: String) {
queue.async(flags: .barrier) {
self.name = name
}
}
// perform reads synchronously (concurrently with respect to other reads, but synchronized with any writes)
func readName() -> String {
return queue.sync {
return name
}
}
}
然后测试将使用readName
func testUpdateName() {
let person = Person()
person.writeName(to: "Bob")
let name = person.readName()
XCTAssertEqual(name, "Bob")
}
但如果没有同步读取的方法,通常也不会有异步写入的属性。如果仅从主线程使用,问题中的示例将有效。否则,你会有比赛条件
这也是XctExpection等待的工作方式吗?我一直在想这个问题。我明白了,这就像是一个函数调用,在这个特殊的情况下,它会耗尽调度队列case@matt我还没有深入了解期望代码,但我怀疑是这样。它当然不是单纯的等待(例如信号量等)。它可以像运行循环方法一样处理事件。确切地说,这正是我发现的神秘之处。:)@guptron-它比这更广泛,但这是基本的想法。请参阅旧的《线程编程指南》中关于的已存档章节。
func testUpdateName() {
let person = Person()
person.writeName(to: "Bob")
let name = person.readName()
XCTAssertEqual(name, "Bob")
}