Swift Calendar.current内存泄漏?

Mar*_*rio 5 calendar ios swift

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

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

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        while Calendar.current.component(.year, from: Date()) > 0
        {
            // why does the memory keep increasing?
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎导致内存泄漏.

此示例将显然阻止UI线程,但它不应该导致内存不断增加,或者至少在循环完成后释放.至少从我的理解不应该.我在这里误解了一些根本的东西吗?或者这是一个错误?

我该如何解决这个问题?

更新

引用评论:

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

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

好吧,但如果我运行一个只有日期比较的循环我没有遇到同样的问题.这是因为优化器介入吗?

while Date() > Date(timeIntervalSince1970: 200)
{
    // no increase of memory here
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 5

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

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

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

在深入研究解决方案之前,我将把您的示例调整为保证最终退出的内容。例如,让我们编写一个例程,该例程旋转直到minute与当前时间相关的时间发生变化(例如,当当前分钟结束并下一分钟开始时)。我们假设previousValue包含当前minute值。

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

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

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

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

    // do something
} while minute == previousValue
Run Code Online (Sandbox Code Playgroud)

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

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


你问:

好吧,但如果我运行一个仅进行日期比较的循环,我不会遇到同样的问题。这是因为优化器介入了吗?

不,这只是因为 Date() 根本没有像Calendar.current.component(_:from:)显然那样引入任何自动释放对象。(顺便说一句,Apple 一直擅长在整个代码库中缓慢删除自动释放对象,因此您可能会在未来的某个日期发现,即使这可能不需要手动自动释放池。)