I am trying to clean my app from memory leaks and I have a few problems understanding this
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.
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
}
}
Have you tried setting URLCache time to request, This will remove the the cache of request and free's the memory
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With