【问题标题】:Swift Dictionary Absurd Memory UsageSwift Dictionary 荒谬的内存使用
【发布时间】:2016-06-04 12:42:18
【问题描述】:

我在我的一个应用程序中遇到了一个有趣的问题。当多次访问Dictionary 时,我的应用程序的内存使用量会在几秒钟内飙升至 1 GB 以上。下面是一些显示问题的示例代码。

override func viewDidLoad() {
        let dictionary = ["key1":"value1"]
        let nsKey: NSString = "key1"
        let swiftKey = nsKey as String
        for _ in 0 ... 10000000 {
            dictionary[swiftKey]
        }
    }

重复访问字典会导致内存攀升,直到循环结束。我查看了工具并看到了大量的字符串分配。原来使用 NSString 是问题所在。

像这样将 nsKey 更改为 swift String 可以解决问题:

let nsKey = "key1"

将字典更改为 NSDictionary 也解决了这个问题:

let dictionary: NSDictionary = ["key1":"value1"]

有谁知道为什么使用强制转换的 NSString 访问字典会导致如此多的堆分配,除了上面描述的之外,还有其他修复方法吗?


这里有一些图片。看起来幕后字符串正在被分配并设置为 autorelease (或者我读错了下面的数据?)这可能是内存使用量不断分配然后在稍后耗尽的原因吗?如果这是真的,这应该被认为是一个“错误”吗?在 OS X 和 iOS 上都会出现此问题。

【问题讨论】:

    标签: ios swift macos performance dictionary


    【解决方案1】:

    最好的解决方案是不要在此处桥接到NSString。只需使用 Swift 类型。或者,正如您所发现的,您可以只使用 Foundation 类型(NSStringNSDictionary)。桥接可能需要制作临时副本。

    不过,无论如何,在这样的循环中,出于某种原因创建临时副本是很常见的(即使您避免了这个特定问题)。为了解决这个问题,您需要在循环中耗尽您的自动释放池。例如:

    let dictionary = ["key1":"value1"]
    let nsKey: NSString = "key1"
    let swiftKey = nsKey as String
    for _ in 0 ... 10000000 {
        autoreleasepool {         // <=== the scope of the current pool
            dictionary[swiftKey]
        }
    }
    

    添加这将使您的记忆保持稳定。这是在 Cocoa 的大循环中很常见的事情。否则,在您从顶级方法返回之前,池不会被耗尽。

    【讨论】:

    • 自动释放池的大推荐!我一直坚持使用 swift 类型,但不幸的是,在我的真实应用程序中,我的一些 swift 字符串在内部被视为 NSStrings,我猜是因为一些外部库正在返回桥接字符串。我尝试强制转换并尝试初始化 String(myNSString) 但问题仍然存在。我可能已经逐字节复制了字符串并以这种方式构建了一个真正的 Swift 字符串,但这似乎太过分了。但是这个自动释放池修复是完美的。您认为值得将其报告为性能“错误”吗?
    • 不太值得报告。几乎所有这种大小的现实世界循环都应该包装在一个自动释放池中。随着时间的推移,我预计桥接的成本会有所提高,并且无论如何它都会变得不那么普遍。您是对的,在某些情况下您目前实际上无法避免桥接字符串,并且通常应该避免尝试(您只会使事情过于复杂)。但是您应该避免无偿添加 NSString(我看到人们有时会这样做)。
    • @RobNapier,我在真实设备中尝试了没有自动释放块的 Epic Byte 代码,我没有遇到高内存使用情况。我只在模拟器中发现高内存使用问题,请您解释为什么行为不同。
    • 我的猜测是优化级别,但它可能是很多东西。平台之间编译器的差异;基础的区别。但最终优化的方式会有所不同。
    • @RobNapier 嘿,我为这个迟到的问题道歉,这种使用自动释放池的方法只有在底层代码使用自动释放而不是释放时才有效,对吗?换句话说,如果 Apple 选择立即释放字符串而不是使用自动释放,自动释放池将没有任何效果?它是否正确?谢谢!
    猜你喜欢
    • 2011-11-03
    • 2023-03-08
    • 2018-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 2017-05-18
    相关资源
    最近更新 更多