Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSArray Leak inside setCompletionBlock

Edit to Question: 21/10/2013 20:10 GMT

Added how the method is called below and also the object that is leaking is "allDBObjects" If i remove this and change the dictionary below to "NSMutableDictionary *objectsById" there is no leak.

Edit to Question: 21/10/2013

After a few days not on this issue I have come back to it. I believe this is all down to "The Deallocation Problem" (https://developer.apple.com/library/ios/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11). I have tested with both MKNetworkKit and AFNetworking 1.3.3 (Changing 1 method to use AFNetworking instead of MKNetwork Kit) and am still getting these objects leaking in my completion block. I have no references to self within my block and using AFNetworking I can see the completionBlock is set to nil and I have tried to manually break the retain cycle by setting the [weakOp setCompletionBlock:nil].

EDIT: The code sample below I tried to use properties and reference them as weakSelf. I have now changed these to local variables and they still leak.

Any ideas?

Original Question

I have taken over a project using MKNetworkKit and Core Data, after running the project through Leaks in instruments I can see a lot of leaked objects in various places in the app.

After debugging the code I can see the objects that are leaking are 2 fetch requests that are happening in the callback of a MKNetworkKit request (setCompletionBlock:). The fetch requests need to be done to check whether the data needs to be inserted or updated.

Some further information. Inside the completion block I am getting an instance of the ManagedObjectContext and creating it with concurrency type of "NSPrivateQueueConcurrencyType" and to perform the insert I am correctly calling "performBlock:" on the moc.

Please advise.

James

Sample Code of Block: Please note: I have commented out the 2 fetch requests are there are no leaks and putting them back in causes the leaks of hundreds of objects, also the weakSelf properties I am setting the NSDictionary and NSArray are (nonatomic, strong).

- (void) updateDbObjects: (int) page withCallback: (CompletionResultsNumberBlock) callback {  

#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
dispatch_queue_t callerQueue = dispatch_get_current_queue();
#pragma GCC diagnostic warning "-Wdeprecated-declarations"

__weak typeof(self) weakSelf = self;


NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[self createFullPath:urlStr]]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
    
    NSManagedObjectContext *moc = [weakSelf managedObjectContextForCurrentThread];
    DataRoot *dataRoot = [DataRoot sharedInstanceInMoc:moc];
    
    NSArray *returnJSON = JSON[@"object"];
    __block int count = returnJSON.count;
            
    if (!count)
    {
        dispatch_async(callerQueue, ^{
            callback(0);
        });
        return;
    }
    
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"DBObjects"];
    NSError *error;
    NSArray *allDBObjects = [moc executeFetchRequest:fetchRequest error:&error];
    
    NSMutableDictionary *objectsById = [NSMutableDictionary dictionaryWithObjects:allTeamsArray forKeys:[allTeamsArray valueForKey: GoalTeamObjectAttributes.teamId]];
            
    for (NSDictionary *rootDict in returnJSON)
    {
        GoalTeamObject *dbObject =  objectsById[rootDict[@"id"]];
        
        if (dbObject == nil)
        {
            dbObject = [DBObjects insertInManagedObjectContext:dataRoot.managedObjectContext];
        } 

        [weakSelf importStandardParametersFrom:rootDict into:dbObject withPrefix:@""];
        
    }
    
    returnJSON = nil;
    objectsById = nil;
    
    [dataRoot saveContext];
    
    NSError *childError = nil;
    
    if ([moc save:&childError]) {
        
        NSError *parentError = nil;
        if (![moc.parentContext save:&parentError]) {
            NSLog(@"Error saving parent");
        }
        
        dispatch_async(callerQueue, ^{
            callback(count);
        });
        
    } else {
        NSLog(@"Error saving child");
    }
    
    
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
    
}];
    
[operation start];

 }

This is how this code is called: It is called recursively in a loop as there are many pages of data.

__block int page = 1;

__weak typeof(self) weakSelf = self;

CompletionResultsNumberBlock loadData;

__block CompletionResultsNumberBlock block_loadData = loadData = ^(int results)
{
    if (results < 100)
    {
        dispatch_async(callerQueue, callback);
    } else {
        [weakSelf updateDbObjects:++page withCallback:block_loadData];
    }
};

[self updateDbObjects:page withCallback: loadData];
like image 569
jodm Avatar asked Nov 11 '22 20:11

jodm


1 Answers

This doesn't look right:

__block CompletionResultsNumberBlock block_loadData = loadData = ^...

Under ARC, the block would hold a strong reference to itself. Under ARC, you should do:

__block __weak CompletionResultsNumberBlock block_loadData = loadData = ^...
like image 151
newacct Avatar answered Dec 19 '22 00:12

newacct