我有这种形式的代码:
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 请求都适用,但不适用于这个。测试挂起。
(注意:使用 active waiting 不会改变任何事情。)
使用调试器,我发现
- 所有执行的代码都很好,但是
-
encodingCompletion
is never called .
现在我最好的猜测是 DispatchQueue.main.async
说,“当有时间时在主线程上执行这个”——它永远不会,因为我的测试代码在那里阻塞(无论如何,都会进行进一步的测试)。
我将其替换为 self.queue.async 和 upload.delegate.queue.addOperation,这是同一函数中的另外两个排队操作。然后测试运行通过但产生意外错误;我的猜测是,encodingCompletion
被调用太早。
这里有几个问题要问;任何问题的答案都可以解决我的问题。
- 我可以以不同的方式测试此类代码,以便
DispatchQueue.main
可以执行其他任务吗? - 如何使用调试器找出哪个线程在何时运行?
- 我该如何适应 Alamofire at the critical position这样就不需要主队列了?
最佳答案
如上所述here ,这是一个糟糕的“解决方案”,因为它在嵌套请求时引入了死锁的可能性。我将其留在这里用于教学目的。
<小时/>改变
DispatchQueue.main.async {
let encodingResult = MultipartFormDataEncodingResult.success(
request: upload,
streamingFromDisk: true,
streamFileURL: fileURL
)
encodingCompletion?(encodingResult)
}
self.queue.sync {
...
}
解决(阅读:解决)问题。
我不知道这是否是一个可靠的修复或其他什么;我有filed an issue .
关于swift - 当主线程等待运行时,Alamofire 从不调用 encodingCompletion 来使用 MultipartFormData 进行上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42644111/