我正在设计一个聊天应用程序,我已经为用户设置了以下上传消息的机制。基本上,我把消息推到一个队列上,然后一个接一个地上传它们。当队列为空时,我调用finishedUploading
,它每秒运行一次,如果队列中有任何内容,则重新运行任务。
var uploadQueue:[UploadMessage]?
let session = NSURLSession.sharedSession()
let lockQueue = dispatch_queue_create("com.dsdevelop.lockQueue", nil)
// RETURNS AMOUNT OF ITEMS STILL IN QUEUE
func getRemainingActiveUploads() -> Int {
return (self.uploadQueue != nil) ? self.uploadQueue!.count : 0
}
//REMOVES MESSAGE FROM QUEUE ONCE UPLOADED
func removeMessageFromUploadQueue(messageToBeRemoved : UploadMessage) {
if (uploadQueue != nil) {
dispatch_sync(lockQueue) {
self.uploadQueue = self.uploadQueue?.filter({$0.date!.compare(messageToBeRemoved.date!) == NSComparisonResult.OrderedSame})
}
}
}
var uploadTimer : NSTimer?
// CALLED ONLY WHEN UPLOADQUEUE IS EMPTY, RERUNS THE UPLOAD FUNCTION AFTER 1 SECOND
func finishedUploading() {
uploadTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(uploadAllLinks), userInfo: nil, repeats: false)
if (needToRefetch) {
needToRefetch = false
newMessageReceived()
}
}
func uploadAllLinks()
{
uploadTimer?.invalidate()
uploadTimer = nil
// suspending queue so they don't all finish before we can show it
session.delegateQueue.suspended = true
session.delegateQueue.maxConcurrentOperationCount = 1
let myUrl = NSURL(string: "http://****")
// create tasks
if (uploadQueue != nil) {
if (uploadQueue?.count > 0) {
for message in uploadQueue!
{
let request = NSMutableURLRequest(URL:myUrl!)
request.HTTPMethod = "POST"
request.timeoutInterval = 10
request.HTTPShouldHandleCookies=false
var postString = "sender=" + message.sender!
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let dltask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) in
if data != nil
{
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
dispatch_async(dispatch_get_main_queue(), {
if let errorToken = jsonArray["error"] as! Bool? {
if !errorToken {
self.uploadQueue = self.uploadQueue!.filter({$0.date!.compare(message.date!) != NSComparisonResult.OrderedSame})
let remaining = self.getRemainingActiveUploads()
print("Downloaded. Remaining: \(remaining)")
if (remaining == 0) {
self.finishedUploading()
}
}
else {
let remaining = self.getRemainingActiveUploads()
print("Downloaded. Remaining: \(remaining)")
if (remaining == 0) {
self.finishedUploading()
}
}
}
else {
let remaining = self.getRemainingActiveUploads()
print("Downloaded. Remaining: \(remaining)")
if (remaining == 0) {
self.finishedUploading()
}
}
})
}
catch {
print("Error: \(error)")
}
}
})
print("Queuing task \(dltask)")
dltask.resume()
}
session.delegateQueue.suspended = false
}
else {
finishedUploading()
}
// resuming queue so all tasks run
}
}
现在,这在以下两种情况下都可以正常工作:
队列为空->
finishedUploading
将被调用,并每秒运行uploadAllLinks
以检查uploadQueue
中的项目队列有一个项->一个项被发布,
remaining == 0
因此调用但是,每当队列有多个项时,第一个项将被上载,
finishedUploading
将失败,然后将不会发生任何事情。我不明白为什么此时不为队列中的其他项运行for循环。 最佳答案
我怀疑问题出在你10秒的超时时间。一旦创建了数据任务,它就开始计时,如果任务保持空闲(没有接收到新数据)超过10秒,它就会终止。
如果您有多个任务,并且操作系统一次只允许上载其中的一个或两个任务,则排队等待启动的任何任务都不会完成。我不认为文件中提到了这一点。
实际上,这种设计使NSURLSession的队列不太理想,因此,大多数人似乎都在编写自己的队列并自行处理并发限制,确保在每个任务开始运行之前就创建了它。我建议做类似的事情:
创建一个方法,开始队列中的下一次上载,或者如果队列基本上是空的,则调用“everything complete”方法。
不是循环本身,而是调用该方法开始第一次上载。
在完成处理程序(在该方法内部)中,半递归地调用该方法以开始下一次上载。
另外,10秒对于超时间隔来说太短了,除非您的设备安装在墙上,并且在Wi-Fi上有可靠的信号。断断续续的Wi-Fi和微弱的手机信号会导致严重的延迟,所以IIRC,默认值是120秒,尽管我在不同的地方读到了60秒。不管怎样,你都不想用10秒。这么短的超时时间几乎可以保证你的应用程序将毫无希望地不可靠。
关于ios - 使用NSURLSession和Queue上载数据,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39291269/