Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alamofire memory leaks Instruments

I am trying to clean my app from memory leaks and I have a few problems understanding this

enter image description here

  • Why Alamofire function Request.serializeResponseJSON is called 30 seconds after I've launched app: I did not touch anything or navigate anywhere, the screen was static.

  • Why does it leaks?

  • Why does my code leaks?

I get same leaks when the screen have loaded.

What I've tried so far:

  • Autoreleasepool;

  • Appending to and initialising arrays in every way that possible;

  • Changing all variable (class, func) to be optional/not optional/weak;

  • Initialising classes in UIViewController;

  • Initialising classes in main thread;

  • Searching these problems in the internet.

  • I've found out, using Xcode memory tool, that it is somehow connected with _ContiguousArrayStorage, but I do not understand how and what is it actually. enter image description here

I am out of any ideas what is wrong here. Any tips would be much appreciated.

Here is all related code: My general API request

public func requestWithLocation(_ httpmethod: Alamofire.HTTPMethod, URL: String, parameters: [String: AnyObject]?, completionHandler: @escaping CompletionHandler) -> (){
    var header: HTTPHeaders = [:]
    var location: [String: Double] = [:]
    let locationManager = CLLocationManager()

    if (CLLocationManager.authorizationStatus() == .authorizedWhenInUse
        || CLLocationManager.authorizationStatus() == .authorizedAlways) &&  locationManager.location != nil {
        location = [
            "lon" :  locationManager.location!.coordinate.longitude,
            "lat" :  locationManager.location!.coordinate.latitude
        ]
    }

    if User.sharedInstance.token != "" {
        header["Authorization"] = User.sharedInstance.token
    }

    var parametersWithLocation = parameters ?? [:]
    parametersWithLocation["location"] = location as AnyObject

    Alamofire.request("\(serverAddress)/\(URL)", method: httpmethod, parameters: parametersWithLocation, encoding: JSONEncoding.default, headers: header).validate().responseJSON { response in
        var data: JSON?

        if response.result.value != nil {
            data = JSON(response.result.value!)
        }
        if User.sharedInstance.token == "" {
            User.sharedInstance.token =  response.response?.allHeaderFields["Authorization"] as! String
        } else {
            if let header = response.response?.allHeaderFields["Authorization"] as? String {
                if User.sharedInstance.token != header {
                    User.sharedInstance.token = header
                }
            }
        }
        completionHandler(data, response.result.error as NSError?)
    }
}

My screen request

class func requestMainScreen(handler: @escaping ([ShortRestaurant], [ShortRestaurant], [ShortRestaurant]) -> ()) {
    var dataForBestChoise: [ShortRestaurant] = []
    var dataForTop: [ShortRestaurant] = []
    var dataForNearest: [ShortRestaurant] = []

    let group = DispatchGroup()
    group.enter()

    APIModel.sharedInstance.requestWithLocation(.post, URL: "restaurants/near", parameters: nil, completionHandler: {(data, error) in

        guard let `data` = data else {
            group.leave()
            return
        }
        for JSON in data["restaurants"].arrayValue {
            dataForNearest.append(ShortRestaurant.initFromJSON(JSON)) //here is leak
        }
        group.leave()
    })

    group.enter()
    APIModel.sharedInstance.requestWithLocation(.post, URL: "restaurants/top", parameters: nil, completionHandler: {(data, error) in
        guard let `data` = data else {
            group.leave()
            return
        }
        for JSON in data["restaurants"].arrayValue {
            dataForTop.append(ShortRestaurant.initFromJSON(JSON))//here is leak
        }
        group.leave()
    })

    group.enter()
    APIModel.sharedInstance.requestWithLocation(.post, URL: "restaurants/personal", parameters: nil, completionHandler: {(data, error) in

        guard let `data` = data else {
            group.leave()
            return
        }

        for JSON in data["restaurants"].arrayValue {
             dataForBestChoise.append(ShortRestaurant.initFromJSON(JSON)) //here is leak
        }
        group.leave()
    })

    group.notify(queue: DispatchQueue.main) {
        handler(dataForBestChoise, dataForTop, dataForNearest)
    }
}

My classes (I know this kind of initialisation is kinda wrong, but I'd changed to init(data: JSON) - did not help:

class func initFromJSON(_ data: JSON) -> ShortRestaurant {
    let restaurant = ShortRestaurant()
    restaurant.id                       = data["id"].stringValue
    restaurant.name                     = data["name"].stringValue
    restaurant.image                    = data["img"].stringValue
    restaurant.description              = data["shortDesc"].stringValue
    restaurant.nameOfMetrostatin        = data["address"]["metro"]["name"].stringValue
    restaurant.mapType                  = data["mapType"].stringValue
    restaurant.address                  = data["address"]["street"].stringValue
    restaurant.longitude                = data["address"]["location"][0].doubleValue
    restaurant.latitude                 = data["address"]["location"][1].doubleValue
    restaurant.phone                    = data["phone"].stringValue
    restaurant.workTime                 = data["currentWork"].stringValue
    restaurant.avarageBill              = data["price"].stringValue
    restaurant.peopleInfo               = data["croud"].stringValue
    restaurant.rating                   = data["rating"].stringValue
    restaurant.ratingTrend              = data["trend"].stringValue
    restaurant.distance                 = data["distance"].doubleValue
    restaurant.isFavourited             = data["isFavourited"].bool ?? false
    restaurant.specialOfferDescription  = data["discounts"]["name"].string
    restaurant.specialOfferName         = data["discounts"]["type"].string
    restaurant.alertText                = data["label"]["name"].string
    restaurant.alertIcon                = data["label"]["type"].string
    restaurant.alertBackground          = data["label"]["color"].string
    restaurant.avaliableDates           = ReservationSchedule.initArrayFrom(data: data["availableDates"])
    restaurant.avaliableTimes           = data["scheduleRes"].arrayObject as? [String] ?? []
    restaurant.doesHaveDiscount         = data["discounts"]["id"].string != nil
    restaurant.doesHaveEvent            = data["events"]["id"].string != nil
    restaurant.weeklyTop                = data["weeklyTop"].bool ?? false
    restaurant.monthlyTop               = data["monthlyTop"].bool ?? false
    restaurant.yearTop                  = data["yearTop"].bool ?? false
    restaurant.isActive                 = data["isActive"].bool ?? true
    return restaurant
}

Array of these leaks:

class ReservationSchedule {
  var description: String
  var data: String
  var dayTitle: String
  var fullTitle: String

  init(data: JSON) {
    self.data = data["value"].stringValue
    self.dayTitle = data["day"].stringValue
    self.description = data["label"].stringValue
    self.fullTitle = data["title"].stringValue
  }

  class func initArrayFrom(data: JSON) -> [ReservationSchedule] {
    var schedule: [ReservationSchedule] = []
    for day in data.arrayValue {
        schedule.append(ReservationSchedule.init(data: day)) //here is leak
    }
    return schedule
  }
}
like image 678
JuicyFruit Avatar asked Jan 11 '17 15:01

JuicyFruit


1 Answers

Have you tried setting URLCache time to request, This will remove the the cache of request and free's the memory

like image 55
Albi Avatar answered Oct 29 '22 08:10

Albi