- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有下载文件的类(class):
class FileDownloader {
private let downloadsSession = URLSession(configuration: .default)
private var task: URLSessionDownloadTask?
private let url: URL
init(url: URL) {
self.url = url
}
public func startDownload(){
download()
}
private func download(){
task = downloadsSession.downloadTask(with: url) {[weak self] (location, response, error) in
guard let weakSelf = self else {
assertionFailure("self was deallocated")
return }
weakSelf.saveDownload(sourceUrl: weakSelf.url, location: location, response: response, error: error)
}
task!.resume()
}
private func saveDownload(sourceUrl : URL, location : URL?, response : URLResponse?, error : Error?) {
if error != nil {
assertionFailure("error \(String(describing: error?.localizedDescription))")
return }
let destinationURL = localFilePath(for: sourceUrl)
let fileManager = FileManager.default
try? fileManager.removeItem(at: destinationURL)
do {
try fileManager.copyItem(at: location!, to: destinationURL)
print("save was completed at \(destinationURL) from \(String(describing: location))")
} catch let error {
print("Could not copy file to disk: \(error.localizedDescription)")
}
}
private func localFilePath(for url: URL) -> URL {
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
return documentsPath.appendingPathComponent(url.lastPathComponent)
}
}
当我调用 startDownload()
时,我在调试时遇到错误:
assertionFailure("self was deallocated")
当我将下载功能更改为:
private func download(){
task = downloadsSession.downloadTask(with: url) {(location, response, error) in
self.saveDownload(sourceUrl: self.url, location: location, response: response, error: error)
}
task!.resume()
}
一切正常,但我担心它可能会导致未在内存中正确释放的对象出现问题。如何避免这种情况?我做的对吗?
最佳答案
首先,为什么会出现断言失败?因为您让 FileDownloader
实例超出范围。你没有分享你是如何调用它的,但你很可能将它用作局部变量。如果你解决了这个问题,你的问题就会消失。
其次,当您更改实现以删除 [weak self]
模式时,您没有强引用循环,而是您只是指示它不要释放 FileDownloader
直到下载完成。如果那是您想要的行为,那很好。这是一个完全可以接受的模式,“让它在异步任务完成之前保持对自身的引用”。事实上,这正是 URLSessionTask
所做的。显然,您需要完全清楚省略 [weak self]
模式的含义,因为在某些情况下它会引入强引用循环,但在这种情况下不会。
强引用循环仅在您有两个对象彼此具有持久性强引用时发生(或者有时可能涉及两个以上的对象)。对于 URLSession
,当下载完成时,Apple 谨慎地编写了 downloadTask
方法,以便在调用它后显式释放闭包,解决任何潜在的强引用循环。
例如,考虑这个例子:
class Foo {
func performAfterFiveSeconds(block: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.doSomething()
block()
}
}
func doSomething() { ... }
}
上面的代码很好,因为 asyncAfter
在运行时释放了闭包。但是考虑这个例子,我们将闭包保存在我们自己的 ivar 中:
class BarBad {
private var handler: (() -> Void)?
func performAfterFiveSeconds(block: @escaping () -> Void) {
handler = block
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.calledWhenDone()
}
}
func calledWhenDone() {
// do some stuff
doSomething()
// when done, call handler
handler?()
}
func doSomething() { ... }
}
现在这是一个潜在的问题,因为这次我们将闭包保存在一个 ivar 中,创建了对闭包的强引用,并引入了经典强引用循环的风险。
但幸运的是,这很容易补救:
class BarGood {
private var handler: (() -> Void)?
func performAfterFiveSeconds(block: @escaping () -> Void) {
handler = block
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.calledWhenDone()
}
}
func calledWhenDone() {
// do some stuff
doSomething()
// when done, call handler
handler?()
// make sure to release handler when done with it to prevent strong reference cycle
handler = nil
}
func doSomething() { ... }
}
这解决了将 handler
设置为 nil
时的强引用循环。这实际上就是 URLSession
(以及 GCD 方法,如 async
或 asyncAfter
)所做的。他们保存闭包直到调用它,然后释放它。
关于ios - 如何通过在闭包中使用 self 来防止内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54430997/
我在一个简单的 GTK 应用程序中有两个小部件: extern crate gdk; extern crate gtk; use super::desktop_entry::DesktopEntry;
我想做这样的事情: const vegetableColors = {corn: 'yellow', peas: 'green'}; const {*} = vegetableColors; cons
该属性它存储在 gradle 中的什么位置? subprojects { println it.class.name // DefaultProject_Decorated depen
我想在 jQuery 闭包中看到窗口属性“otherName”描述符。但 进入 jQuery 闭包 'otherName' 描述符显示未定义,我认为可能 是 getOwnPropertyDescrip
我曾经看过 Douglas Crockford 的一次演讲,在 javascript 的上下文中,他提到将 secret 存储在闭包中可能很有用。 我想这可以在 Java 中像这样天真地实现: pub
我很难理解 Swift 中闭包中真正发生的事情,希望有人能帮助我理解。 class MyClass { func printWhatever(words: String) {
我有两个 3 表:用户、个人资料、friend_request $my_profile_id变量存储用户个人资料ID的值 $my_user_id = Auth::user()->id; $my_pro
我正在尝试通过使用 GLFW 的包装来学习 Swift GLFW 允许添加错误回调: GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cb
我是一名优秀的程序员,十分优秀!