Reputation: 2668
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
}
}
Upvotes: 19
Views: 1212
Reputation: 1855
Have you tried setting URLCache time to request, This will remove the the cache of request and free's the memory
Upvotes: 1