【问题标题】:Uploading Data Using NSURLSession and Queue使用 NSURLSession 和队列上传数据
【发布时间】:2016-09-02 11:28:02
【问题描述】:

我正在设计一个聊天应用程序,我已经为用户设置了以下机制来上传消息。基本上,我将消息推送到队列中,然后一个接一个地上传。当队列为空时,我调用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
}

}

现在这在以下两种情况下可以正常工作:

  1. 队列为空 -> finishedUploading 被调用,uploadAllLinks 每秒运行一次以检查 uploadQueue 中的项目
  2. 队列有一项 -> 一项被发布,remaining == 0 因此称为finishedUploading

但是,只要队列有多个项目,就会上传第一个项目,if remaining == 0 失败,然后什么也没有发生。我不明白为什么此时没有为队列中的其他项目运行 for 循环。

【问题讨论】:

    标签: ios swift queue nsurlsession nsoperationqueue


    【解决方案1】:

    我怀疑问题出在您的 10 秒超时间隔上。这会在数据任务创建后立即开始计时,如果任务保持空闲(未接收新数据)超过 10 秒,则终止任务。

    如果您有多个任务并且操作系统一次只允许上传一两个,那么任何排队等待开始的任务都将永远不会完成。我认为文档没有提到这一点。

    在实践中,这种设计使得 NSURLSession 的队列不太理想,因此,大多数人似乎都编写了自己的队列并自己处理并发限制,确保每个任务在它应该开始运行之前就被创建。我建议做类似的事情:

    • 创建一个方法来开始队列中的下一次上传,或者在队列为空时调用“一切都完成”方法——基本上是循环的主体。
    • 调用该方法开始第一次上传,而不是循环本身。
    • 在您的完成处理程序中(在该方法内),以半递归方式调用该方法以开始下一次上传。

    此外,对于超时间隔来说,10 秒太短了,除非您的设备安装在墙上并且连接到 Wi-Fi 且信号稳定。不稳定的 Wi-Fi 和微弱的蜂窝信号会导致严重的延迟,因此 IIRC,默认值为 120 秒,尽管我在各个地方读过 60 秒。无论哪种方式,您都想要使用 10 秒。如此短的超时时间几乎可以保证您的应用程序完全不可靠。

    【讨论】:

    • 因为在 for 循环中,请求变量不会为队列中的每个项目重新初始化;因此十秒间隔仅适用于队列中的一项?
    • 确实如此,但每个项目都有一个十秒的超​​时时间,从前一个项目后几微秒开始。 for 循环只是在下一个任务之后创建任务并启动它们,因为在开始下一个任务之前您不需要等待每个任务实际完成。
    • 我可能错了超时间隔是失败的原因,但无论哪种方式,它肯定会在蜂窝网络上导致相当一致的故障,所以你不应该这样做.
    • 好的我都改成120秒了,明天我会执行你的设计建议,如果有什么问题我会在这里评论,谢谢你的详细回答:)
    猜你喜欢
    • 2014-02-07
    • 2014-02-14
    • 2016-02-28
    • 2013-11-27
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 2014-02-22
    • 2016-06-04
    相关资源
    最近更新 更多