在urlSession的操作中,代表变为零。如何将委托变量保留在单独的线程中?

时间:2018-10-30 05:09:07

标签: ios swift nsurlsession nsoperationqueue

我正在使用OperationQueue使用URLSession.dataTask将文件一个一个地上传到远程服务器。委托用于更新进度条,但是在实现OperationQueue之后,我的委托变为nil。它没有OperationQueues就可以工作。在程序运行时查看堆栈,我看不到进度条的视图控制器。已经过了几天,我还是不太清楚。我猜想视图控制器正在被释放,但是我不确定如何防止它被释放。谢谢。

我在NetWorkViewController中将委托设置为self,但是在NetworkManager类的urlSession(didSendBodyData)中,委托变为nil。委托并不弱,它是一个类变量。

但是,在我的BlockOperation的完成块中,我的代表再次变为null。这适用于通过委托关闭ViewController。但是当我尝试在urlSession(didSendBodyData)中进行更新时,委托为零。

更新10/30/2018

似乎我的urlSessions委托位于单独的线程上,并在调用时排队到主线程中,但是我丢失了对更新UI的自定义委托的引用。我正在尝试阅读有关多线程的更多信息,但任何帮助将不胜感激!

更新2 2018年10月30日

找到解决方案问题是我在每个操作中都创建了NetworkManager的另一个实例。这将导致delegatenil,因为正在为每个操作创建NetworkManager的新实例。解决方法是从原始self传递NetworkManager,以便保留delegate

上传文件

    func uploadFiles(item: LocalEntry) {
        let mainOperation = UploadMainFileOperation(file: item)
        // This is where I need to give the operation its 
        // networkManager so the proper delegate is transferred.
        mainOperation.networkManager = self
        mainOperation.onDidUpload = { (uploadResult) in
            if let result = uploadResult {
            self.result.append(result)
            }
        }
        if let lastOp = queue.operations.last {
            mainOperation.addDependency(lastOp)
        }
        queue.addOperation(mainOperation)

    ....
    ....

        let finishOperation = BlockOperation { [unowned self] in
            self.dismissProgressController()
            for result in self.result {
                print(result)
            }
            self.delegate?.popToRootController()
        }
        if let lastOp = queue.operations.last {
            finishOperation.addDependency(lastOp)
        }
        queue.addOperation(finishOperation)

        queue.isSuspended = false
    }

UploadMainFileOperation

class UploadMainFileOperation: NetworkOperation {
    let file: LocalEntry
    // First issue is here. I re-declared another NetworkManager that doesn't have its delegate properly set.
    private let networkManager = NetworkManager() 

    // I have since have this class receive the original networkManager after it's declared.
    var networkManager: NetworkManager?

    var onDidUpload: ((_ uploadResult: String?) -> Void)!

    init(file: LocalEntry) {
        self.file = file
    }

    override func execute() {
        uploadFile()
    }

    private func uploadFile() {

        networkManager.uploadMainFile(item: file) {
            (httpResult) in
            self.onDidUpload(httpResult)
            self.finished(error: "upload main")
        }
    }
}

urlSession(didSendBodyData)

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
    // This is wrong.
    let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
    updateDelegateWith(progress: uploadProgress)
    // This is the correct way for my situation.
    // Because each operation on the queue is on a separate thread. I need to update the UI from the main thread.
    DispatchQueue.main.async {
        let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
       self.updateDelegateWith(progress: uploadProgress)
    }
}

updateDelegateWith(进度:浮动)

func updateDelegateWith(progress: Float) {
    delegate?.uploadProgressWith(progress: progress)
}

NetworkManagerViewController 进度条所在的位置

class NetworkViewController: UIViewController, NetWorkManagerDelegate {

var localEntry: LocalEntry?

var progressBackground = UIView()

var progressBar = UIProgressView()

func uploadProgressWith(progress: Float) {
    progressBar.progress = progress
    view.layoutSubviews()
}

deinit {
    print("deallocate")
}

override func viewDidLoad() {
    super.viewDidLoad()

    let networkManager = NetworkManager()
    networkManager.delegate = self
    networkManager.uploadFiles(item: self.localEntry!)
....
....
}

}

3 个答案:

答案 0 :(得分:1)

正如@Kamran用户指出的那样,我正在3 内创建tempImageView.setOnDragDetected((event) -> { Image image; // Load your image and do other stuff. tempImageView.startDragAndDrop(TransferMode.ANY).setDragView(image); }); 的类级别实例。通过将该变量更改为networkManager并给它一个UploadMainFileOperation的实例,例如Optional来解决该问题,该实例正在排队。代码块已更新,其中包含正确代码的注释以及错误代码。

答案 1 :(得分:1)

在共享最新代码的情况下,我建议将NetworkManager实例保持在class级别,而不是function级别,因为这将确保networkManager实例是没有释放。

class NetworkViewController: UIViewController, NetWorkManagerDelegate {

var localEntry: LocalEntry?

var progressBackground = UIView()

var progressBar = UIProgressView()

let networkManager = NetworkManager()

func uploadProgressWith(progress: Float) {
    progressBar.progress = progress
    view.layoutSubviews()
}

deinit {
    print("deallocate")
}

override func viewDidLoad() {
    super.viewDidLoad()

    networkManager.delegate = self
    networkManager.uploadFiles(item: self.localEntry!)
}

...

此外,您需要注意retain-cycles导致内存泄漏的情况。为了避免保留周期,您需要将delegate变量声明为weak

答案 2 :(得分:0)

如果您设置了一个委托,后来又变成nil,则表示您的委托已被释放。

我建议在您的委托类中创建一个(空)deinit,并在该方法中为调试器设置一个断点。这将帮助您找出丢失对所述代表的引用的地方。

您可以通过将委托分配给您的一个类的属性来避免这种情况,或者在您的完成块之一中使其成为强引用。