我正在进行三个 api 调用,并希望 API1 应首先执行,完成后应执行 API2,然后执行 API3。
我为此使用了操作队列,并添加了对操作的依赖性。我也尝试设置优先级,但没有按顺序调用 api。帮我看看如何正确制作。
代码是这样的:
let op1 = Operation()
op1.completionBlock = {
self.APICall(urlString: self.url1)
}
op1.queuePriority = .veryHigh
let op2 = Operation()
op2.completionBlock = {
self.APICall(urlString: self.url2)
}
op2.queuePriority = .high
let op3 = Operation()
op3.completionBlock = {
self.APICall(urlString: self.url3)
}
op3.queuePriority = .normal
op2.addDependency(op1)
op3.addDependency(op2)
queue.addOperations([op1, op2, op3], waitUntilFinished: false)
我将 API 调用方法放在 DispatchQueue.main.sync 中,如下所示:
func APICall(urlString: String) {
let headers: HTTPHeaders = [
"Accept": "text/html"
]
print(urlString)
DispatchQueue.main.sync {
Alamofire.request(urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON {
response in
// self.stopActivityIndicator()
print(response.result.value)
switch response.result {
case .success:
break
case .failure(let error):
break
}
}
}
}
有几个问题:
-
如果您尝试管理操作之间的依赖关系,则不能使用操作的completionBlock
对于依赖项所依赖的代码。直到操作完成后才会调用完成块(从而破坏了任何依赖项的目的)。
所以下面将not按预期工作:
let queue = OperationQueue()
let op1 = Operation()
op1.completionBlock = {
print("starting op1")
Thread.sleep(forTimeInterval: 1)
print("finishing op1")
}
let op2 = Operation()
op2.completionBlock = {
print("starting op2")
Thread.sleep(forTimeInterval: 1)
print("finishing op2")
}
op2.addDependency(op1)
queue.addOperations([op1, op2], waitUntilFinished: false)
但如果你像这样定义操作,它就会起作用:
let op1 = BlockOperation() {
print("starting op1")
Thread.sleep(forTimeInterval: 1)
print("finishing op1")
}
let op2 = BlockOperation {
print("starting op2")
Thread.sleep(forTimeInterval: 1)
print("finishing op2")
}
(但这只是因为我重新定义了同步操作才有效。请参阅下面的第 3 点。)
值得注意的是,通常你从不使用Operation
直接地。作为docs say:
表示与单个任务关联的代码和数据的抽象类。 ...
因为Operation
class 是一个抽象类,您不能直接使用它,而是子类化或使用系统定义的子类之一(NSInvocationOperation or BlockOperation)来执行实际任务。
因此使用BlockOperation
,上面,或对其进行子类化,如下面第 3 点所示。
-
如果必须严格遵守操作执行的顺序,则不应使用优先级来管理操作执行的顺序。作为queuePriority
docs say(强调):
该值用于影响操作出队和执行的顺序...
您应该仅根据需要使用优先级值来对非相关操作的相对优先级进行分类。不应使用优先级值来实现不同操作对象之间的依赖关系管理。如果需要在操作之间建立依赖关系,请使用addDependency(_:)
方法代替。
因此,如果您将 100 个高优先级操作和 100 个默认优先级操作排队,那么您将not保证所有高优先级的程序将在低优先级的程序开始运行之前启动。它会倾向于优先考虑它们,但并非严格如此。
-
第一点是没有意义的,因为您正在调用异步方法。所以你不能使用简单的Operation
or BlockOperation
。如果您不希望在前一个网络请求完成之前启动后续网络请求,则需要将这些网络请求包装在自定义异步中Operation
具有所有特殊 KVO 的子类,其中包括:
class NetworkOperation: AsynchronousOperation {
var request: DataRequest
static var sessionManager: SessionManager = {
let manager = Alamofire.SessionManager(configuration: .default)
manager.startRequestsImmediately = false
return manager
}()
init(urlString: String, parameters: [String: String]? = nil, completion: @escaping (Result<Any>) -> Void) {
let headers: HTTPHeaders = [
"Accept": "text/html"
]
let string = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let url = URL(string: string)!
request = NetworkOperation.sessionManager.request(url, parameters: parameters, headers: headers)
super.init()
request.responseJSON { [weak self] response in
completion(response.result)
self?.finish()
}
}
override func main() {
request.resume()
}
override func cancel() {
request.cancel()
}
}
然后你可以这样做:
let queue = OperationQueue()
let op1 = NetworkOperation(urlString: ...) { result in
...
}
let op2 = NetworkOperation(urlString: ...) { result in
...
}
let op3 = NetworkOperation(urlString: ...) { result in
...
}
op2.addDependency(op1)
op3.addDependency(op2)
queue.addOperations([op1, op2, op3], waitUntilFinished: false)
因为那是使用AsynchronousOperation
子类(如下所示),直到异步请求完成后操作才会完成。
/// Asynchronous operation base class
///
/// This is abstract to class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `Operation` subclass. You can subclass this and
/// implement asynchronous operations. All you must do is:
///
/// - override `main()` with the tasks that initiate the asynchronous task;
///
/// - call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `finish()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `finish()` is called.
public class AsynchronousOperation: Operation {
/// State for this operation.
@objc private enum OperationState: Int {
case ready
case executing
case finished
}
/// Concurrent queue for synchronizing access to `state`.
private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent)
/// Private backing stored property for `state`.
private var _state: OperationState = .ready
/// The state of the operation
@objc private dynamic var state: OperationState {
get { stateQueue.sync { _state } }
set { stateQueue.sync(flags: .barrier) { _state = newValue } }
}
// MARK: - Various `Operation` properties
open override var isReady: Bool { return state == .ready && super.isReady }
public final override var isAsynchronous: Bool { return true }
public final override var isExecuting: Bool { return state == .executing }
public final override var isFinished: Bool { return state == .finished }
// KVN for dependent properties
open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
if ["isReady", "isFinished", "isExecuting"].contains(key) {
return [#keyPath(state)]
}
return super.keyPathsForValuesAffectingValue(forKey: key)
}
// Start
public final override func start() {
if isCancelled {
state = .finished
return
}
state = .executing
main()
}
/// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.
open override func main() {
fatalError("Subclasses must implement `main`.")
}
/// Call this function to finish an operation that is currently executing
public final func finish() {
if !isFinished { state = .finished }
}
}
作为一个非常小的观察,您的代码指定了带有 JSON 参数的 GET 请求。这没有道理。 GET 请求没有可以包含 JSON 的正文。 GET 请求仅使用 URL 编码。此外,您没有传递任何参数。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)