【发布时间】:2017-04-10 15:17:34
【问题描述】:
我在使用 Operation 和 OperationQueue 时遇到了 Alamofire 问题。
我有一个名为NetworkingQueue 的OperationQueue,我将一些操作(包装AlamofireRequest)推入其中,一切正常,但在应用程序运行期间,有一段时间没有发送所有Alamofire 请求。我的队列越来越大,没有请求到最后。
我没有计划随时重现它。
有没有人能帮帮我?
这是一个代码示例
BackgroundAlamoSession
let configuration = URLSessionConfiguration.background(withIdentifier: "[...].background")
self.networkingSessionManager = Alamofire.SessionManager(configuration: configuration)
AbstractOperation.swift
import UIKit
import XCGLogger
class AbstractOperation:Operation {
private let _LOGGER:XCGLogger = XCGLogger.default
enum State:String {
case Ready = "ready"
case Executing = "executing"
case Finished = "finished"
var keyPath: String {
get{
return "is" + self.rawValue.capitalized
}
}
}
override var isAsynchronous:Bool {
get{
return true
}
}
var state = State.Ready {
willSet {
willChangeValue(forKey: self.state.rawValue)
willChangeValue(forKey: self.state.keyPath)
willChangeValue(forKey: newValue.rawValue)
willChangeValue(forKey: newValue.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.rawValue)
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: self.state.rawValue)
didChangeValue(forKey: self.state.keyPath)
}
}
override var isExecuting: Bool {
return state == .Executing
}
override var isFinished:Bool {
return state == .Finished
}
}
一个具体的操作实现
import UIKit
import XCGLogger
import SwiftyJSON
class FetchObject: AbstractOperation {
public let _LOGGER:XCGLogger = XCGLogger.default
private let _objectId:Int
private let _force:Bool
public var object:ObjectModel?
init(_ objectId:Int, force:Bool) {
self._objectId = objectId
self._force = force
}
convenience init(_ objectId:Int) {
self.init(objectId, force:false)
}
override var desc:String {
get{
return "FetchObject(\(self._objectId))"
}
}
public override func start(){
self.state = .Executing
_LOGGER.verbose("Fetch object operation start")
if !self._force {
let objectInCache:objectModel? = Application.main.collections.availableObjectModels[self._objectId]
if let objectInCache = objectInCache {
_LOGGER.verbose("object with id \(self._objectId) founded on cache")
self.object = objectInCache
self._LOGGER.verbose("Fetch object operation end : success")
self.state = .Finished
return
}
}
if !self.isCancelled {
let url = "[...]\(self._objectId)"
_LOGGER.verbose("Requesting object with id \(self._objectId) on server")
Application.main.networkingSessionManager.request(url, method : .get)
.validate()
.responseJSON(
completionHandler: { response in
switch response.result {
case .success:
guard let raw:Any = response.result.value else {
self._LOGGER.error("Error while fetching json programm : Empty response")
self._LOGGER.verbose("Fetch object operation end : error")
self.state = .Finished
return
}
let data:JSON = JSON(raw)
self._LOGGER.verbose("Received object from server \(data["bId"])")
self.object = ObjectModel(objectId:data["oId"].intValue,data:data)
Application.main.collections.availableobjectModels[self.object!.objectId] = self.object
self._LOGGER.verbose("Fetch object operation end : success")
self.state = .Finished
break
case .failure(let error):
self._LOGGER.error("Error while fetching json program \(error)")
self._LOGGER.verbose("Fetch object operation end : error")
self.state = .Finished
break
}
})
} else {
self._LOGGER.verbose("Fetch object operation end : cancel")
self.state = .Finished
}
}
}
网络队列
class MyQueue {
public static let networkQueue:SaootiQueue = SaootiQueue(name:"NetworkQueue", concurent:true)
}
我如何在另一个操作中使用它并等待结果
let getObjectOperation:FetchObject = FetchObject(30)
SaootiQueue.networkQueue.addOperations([getObjectOperation], waitUntilFinished: true)
我如何使用它主要操作使用KVO
let getObjectOperation:FetchObject = FetchObject(30)
operation.addObserver(self, forKeyPath: #keyPath(Operation.isFinished), options: [.new], context: nil)
operation.addObserver(self, forKeyPath: #keyPath(Operation.isCancelled), options: [.new], context: nil)
queue.addOperation(operation)
//[...]
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let operation = object as? FetchObject {
operation.removeObserver(self, forKeyPath: #keyPath(Operation.isFinished))
operation.removeObserver(self, forKeyPath: #keyPath(Operation.isCancelled))
if keyPath == #keyPath(Operation.isFinished) {
//Do something
}
}
一些澄清:
我的应用程序是一个广播播放器,我需要在播放音乐和背景时获取当前正在播放的节目。这就是为什么我需要后台会话。
事实上,当应用程序处于前台时,我也将后台会话用于我所做的所有网络。我应该避免这种情况吗?
我正在使用的等待来自另一个队列,并且从未在主队列中使用(我知道这是一个线程反模式,我会处理它)。
实际上,当我进行两次联网操作时使用它,而第二次取决于第二次的结果。我在第一次操作后等待以避免 KVO 观察。我应该避免这种情况吗?
补充编辑:
当我说“我的队列越来越大,没有请求走到最后”时,这意味着在应用程序生命周期中的某个时刻,暂时是随机的(我每次都找不到重现它的方法),Alamofire 请求没有到达响应方法。
因为Operation 包装器没有结束并且队列正在增长。
顺便说一句,我正在努力将 Alamofire 请求转换为 URLRequest 以获得线索,并且我在使用主队列时发现了一些问题。由于 Alamofire 使用主队列进行响应方法,我必须对原因进行排序,我会看看是否发现潜在的死锁
我会及时通知您。谢谢
【问题讨论】:
-
感谢您的评论。我添加了一些示例代码。我希望它能提供更多信息,如果有什么问题在检测它。
-
什么是
operation.first?我没有看到任何first属性... -
提供代码时,我尝试删除所有业务代码以专注于问题,这很糟糕。首先是业务代码的一部分。事实上,所有操作都是重载以提出不同的请求。第一个参数在重载类上。我从示例代码中删除它
-
如果您 100% 确定这是到达
request的情况,但您没有看到responseJSON被调用,我所知道的唯一可能导致的情况是僵局。但这会导致 UI 锁定,并且主线程上的任何内容都会冻结。或者,如果您设置了一些异常大的超时参数,它可能似乎没有运行闭包。或者,如果您的操作中有一些路径没有将state设置为完成或取消,您的队列将备份。很难说没有MCVE。
标签: ios alamofire nsoperationqueue operation