swiftmemory-leaksjsondecoder

Swift - Memory Leak in JSONDecoder


I discovered some memory leaks in my iOS app when it decodes server responses. These memory leaks don't happen all the time. I am using the following code to decode the response. The codes are inside a generic struct. T.self is also a struct not a class.

struct Response<T:Decodable> {
    
    var value: T?
    var result: ProcessResult<CustomError>
    var request: TRequestHeader
    
    init(_ request: TRequestHeader) {
        self.request = request
        result = .noDataReceived
    }
    
    init(_ request: TRequestHeader, _ responseData: Data) {
        self.init(request)
        do {
            try autoreleasepool {
                // parse the server response
                if let dict = try JSONSerialization.jsonObject(with: responseData) as? [String:Any] {
                    var success = dict["success"] as? Bool ?? false
                    var dataDict: [String:Any]?
                    var dataArray: [Any]?
                    
                    dataDict = dict["data"] as? [String:Any]
                    if dataDict == nil {
                        dataArray = dict["data"] as? [Any]
                    }
                    
                    // now we can decode the JSON data
                    if success {
                        if let dict = dataDict {
                            if let data = try? JSONSerialization.data(withJSONObject: dict, options: []) {
                                let decoder = JSONDecoder()
                                if let decodedObj = try? decoder.decode(T.self, from: data) {
                                    value = decodedObj
                                }
                            }
                        } else if let array = dataArray {
                            if let data = try? JSONSerialization.data(withJSONObject: array, options: []) {
                                let decoder = JSONDecoder()
                                if let decodedObj = try? decoder.decode(T.self, from: data) {
                                    value = decodedObj
                                }
                            }
                        }
                        
                        result = .success
                    }
                }
            }
        } catch let error {
            result = .failed(CustomError(code: 0, message: error.localizedDescription))
        }
    }
}

So the if let decodedObj = try? decoder.decode(T.self, from: data) causes the leak and I don't know how to prevent this. I covered the entire code block with try autoreleasepool { } but it didn't solve my problem.

I understand that it is not easy to understand what is causing the leak from this code snippet but when I googled JSONDecoder().decode memory leak issues I found too many complaints from other developers. Some says it is a bug in Swift and I can say Swift 5.0 didn't solve my problem. I have checked my Xcode's Swift version and it is 5.0.

Any comments and/or code shares about generic functions that does decoding server response highly appreciated. Thank you.

enter image description here


Solution

  • I came across a similar issue where I had a loop creating temporary objects using a JSONDecoder. The temporary objects were not released so the memory usage of the command line tool I was developing kept increasing. I was able to resolve it using an autoreleasepool. See https://medium.com/@itsachin523/understanding-autoreleasepool-in-swift-with-examples-5850d7ce8ed.