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
.
我正在使用 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
.