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.
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?
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];
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 = ^...
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