Swift Calendar.current内存泄漏? [英] Swift Calendar.current memory leak?

查看:98
本文介绍了Swift Calendar.current内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在应用程序中遇到内存问题,已经能够将其分解为NSCalendar。

I've encountered a memory issue in an app and I've been able to break it down to the NSCalendar.

像这样的简单视图控制器:

A simple view controller like this:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        while Calendar.current.component(.year, from: Date()) > 0
        {
            // why does the memory keep increasing?
        }
    }
}

似乎导致内存泄漏。

此示例将明显阻塞UI线程,但不应导致内存不断增加,或至少在循环完成后将其释放。至少从我的理解来看,不应该这样。我在这里误解了一些基本的东西吗?还是错误?

This example will obviosly block the UI thread but it should not cause the memory to continuously increase, or at least be released after the loop is done. Well at least from my understanding it shouldn't. Am I misunderstanding something fundamental here? Or is it a bug?

如何解决此问题?

引用自评论:


仅供参考-您的问题与NSCalendar无关。您的问题是您的while循环永远不允许清理内存

FYI - your issue has nothing to do with NSCalendar. Your issue is your while loop never allowing memory to be cleaned up

所有这些Date实例也占用了内存

All of those Date instances are taking up memory too

好吧,但是如果我运行的只是一个日期比较的循环,那么我就不会遇到相同的问题。

Well but if I run a loop with just a date comparison im not running into the same issue. Is this because the optimiser steps in?

while Date() > Date(timeIntervalSince1970: 200)
{
    // no increase of memory here
}


推荐答案

正如其他人指出的那样,问题在于 Calendar.current.component(_:from:)在后台引入了一个自动释放对象,该对象直到自动释放池耗尽后才会释放。

As others have pointed out, the problem is that Calendar.current.component(_:from:) is, behind the scenes, introducing an autorelease object, an object that is not released until the autorelease pool is drained.

在引用计数的早期,Objective-C代码是一种返回新分配的对象的通用方法,该对象在调用者完成操作后将自动释放。返回自动释放对象。这个对象只有在您退回到运行循环时才会被释放,这会消耗自动释放池。而且,您可以通过添加自己的手动自动释放池来控制重复创建自动释放对象的大型循环上的高水位标记。

Back in the early days of reference counting Objective-C code, the common way to return a newly allocated object that would be automatically released when the caller was done with it was to return an "autorelease" object. It was an object that would only be deallocated when you yielded back to the run loop, which would drain the autorelease pool. And you could control your high-water mark on large loops that repeatedly created autorelease objects by adding your own manual autorelease pools.

Swift本身并不创建自动释放对象,因此这个问题有点像我们在自己的Swift代码中通常不会遇到的Objective-C时代错误。但是,每当编写循环并调用可能在后台使用自动释放对象的Cocoa API的代码时(例如在这种情况下),我们都必须对此敏感。

Swift doesn't natively create autorelease objects, so this issue is a bit of an Objective-C anachronism that we don't generally encounter in our own Swift code. But we have to be sensitive to this whenever writing code that loops and calls Cocoa APIs that might be using autorelease objects behind the scenes, such as in this case.

深入研究解决方案,我将调整您的示例以确保最终退出。例如,让我们编写一个例程,该例程一直旋转到与当前时间关联的分钟为止(例如,当当前分钟结束并且下一分钟开始时)。假设 previousValue 包含当前的分钟值。

Before I dive into the solution, I'm going to tweak your example to something that is guaranteed to eventually exit. For example, let's write a routine that spins until the minute associated with the current time changes (e.g. when the current minute ends and the next starts). Let's assume that previousValue contains the current minute value.

诀窍是,我们需要将 autoreleasepool 内部循环。在以下示例中,我们利用了 autoreleasepool 是泛型的事实,它返回在其闭包内返回的内容:

The trick is that we need to put the autoreleasepool inside the loop. In the following example, we're taking advantage of the fact that autoreleasepool is generic that returns whatever is returned inside its closure:

while autoreleasepool(invoking: { Calendar.current.component(.minute, from: Date()) }) == previousValue {
    // do something
}

注意,如果您发现该模式难以阅读(需要一段时间才能使用)以闭包作为方法的参数),可以使用 repeat - until 循环来完成大致相同的操作:

Note, if you find that pattern hard to read (it takes a while to get used to closures as parameters to methods), you can use a repeat-until loop, to accomplish largely the same thing:

var minute: Int!
repeat {
    minute = autoreleasepool {
        Calendar.current.component(.minute, from: Date())
    }

    // do something
} while minute == previousValue






顺便说一句,这种循环快速旋转的过程效率极低。当然,正如您提到的那样,您永远不会在主线程上执行此操作(因为我们永远不想阻塞主线程)。但是您通常不会在 any 线程上执行此操作,因为旋转的计算量很大。有时您必须这样做(例如,无论如何都要在后台线程上进行一些复杂的计算,并且您希望它在某个特定的时间停止),但是十分之九的代码在设计中更深层地困扰着代码。通常,明智地使用计时器等可以实现所需的效果,而无需计算开销。


As an aside, this process of having a loop that spins quickly like this is highly inefficient. Certainly, as you mentioned, you would never do this on the main thread (because we never want to block the main thread). But you wouldn't generally do it on any thread because spinning is so computationally intense. Sometimes you have to do it (e.g. doing some complex calculation on background thread anyway and you want it to stop at some particular time), but 9 times out of 10, it's code smell for some deeper problem in the design. Often judicious use of timers or the like can accomplish the desired effect, without the computational overhead.

很难为您提供适合您情况的最佳解决方案,因为我们不知道您要解决的问题是什么。但请注意,通常不建议在线程上旋转。

It's hard to advise you about the best solution in your case, as we don't know what broader problem you're trying to solve. But simply be aware that spinning on a thread is generally inadvisable.

您问:


好吧,但是如果我运行一个仅比较日期的循环,则不会遇到相同的问题。

Well but if I run a loop with just a date comparison im not running into the same issue. Is this because the optimiser steps in?

不,这仅仅是因为Date()根本不会在混合中引入任何自动释放对象就像 Calendar.current.component(_:from:)一样。 (顺便说一句,Apple擅长在整个代码库中缓慢删除自动释放对象,因此您很可能会在以后的某个日期发现它,甚至可能不需要手动自动释放池。)

No, its simply because Date() simply doesn't introduce any autorelease objects to the mix like Calendar.current.component(_:from:) evidently does. (BTW, Apple's been good about slowly excising autorelease objects throughout their code base, so you're likely to discover at some future date, even this is likely to not require manual autoreleasepool.)

这篇关于Swift Calendar.current内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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