delegate 在 urlSession 的操作中变为 nil。如何将委托变量保存在单独的线程中?

Delegate becomes nil in Operation of urlSession. How to keep delegate variable in separate thread?

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

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

但是,我的委托在我的 BlockOperation 的完成块中再次变为 none-nil。这适用于通过委托解除 ViewController 。但是当我尝试在 urlSession(didSendBodyData) 中更新时,委托为 nil...

2018 年 10 月 30 日更新

我的 urlSessions 委托似乎在一个单独的线程上,并在调用时排入主线程,但我丢失了对更新 UI 的自定义委托的引用。我正在尝试阅读有关多线程的更多信息,但我们将不胜感激!

更新 2 10/30/2018

找到解决方案 问题是我在每个操作中创建了 NetworkManager 的另一个实例。这会导致 delegate 变为 nil,因为每个操作都会创建一个 NetworkManager 的新实例。解决方法是从原始 NetworkManager 传递 self,以便保留 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!)
....
....
}

}

如果你设置了一个委托,后来它变成了nil,这意味着你的委托已经被释放。

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

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

正如用户@Kamran 指出的那样,我正在 UploadMainFileOperation 中创建 networkManager 的 class 级别实例。通过将该变量更改为 Optional 并为其提供一个 NetworkManager 的实例,作为 self 的实例,该问题已得到解决,该实例正在排队操作。代码块已更新为正确代码和错误代码的注释。

共享最新代码后,我建议将 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.