我正在使用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
的另一个实例。这将导致delegate
为nil
,因为正在为每个操作创建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!)
....
....
}
}
答案 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
,并在该方法中为调试器设置一个断点。这将帮助您找出丢失对所述代表的引用的地方。
您可以通过将委托分配给您的一个类的属性来避免这种情况,或者在您的完成块之一中使其成为强引用。