我正在尝试使用受this talk启发的Swift 3实现一个小型的未来(promises)库,这是我的实践:
public enum Result<T, E: Error> {
case Success(T)
case Error(E)
}
public struct Future<T, E: Error> {
public typealias ResultType = Result<T, E>
public typealias Completion = (ResultType) -> Void
public typealias AsyncOperation = (Completion) -> Void
private let operation: AsyncOperation
public init(result: ResultType) {
self.init(operation: { completion in
completion(result)
})
}
public init(value: T) {
self.init(result: .Success(value))
}
public init(error: E) {
self.init(result: .Error(error))
}
public init(operation: @escaping (Completion) -> Void) {
self.operation = operation
}
public func start(completion: Completion) {
self.operation() { result in
completion(result)
}
}
}
//: ### Error handeling
enum UserInfoErrorDomain: Error {
case UserDoesNotExist
case UserRequestFailure
case NetworkRequestFailure
}
这是我的用法:
func downloadFile(URL: NSURL) -> Future<NSData, UserInfoErrorDomain> {
return Future(operation: { completion in
DispatchQueue.main.async( execute: {
print("Async2")
let result: Result<NSData, UserInfoErrorDomain>
if let data = NSData(contentsOf: URL as URL) {
result = Result.Success(data)
}
else {
result = Result.Error(.NetworkRequestFailure)
}
completion(result) // ERROR here Closure use of non-escaping parameter 'completion' may allow it to escape
})
})
}
但是我遇到completion(result)
并且错误Closure use of non-escaping parameter 'completion' may allow it to escape
但是闭包在方法@escaping
中已经标记为public init(operation: @escaping (Completion) -> Void)
,但也许是因为它是一个闭包,它将一个闭包作为参数并返回void需要另一个注释,所以要做到这一点Swift 3因为看起来代码曾经在Swift 2中工作
答案 0 :(得分:1)
你是对的。[...]但也许是因为它是一个闭包,它将一个闭包作为参数并返回void需要另一个注释[...]
Completion
类型为(ResultType) -> Void
,由于它是AsyncOperation
函数类型的参数,因此默认情况下它是非转义的 - 这意味着您无法捕获completion
转义闭包中的参数(例如传递给DispatchQueue.main.async
的参数)。
因此,您需要将Completion
注释为@escaping
:
public typealias AsyncOperation = (@escaping Completion) -> Void
并且您希望init(operation:)
和start(completion:)
函数看起来像这样:
public init(operation: @escaping AsyncOperation) {
self.operation = operation
}
// the completion: parameter needs to be escaping as it's going to be called after
// an async operation has completed.
public func start(completion: @escaping Completion) {
self.operation { result in
completion(result)
}
}