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

查看:143
本文介绍了使用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:

推荐答案

您必须在会话中调用 invalidateAndCancel finishTasksAndInvalidate ,首先......或者,繁荣, 内存泄漏。

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] singleton,用于拥有自己的自定义单例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缓存每秒。 (我观察到每个新会话都负责appx.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将其处理委托给现在进行会话的系统,而不是您的应用程序,因此即使您的应用程序未运行也可能发生。所以它只是隐藏在你身上......但它就在那里......所以如果那里存在巨大的内存增长,我不会把它放过苹果公司拒绝你的应用程序或终止它的后台会话(即使仪器不会显示它你,我打赌他们可以看到它)。

    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天全站免登陆