0

I have an app that has a problem with memory leaks. After using various tools, I narrowed the leak down to a ByteCountFormatter.

I have created a very simple unit test to check the code:

import XCTest

final class DiskUtilitiesTests : XCTestCase
{
    let formatter = ByteCountFormatter()
    
    func testMemoryLeaks() {
        self.formatter.allowedUnits = .useBytes
        self.formatter.includesUnit = true
        
        for i in 0 ..< UInt64.max {
            let string = self.formatter.string(fromByteCount: Int64(i))
        }
    }
}

When running the unit test, the memory increases very fast. The code seems very simple, so I do not understand why the memory consumption increases at all. At first I thought that the memory of the string variables is freed once the for loop exits, but this is not the case. Even when the scope is left (for loop is exited), the memory is not freed.

As an additional test, I added the call to the formatter to a autoreleasepool:

import XCTest

final class DiskUtilitiesTests : XCTestCase
{
    let formatter = ByteCountFormatter()
    
    func testMemoryLeaks() {
        self.formatter.allowedUnits = .useBytes
        self.formatter.includesUnit = true
        
        for i in 0 ..< UInt64.max {
            autoreleasepool {
                let string = self.formatter.string(fromByteCount: Int64(i))
            }
        }
    }
}

Now the memory consumption is constant (both in Xcode and Activity Monitor). What is the explanation for this behaviour?

inexcitus
  • 2,471
  • 2
  • 26
  • 41
  • 1
    See https://stackoverflow.com/questions/70050353/swift-risk-in-using-autoreleasepool-cpu-usage/70051512#70051512: `ByteCountFormatter` is an Objective-C API which returns an autoreleased string, which means that it doesn't behave by usual Swift/ARC rules — and exists until the nearest surrounding `autoreleasepool` releases it. This is the perfect usage of a manual `autoreleasepool { ... }`: to insert an explicit scope for releasing the memory. – Itai Ferber Mar 30 '23 at 14:32
  • 1
    Autoreleased values are extremely common for existing Objective-C APIs, but you usually won't notice those values for long unless you exercise the worst-case scenario: a hot loop which allocates many autoreleased objects before returning. – Itai Ferber Mar 30 '23 at 14:33

0 Answers0