0

I've been trying to find out what causes memory leaks, but in Instruments, I am being told that the memory leak happens at seriesDownloadingQueue.addOperation(downloadOperation) and in the class that calls the method getSeries(), which starts the updating process.

I'm completely new to asynchronous programming and memory leaks. I've read some posts on stack overflow about finding and fixing the leaks. I've also read an article from http://www.raywenderlich.com on how to use Instruments. This app is a database preloader. It downloads and processes information. The .sqlite file will be used in a mobile app.

Below is the code which allegedly causes the memory leak. If you need more information, I'll provide it.

import Foundation
import CoreData

class SerieFetcher: NSObject{
    dynamic var didGetSeries: Bool = false
    static var doneSeries: Int = 0

    func getSeries(){
        var seriesDownloadingQueue: NSOperationQueue{
            let val = NSOperationQueue()
            val.maxConcurrentOperationCount = 32
            val.name = "Serie Downloading & Processing Queue"
            return val
        }

        var defaultSessionConfiguration:NSURLSessionConfiguration{
            let val = NSURLSessionConfiguration.defaultSessionConfiguration()
            val.HTTPMaximumConnectionsPerHost = 20
            return val
        }
        let defaultSession: NSURLSession = NSURLSession(configuration: defaultSessionConfiguration,delegate: nil, delegateQueue: seriesDownloadingQueue)

        if let countries = fetchCountries(){
            for country in countries{
                if let url = NSURL(string:(BASE_URL + "series/"+CAT_STAMPS+"producer/\(country.0)")){
                    let downloadOperation = downloadSeriesOperation(downloadURL: url, countryObjectID: country.1, countryCount: countries.count, defaultSession: defaultSession , completionHandler: { [weak self](didGetSeries) in
                        if(didGetSeries == true){
                            self!.didGetSeries = didGetSeries
                            print("Finished Downloading Series")
                        }
                    })
                    downloadOperation.completionBlock = nil
                    seriesDownloadingQueue.addOperation(downloadOperation)
                }
            }
        }
    }

    func fetchCountries() -> [Int: NSManagedObjectID]?{
        let fetchRequest = NSFetchRequest(entityName: "Country")
        fetchRequest.resultType = .DictionaryResultType
        let objectIDDesc = NSExpressionDescription()
        objectIDDesc.name = "objectID"
        objectIDDesc.expression = NSExpression.expressionForEvaluatedObject()
        objectIDDesc.expressionResultType = .ObjectIDAttributeType
        fetchRequest.propertiesToFetch = ["countryID",objectIDDesc]
        fetchRequest.returnsDistinctResults = true

        do{
            let results = try managedContext.executeFetchRequest(fetchRequest) as! [NSDictionary]

            var countryIDs: [Int: NSManagedObjectID] = [:]
            for result in results{
                let countryID: Int = result.valueForKey("countryID") as! Int
                let objectID: NSManagedObjectID = result.valueForKey("objectID") as! NSManagedObjectID

                countryIDs.updateValue(objectID, forKey: countryID)
            }
            return countryIDs

        }catch let error as NSError{
            print(error.localizedDescription)
        }
        return nil
    }
}

class downloadSeriesOperation: NSOperation{
    let countryObjectID:NSManagedObjectID
    let downloadURL:NSURL
    let countryCount:Int
    let defaultSession:NSURLSession
    let completionHandler: (didGetSeries: Bool) -> Void

    init(downloadURL:NSURL, countryObjectID: NSManagedObjectID,countryCount: Int, defaultSession:NSURLSession, completionHandler: (didGetSeries:Bool) -> Void){
        self.downloadURL = downloadURL
        self.countryObjectID = countryObjectID
        self.countryCount = countryCount
        self.defaultSession = defaultSession
        self.completionHandler = completionHandler
    }

    override func main() {
        let dataTask = defaultSession.dataTaskWithURL(downloadURL, completionHandler: { (data, response, error) in
            let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
            privateMOC.persistentStoreCoordinator = managedContext.persistentStoreCoordinator
            privateMOC.undoManager = nil

            var parsedData: NSArray?
            do{
                parsedData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as? NSArray
            }catch let error as NSError{
                print(error.localizedDescription)
            }

            for val in parsedData!{
                if let serieID = Int(val[0] as! NSString as String){
                    if let serieName = val[1] as? NSString as? String{
                        if serieID == 0{
                            continue
                        }

                        let serie = NSEntityDescription.insertNewObjectForEntityForName("Serie", inManagedObjectContext: privateMOC) as! Serie
                        serie.country = privateMOC.objectWithID(self.countryObjectID) as? Country
                        serie.serieID = serieID
                        serie.serieName = serieName
                    }
                }
            }

            privateMOC.performBlock({
                do{
                    try privateMOC.save()
                    privateMOC.reset()
                }catch let error as NSError{
                    print(error.localizedDescription)
                }
            })

            SerieFetcher.doneSeries += 1
            print(self.countryCount,"/",SerieFetcher.doneSeries)

            if(SerieFetcher.doneSeries == self.countryCount){
                self.completionHandler(didGetSeries: true)
            }
        })

        dataTask.resume() //Start the Download
    }
}
Vemonus
  • 868
  • 1
  • 16
  • 28
sloeberGJ
  • 355
  • 3
  • 13
  • 3
    How large is the leak and how commonly does it occur (does this code run often and every time it runs you leak the same amount of memory?) If the answers are that you're talking about 100 bytes and it happens a few times, then yes, Foundation has small leaks and there are some things that will be reported as leaks that may not actually be leaks, and there is little you can do about that. Before diving deeply into this, make sure the memory you're trying to reclaim is worthwhile. If it is many kB (and especially if it would grow to MB), and happens regularly, it is more likely your fault. – Rob Napier Aug 16 '16 at 13:31
  • Something strange has happend, it suddenly doesn't leak memory any more? I posted non working code with [weak self], which i red here: http://stackoverflow.com/questions/31696417/swift-closure-with-variable-causing-memory-leak/31698730#31698730 It made my code stop working, now i removed it, it suddenly doesn't leak any more? While it also leaked memory before I added it. Is this normal behavior? I tried multiple times before I asked this question here. – sloeberGJ Aug 16 '16 at 15:24

0 Answers0