如何在Swift 3中同时发出https请求
我在执行https请求时遇到问题,如果请求没有任何错误,我永远不会得到消息,这是一个命令行工具应用程序,我有一个允许http请求的plist,我总是看到完成块如何在Swift 3中同时发出https请求,http,concurrency,swift3,nsoperation,nsoperationqueue,Http,Concurrency,Swift3,Nsoperation,Nsoperationqueue,我在执行https请求时遇到问题,如果请求没有任何错误,我永远不会得到消息,这是一个命令行工具应用程序,我有一个允许http请求的plist,我总是看到完成块 typealias escHandler = ( URLResponse?, Data? ) -> Void func getRequest(url : URL, _ handler : @escaping escHandler){ let session = URLSession.shared var request =
typealias escHandler = ( URLResponse?, Data? ) -> Void
func getRequest(url : URL, _ handler : @escaping escHandler){
let session = URLSession.shared
var request = URLRequest(url:url)
request.cachePolicy = .reloadIgnoringLocalCacheData
request.httpMethod = "GET"
let task = session.dataTask(with: url ){ (data,response,error) in
handler(response,data)
}
task.resume()
}
func startOp(action : @escaping () -> Void) -> BlockOperation{
let exOp = BlockOperation(block: action)
exOp.completionBlock = {
print("Finished")
}
return exOp
}
for sUrl in textFile.components(separatedBy: "\n"){
let url = URL(string: sUrl)!
let queu = startOp {
getRequest(url: url){ response, data in
print("REACHED")
}
}
operationQueue.addOperation(queu)
operationQueue.waitUntilAllOperationsAreFinished()
一个问题是,您的操作只是启动请求,但由于请求是异步执行的,因此操作会立即完成,而不是实际等待请求完成。在异步请求完成之前,您不希望完成该操作 如果要对操作队列执行此操作,诀窍是必须对
operation
进行子类化,并为isExecuting
和isFinished
执行必要的KVO。然后,在启动请求时更改isExecuting
,在完成请求时更改isFinished
,并使用两者的关联KVO。所有这些都在中概述,特别是在本节中。(注意,本指南有点过时(它指的是isConcurrent
属性,已被替换为isisAsynchronous
;它的重点是Objective-C等),但它向您介绍了这些问题
无论如何,这是一个抽象类,我用它来封装所有这些异步操作:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.
public class AsynchronousOperation : Operation {
override public var isAsynchronous: Bool { return true }
private let lock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return lock.synchronize { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
lock.synchronize { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return lock.synchronize { _finished }
}
set {
willChangeValue(forKey: "isFinished")
lock.synchronize { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func completeOperation() {
if isExecuting {
isExecuting = false
isFinished = true
}
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
}
我使用这个苹果扩展来NSLocking
,以确保我同步了上面的状态更改(他们的扩展名是NSLock
上的withCriticalSection
,但这是一个稍微更通用的格式副本,处理符合NSLock
的任何内容,并处理引发错误的闭包):
无论如何,完成此操作后,我现在可以为网络请求创建操作,例如:
let queue = OperationQueue()
queue.name = "com.domain.app.network"
let url = URL(string: "http://...")!
let operation = NetworkOperation(session: .shared, url: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
// do something with `data` here
}
let operation2 = BlockOperation {
print("done")
}
operation2.addDependency(operation)
queue.addOperations([operation, operation2], waitUntilFinished: false) // if you're using command line app, you'd might use `true` for `waitUntilFinished`, but with standard Cocoa apps, you generally would not
注意,在上面的示例中,我添加了第二个操作,它只是打印了一些内容,使其依赖于第一个操作,以说明第一个操作直到网络请求完成后才完成
显然,您通常不会使用原始示例中的
WaitUntilAllOperationsDefished
,也不会使用我的示例中的addOperations
的waitUntilFinished
选项。但因为您处理的是一个在这些请求完成之前不想退出的命令行应用程序,所以这种模式是可以接受的。(我提到这一点只是为了让未来的读者感到惊讶,waitUntilFinished
,这通常是不可取的。)你可以看看。它支持swift3中的同步http请求。非常感谢!但是你不要错过一个覆盖公共函数取消()
在AsynchronousOperation
类中,您在该类中拥有isFinished=true
并调用其super?否则,如果取消,操作将保留在队列中:)文档说,“取消操作不会立即强制它停止正在执行的操作……代码必须显式检查此属性中的值,并根据需要中止。”因此,不,对于异步操作的默认实现来说,仅仅完成操作是不明智的,但是它的子类必须决定如何以及何时停止实际的底层任务,然后才完成操作。如果子类由于某种原因没有响应取消,那么操作就不应该完成sh也未成熟。嗨,Rob,谢谢:)好的,那么你不想改成我建议的NetworkOperation
吗?谢谢你的时间:)NetworkOperation
确实实现了cancel
,并且它取消了任务。当网络任务被取消时,它的完成处理程序被调用(这就是我们调用完成任务的completeOperation
)的地方。好的,我使用了completionBlock
操作,而不是您使用的requestCompletionHandler
。这可能会有所不同。很抱歉。但是我在我的子类“override func cancel()中添加了对completeOperation()
的调用
,然后内存中就没有操作了。感谢您的帮助:)
class NetworkOperation: AsynchronousOperation {
var task: URLSessionTask!
init(session: URLSession, url: URL, requestCompletionHandler: @escaping (Data?, URLResponse?, Error?) -> ()) {
super.init()
task = session.dataTask(with: url) { data, response, error in
requestCompletionHandler(data, response, error)
self.completeOperation()
}
}
override func main() {
task.resume()
}
override func cancel() {
task.cancel()
super.cancel()
}
}
let queue = OperationQueue()
queue.name = "com.domain.app.network"
let url = URL(string: "http://...")!
let operation = NetworkOperation(session: .shared, url: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
// do something with `data` here
}
let operation2 = BlockOperation {
print("done")
}
operation2.addDependency(operation)
queue.addOperations([operation, operation2], waitUntilFinished: false) // if you're using command line app, you'd might use `true` for `waitUntilFinished`, but with standard Cocoa apps, you generally would not