Reputation: 3393
I'm having a problem with the (huge) amount of memory my app consumes after I do a concrete operation. Basically, it's a set of around 80 HTTP
requests (*), that I must to wait all of them to finish, in a data sync operation. All is inside a NSOperation, that it's called by a NSOperationQueue. I receive the request, parse JSON, and save results in Core Data, nothing weird. The pseudo-code would be the following:
NSArray *idsToFetch = ...;
NSString *serverRequestFilter = ...;
//Suppose there are 80 batches, so here is one of them:
serverRequestFilter = [NSString stringWithFormat:---];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// <Setup some headers...>
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_enter(group);
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// <Some error handling here>
id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([res isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"];
NSArray *results = [d objectForKey:@"results"];
d = nil;
res = nil;
// <...>
// Save in Core Data
while (i < count) {
[moc performBlockAndWait:^{
Blablabla *bp;
bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc];
NSDictionary *rel = results[i];
[bp setXXX:[rel valueForKey:@"XXX"]];
// <the same for about 10 attributes>
}
}
// <Core data save>
dispatch_group_leave(group);
}
}] resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
At this point, the memory consumption can be as high as 120Mb. I can continue using the app, put it in background, or whatever, I must have the mother of the memory leaks. If I leave this view controller, the memory consumption keeps high.
I profiled the app, seeing a huge amount of memory being leaked in strings (?), but I don't know how to fix it.
Digging in the profile, the top 'responsible callers' from that strings are:
- [NSPlaceholderString initWithFormat:locale:arguments:] (15k+)
- [NSSQLCore _prepareDictionaryResultsFromResultSet:usingFetchPlan:] (15k+)
- [NSURL(NSURL) initWithString:relativeToURL:] (the number of HTTP requests)
Thanks in advance
(*) due to server limitations, I must do this way. I must fetch a lot of entities, so I do it with pagination.
Upvotes: 0
Views: 1402
Reputation: 4749
While working with blocks and memory management, please consider few points:
Try to use weak reference of class level instance variables.
dispatch_group_enter(group);
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// <Some error handling here>
id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([res isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"];
NSArray *results = [d objectForKey:@"results"];
d = nil;
res = nil;
// <...>
// Save in Core Data
// CHANGE: optimize memory with autorelease pool
@autoreleasepool{
__block NSInteger localCount = count;
[moc performBlockAndWait:^{
// CHANGE: add while loop inside performBlock, because, perform block in loop on managedobjectcontext is quite costly
while (i < localCount) {
Blablabla *bp;
bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc];
NSDictionary *rel = results[i];
[bp setXXX:[rel valueForKey:@"XXX"]];
// <the same for about 10 attributes>
}
}
// <Core data save>
}
dispatch_group_leave(group);
}
}] resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
Upvotes: 1