使用 NSURLSession.downloadTaskWithURL 时出现内存泄漏 [英] Memory leak when using NSURLSession.downloadTaskWithURL

查看:63
本文介绍了使用 NSURLSession.downloadTaskWithURL 时出现内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我在使用 Swift 的过程中遇到了另一个障碍.我正在尝试将多个图像加载到一个图像库中 - 除了一件事之外,一切正常.尽管我清除了图像,但应用程序的内存使用量仍在不断增长.基本上把所有的代码都删除后,我发现这是我的图片加载脚本造成的:

So I hit another roadblock in my endeavors with Swift. I am trying to load multiple images into an image gallery - all works fine except of one thing. The memory use of the application keeps growing and growing despite the fact that I clear the images. After eleminating basically all the code, I found out that this is caused by my image loading script:

func loadImageWithIndex(index: Int) {
    let imageURL = promotions[index].imageURL
    let url = NSURL(string: imageURL)!
    let urlSession = NSURLSession.sharedSession()
    let query = urlSession.downloadTaskWithURL(url, completionHandler: { location, response, error -> Void in

    })
    query.resume()
}

正如你所看到的,这段代码现在基本上什么都不做.然而,每次我调用它时,我的应用程序内存使用量都会增加.如果我注释掉查询,内存使用量不会改变.

As you can see this piece of code does basically nothing right now. Yet every time I call it, my apps memory usage grows. If I comment out the query, the memory usage is not changing.

我读过几个类似的问题,但它们都涉及到委托的使用.好吧,在这种情况下,没有委托,但存在内存问题.有人知道如何消除它以及导致它的原因吗?

I have read several similar issues but they all involved the use of a delegate. Well, in this case there is no delegate yet there is the memory issue. Does anybody know how to eliminate it and what is causing it?

这是一个完整的测试类.似乎只有在可以加载图像时内存才会增长,就像指向图像的指针将永远保留在内存中一样.当未找到图像时,什么也没有发生,内存使用率保持较低.也许有一些提示如何清理这些指针?

Here is a complete test class. Seems like the memory grows only when the image could be loaded, like the pointers to the image would be kept in the memory for ever. When the image was not found, nothing happens, memory usage stays low. Maybe some hint how to clean those pointers?

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        //memory usage: approx. 23MB with 1 load according to the debug navigator
        //loadImage()

        //memory usage approx 130MB with the cycle below according to the debug navigator
        for i in 1...50 {
            loadImage()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func loadImage() {
        let imageURL = "http://mama-beach.com/mama2/wp-content/uploads/2013/07/photodune-4088822-beauty-beach-and-limestone-rocks-l.jpg" //random image from the internet
        let url = NSURL(string: imageURL)!
        let urlSession = NSURLSession.sharedSession()
        let query = urlSession.downloadTaskWithURL(url, completionHandler: { location, response, error -> Void in
            //there is nothing in here
        })
        query.resume()
    }
}

对不起,我还不知道如何使用分析器(在整个 iOS 爵士乐中非常菜鸟),至少我会附上由上面的代码生成的分析器的屏幕截图:

I am sorry, I have no idea how to use the profiler just yet (being very noob in this whole iOS jazz), at least I will attach a screenshot of the profiler that is produced by the code above:

推荐答案

您必须在会话上调用 invalidateAndCancelfinishTasksAndInvalidate,首先...否则,繁荣,内存泄漏.

You have to call invalidateAndCancel or finishTasksAndInvalidate on the session, first of all... or else, boom, memory leak.

Apple 的 NSURLSession 类参考在侧边框中的管理会话部分说明:

Apple's NSURLSession Class Reference states in Managing the Session section in a sidebox:

重要--- 会话对象保持对委托的强引用,直到您的应用程序退出或显式地使会话无效.如果你不使会话无效,您的应用会泄漏内存,直到退出.

IMPORTANT --- The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.

是的.

您也可以考虑这两种方法:

You might also consider these two methods:

  • flushWithCompletionHandler:

将 cookie 和凭据刷新到磁盘,清除临时缓存,以及确保未来的请求发生在新的 TCP 连接上.

Flushes cookies and credentials to disk, clears transient caches, and ensures that future requests occur on a new TCP connection.

  • resetWithCompletionHandler:
  • 清空所有 cookie、缓存和凭证存储,删除磁盘文件,将正在进行的下载刷新到磁盘,并确保未来请求发生在新的套接字上.

    Empties all cookies, caches and credential stores, removes disk files, flushes in-progress downloads to disk, and ensures that future requests occur on a new socket.

    ... 直接引用自上述 NSURLSession 类参考.

    ... as directly quoted from the aforementioned NSURLSession Class Reference.

    我还应该注意您的会话配置可能会产生影响:

    I should also note that your session config can have an effect:

    - (void)setupSession {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        config.URLCache = nil;
        config.timeoutIntervalForRequest = 20.0;
        config.URLCredentialStorage = nil;
        config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
        self.defaultSession = [NSURLSession sessionWithConfiguration:config];
        config = nil;
    }
    

    一个关键,如果你不使用 [NSURLSession sharedSession] 单例,是让你有自己的自定义单例 NSObject 子类,它有一个会话作为属性.这样,您的会话对象就会被重用.每个会话都会创建与您的应用相关联的 SSL 缓存,需要 10 分钟才能清除,如果您为每个请求的新会话分配新内存,那么无论您是 invalidateAndCancel 还是刷新/重置会话,您都会看到 SSL 缓存中的无限内存增长会持续 10 分钟.

    One key, if you are not using the [NSURLSession sharedSession] singleton, is for you to have your own custom singleton NSObject subclass that has a session as a property. That way, your session object gets reused. Every session will create an SSL cache associated to your app, which takes 10 minutes to clear, if you allocate new memory for a new session with each request, then you will see unbounded memory growth from SSL caches that persist for 10 minutes regardless of whether or not you invalidateAndCancel or flush/reset the session.

    那是因为安全框架会私下管理 SSL 缓存,但会根据它锁定的内存向您的应用收费.无论您是否将配置的 URLCache 属性设置为 nil,都会发生这种情况.

    That's because Security framework privately manages the SSL cache, but charges your app for the memory it locks up. This will happen whether or not you set your configuration's URLCache property to nil.

    例如,如果您习惯于说每秒 100 个不同的 Web 请求,并且每个请求都使用一个新的 NSURLSession 对象,那么您正在创建类似 400k 的 SSL 缓存每秒.(我观察到每个新会话负责大约 4k 的安全框架分配.)10 分钟后,这是 234 兆字节!

    For example, if you are in the habit of doing say, 100 different web requests per second, and each one uses a new NSURLSession object, then you're creating something like 400k of SSL cache per second. (I have observed that each new session is responsible for appx. 4k of Security framework allocations.) After 10 minutes, that's 234 megabytes!

    所以从 Apple 那里得到提示并使用带有 NSURLSession 属性的单例.

    So take a cue from Apple and use a singleton with an NSURLSession property.

    请注意,backgroundSessionConfiguration 类型为您节省此 SSL 缓存内存以及 NSURLCache 等所有其他缓存内存的原因是因为 backgroundSession 将其处理委托给现在进行会话的系统,而不是您的应用程序,因此即使您的应用程序未运行,它也可能发生.所以它只是对你隐藏起来......但它就在那里......所以如果那里有巨大的内存增长,我不会让Apple拒绝你的应用程序或终止它的后台会话(即使Instruments不会显示它你,我打赌他们能看到).

    Note that the reason the backgroundSessionConfiguration type saves you this SSL cache memory, and all other cache memory like NSURLCache, is because a backgroundSession delegates its handling to the system who now makes the session, not your app, so it can happen even if your app is not running. So it's just hidden from you... but it's there... so I would not put it past Apple to reject your app or terminate its background sessions if there is huge memory growth back there (even though Instruments won't show it to you, I bet they can see it).

    Apple 的文档说 backgroundSessionConfiguration 对于 URLCache 的默认值为零,而不仅仅是零容量.因此,尝试使用临时会话或默认会话,然后将其 URLCache 属性设置为 nil,如我上面的示例所示.

    Apple's docs say that backgroundSessionConfiguration has a nil default value for the URLCache, not just a zero capacity. So try an ephemeral session or default session and then set its URLCache property to nil, as in my example above.

    如果您不打算使用缓存,也可能将 NSURLRequest 对象的 cachePolicy 属性设置为 NSURLRequestReloadIgnoringLocalCacheData 也是一个好主意 :D

    It's probably also a good idea to set your NSURLRequest object's cachePolicy property to NSURLRequestReloadIgnoringLocalCacheData also, if you are not going to have a cache :D

    这篇关于使用 NSURLSession.downloadTaskWithURL 时出现内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆