1

Please I'm using this extension to get the past time from the date. But I'm seeing memory leak in RelativeDateTimeFormatter() Any suggestions please?

extension Date {
    func timeAgoDisplay() -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter.localizedString(for: self, relativeTo: Date())
    }
}

enter image description here

  • How are you creating so many Formatters? Are you invoking your extension in a `for {}` loop? – Duncan C Aug 21 '22 at 22:44
  • As already mentioned this will create a new formatter every time you call this method – Leo Dabus Aug 21 '22 at 22:50
  • @DuncanC it really doesn’t matter if it is a loop or a timer. What OP needs is to hold a reference of the formatter declaring it at the view controller or create a static one. – Leo Dabus Aug 21 '22 at 22:53
  • 1
    Sure it matters. If the Formatters are Autoreleased then they will accumulate until the loop returns. It would be an artificial case, demonstrating a problem that isn’t really a problem. First I want to understand the scope of the “problem” before suggesting an answer. (Like putting the code inside the loop that creates Formatters in an `@autorelease` block.) – Duncan C Aug 21 '22 at 23:08
  • Agreed that putting the formatter in a static var would be a good solution, but the OP is using an extension, which can’t allocate new storage. – Duncan C Aug 21 '22 at 23:10
  • @DuncanC an autorelease block might solve the leak but it would still be very expensive to create a new formatter every time he calls this method – Leo Dabus Aug 21 '22 at 23:15
  • Sure. Again, though, it matters how often it's used. Though somewhat expensive to create, Formatters still only take a fraction of a second to create. If you're only creating a handful at a time you won't notice the time/memory load. – Duncan C Aug 22 '22 at 01:49
  • Oh, my bad. I guess you’re right about static vars in extensions. Anyway, unless your use-case involves creating Formatter objects over and over in a loop, the performance hit is usually not noticeable. If you ARE creating them in a loop, don’t. Use one shared Formatter. But as an experiment, I bet putting the looped code in an `@autorelease{}` block would solve the growing memory footprint problem (which is not actually a leak.) – Duncan C Aug 23 '22 at 18:26

1 Answers1

0

You should generally not worry about negligible amounts of memory that might leak from inside the frameworks. That having been said, you should avoid instantiating unnecessary formatters. E.g., you could create a static variable and re-use it:

extension Date {
    private static var formatter: RelativeDateTimeFormatter = {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter
    }()
    
    func timeAgoDisplay() -> String {
        Self.formatter.localizedString(for: self, relativeTo: Date())
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044