0

I tried to check my app for memory leaks using Leaks from Instrument. It turns out that the Leaks points out that the memory leak is in on of my extension: Date. The extension function is used inside a View struct in swiftUI to a @Published date: Date property from my viewModel. Am I doing something wrong? What do I have to do to not have a strong reference inside the extension? here is my extension:

extension Date {
    func toString(withFormat customFormat: String = "dd MMM YYYY") -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = customFormat
        return dateFormatter.string(from: self)
    }
}

This is the struct that has a date as a property:

struct Article: Codable, Hashable {
    let source: Source?
    let title: String?
    let author: String?
    let description: String?
    let url: String?
    let urlToImage: String?
    let publishedAt: Date?
    let content: String?
}

My ViewModel that has arrays of Articles:

    class NewsListViewModel: ObservableObject {
        @Published var searchedArticles = [Article]()
        @Published var tabsArticles = [Tabs : [Article]] ()
        @Published var isSearching = false
        @Published var searchText = ""
        @Published var selectedTab = Tabs.general
        @Published var error: Error?
    }

And the My main list view:

struct NewsList: View {
    @ObservedObject var viewModel: NewsListViewModel
    
    var body: some View {
        NavigationView {
            //.....//
            ScrollableNewsListView(articles: articles, category: viewModel.selectedTab.rawValue, checkLastCellAction: checkActionForLastCell)
            //.....//
            
        }
    }
}

StrollableView:

private struct ScrollableNewsListView: View {
    var articles: [Article]

    var body: some View {
         //.....//
         ForEach(articles.indices, id: \.self) { index in
                 NewsCell(article: articles[index])
                     .onAppear {
                         checkLastCellAction(index)
                         //checkActionForLastCell(index: index)
                     }                        
         }
         //.....//
    }
}

The cellView that I user inside a LazyVStack:

private struct NewsCell: View {
    var article: Article
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack {
                URLImage(url: imageURL)
                    .frame(width: 50, height: 50)
                    .cornerRadius(25)
                VStack(alignment: .leading) {
                    Text(article.author ?? "news_details_unknown_author".localized)
                    Text(dateString)// <-------- HERE I USE MY DateString that calls .toString() from Date extension
                        .font(.SFDisplayRegularFont(with: 12))
                        .foregroundColor(.gray)
                }
                Spacer()
            }

        }.padding(.all, 15)
        .background(Color.newsCellBackground)
        .cornerRadius(10)
    }
    var dateString: String {
        guard let date = article.publishedAt else { return ""}
        return date.toString()//<------------ Date.toString()
    }
    
    var title: String {
        article.title ?? ""
    }
}

Picture of Leaks from Instruments: enter image description here

Adrian Macarenco
  • 165
  • 2
  • 11
  • How large is the leak? If this is a <100 bytes that occurs once (rather than per use), then this is normal. Cocoa has small leaks, and the leaks instrument has false positives. If it is something that leaks every time you use it, then that's a real thing. – Rob Napier Mar 11 '21 at 13:53
  • I uploaded a picture with leaks from Instruments. I think that each cell that appears creates a memory leak, looks like it is because of that method from Date extension : .toString() – Adrian Macarenco Mar 11 '21 at 14:16
  • Set `.locale`. See https://stackoverflow.com/questions/7136011/nsdateformatter-memory-leak-issue (DateFormatter likely creates is own Locale object, and may leak it, or it may cache it and Instruments is just wrong. Depends on if this leak continues every time you use it.) – Rob Napier Mar 11 '21 at 14:45
  • Doesn't look like the issue is in this code snippet. Maybe you can provide one from ViewModel's code where this is called ? On a side note, init of `DateFormatter` is somewhat expensive, so I would suggest that you find a way to avoid doing it for every call to your function. – mkbrwr Mar 11 '21 at 00:46
  • I added the structs and viewModel class. – Adrian Macarenco Mar 11 '21 at 11:35
  • You can try removing `let dateFormatter = DateFormatter()` from extension to avoid creating `DateFormatter` objects for each call to your extension. Maybe just make it a global variable for a quick check. And if it works, find more architecturally sound place for it. – mkbrwr Mar 11 '21 at 15:05
  • I have the exact same problem with a VStack requesting a string from a date. Any solutions to this ? – PoolHallJunkie Sep 01 '22 at 11:42

0 Answers0