问题描述
我有这种形式的代码:
func myFunction(<...>, completionHandler: (ResponseType) -> Void) {
<prepare parameters>
mySessionManager.upload(multipartFormData: someClosure,
to: saveUrl, method: .post, headers: headers) { encodingResult in
// encodingCompletion
switch encodingResult {
case .failure(let err):
completionHandler(.error(err))
case .success(let request, _, _):
request.response(queue: self.asyncQueue) { response in
// upload completion
<extract result>
completionHandler(.success(result))
}
}
}
}
并测试如下代码:
func testMyFunction() {
<prepare parameters>
var error: Error? = nil
var result: MyResultType? = nil
let sem = DispatchSemaphore(value: 0)
var ran = false
myFunction(<...>) { response in
if ran {
error = "ran twice"
return
}
defer {
ran = true
sem.signal()
}
switch response {
case .error(let err): error = err
case .success(let res): result = res
}
}
sem.wait()
XCTAssertNil(error, "Did not want to see this error: \(error!)")
<test response>
}
我使用信号量来阻塞主线程,直到请求被异步处理为止;这对于我所有其他Alamofire要求都很有效-但不是这个要求。测试挂起。
(请注意,使用不会改变。)
I use a semaphore to block the main thread until the request is processed asynchronously; this works fine for all my other Alamofire requests -- but not this one. The test hangs.
(Note bene: Using active waiting does not change things.)
使用调试器,我发现
- 所有执行的代码都很好,但是
- 。
- all code that executes does so just fine but
encodingCompletion
is never called.
现在,我最好的猜测是 DispatchQueue.main.async
说:在有时间的时候在主线程上执行此操作 –它永远不会,因为我的测试代码在那儿阻塞了(无论如何都会运行进一步的测试)。
Now my best guess is that DispatchQueue.main.async
says, "execute this on the main thread when it has time" -- which it never will, since my test code is blocking there (and will run further tests, anyway).
我将其替换为 self.queue.async
和 upload.delegate.queue.addOperation
,这是在同一函数中发现的另外两个排队操作。然后测试通过,但会产生意外错误;我的猜测是,然后 encodingCompletion
被称为太早了。
I replaced it with self.queue.async
and upload.delegate.queue.addOperation
, two other queueing operations found in the same function. Then the test runs through but yields unexpected errors; my guess is that then, encodingCompletion
is called too early.
有几个这里要问的问题;任何问题的答案都可以解决我的问题。
There are several questions to ask here; an answer to any can solve my problem.
- 我可以以不同的方式测试此类代码,以使
DispatchQueue.main
可以执行其他任务吗? - 如何使用调试器找出哪个线程在何时运行?
- 如何使用适应不需要主队列?
- Can I test such code differently so that
DispatchQueue.main
can get to other tasks? - How can I use the debugger to find out which thread runs when?
- How can I adapt Alamofire at the critical position so that it does not require the main queue?
推荐答案
我们不应该阻塞主线程。 XCTest有自己的等待异步计算的解决方案:
We should not block the main thread. XCTest has its own solution for waiting on asynchronous computations:
let expectation = self.expectation(description: "Operation should finish.")
operation(...) { response in
...
expectation.fulfill()
}
waitForExpectations(timeout: self.timeout)
来自:
在XCTest之外,我们可以使用与可以:
var done = false
operation(...) { response in
...
done = true
}
repeat {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
} while !done
注意:假设操作
将其工作发送到同一队列上。如果它使用另一个队列,将无法使用;但是使用 DispatchSemaphore
的方法(请参阅)不会导致死锁并可以使用。
Note: This assumes that operation
sends its work to the same queue itself is executed on. If it uses another queue, this won't work; but then the approach using DispatchSemaphore
(see the question) does not cause a deadlock and can be used.
XCTest中的实现做得更多(期望值高,超时,可配置的睡眠间隔等),但这是基本机制
The implementation in XCTest does a lot more (multiple expectations, timeout, configurable sleep interval, etc.) but this is the basic mechanism.
这篇关于当主线程等待其运行时,Alamofire永远不会通过MultipartFormData调用encodingCompletion进行上传的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!