I've used Google Analytics on several iOS apps. No problems. This time, problem.
I do the basic setup using version 3.0. Add library/header, include required frameworks, and stuff the boiler plate code into the AppDelegate.m
. So far so good, everything works as expected. I take my first UIViewController
and change it to extend GAITrackedViewController
and it hits the fan. The app freezes up on the first screen and memory usage starts going up about 4Meg per second. So I change the UIViewController
back and all is good. I try making the screen name call manually in viewDidLoad
.
// Analytics
id tracker = [[GAI sharedInstance] defaultTracker];
[tracker set:kGAIScreenName value:@"Initial"];
[tracker send:[[GAIDictionaryBuilder createAppView] build]];
Same thing happens. My view controller has a couple custom container views and it the root view controller on a generic UINavigationViewController
. I figure it's probably the custom containers confusing it about which is the active view controller and what screen name to use (but I'm not seeing any sign of this in the logging).
Has anyone run into this problem and been able to nail down exactly what's causing it and how to work around it?
João's answer is correct, but I'd like to explain it more.
From Google's Getting Started document
If your app uses the CoreData framework: responding to a notification, e.g. NSManagedObjectContextDidSaveNotification, from the Google Analytics CoreData object may result in an exception. Instead, Apple recommends filtering CoreData notifications by specifying the managed object context as a parameter to your listener.
What that means is...
// This code will cause a problem because it gets triggered on ANY NSManagedObjectContextDidSaveNotification.
// (both your managed object contact and the one used by Google Analytics)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
// This code is safe and will only be trigger from the notification generated by your Managed Object Context.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:myManagedObjectContext];
Now I read the docs and I had done this properly, but I was still having the problem. Turns out I didn't update my code for when I removed the notification.
// Not Safe
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:nil];
// Safe
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:myManagedObjectContext];
The moral of the story is, pay attention to your notification listeners. It takes a couple seconds to specify a listener for a specific object and it can take a long time to debug an issue because you accidentally listening to events you don't want to or remove listening to events.
Solution 1: Use a specific moc on object
parameter when observing NSManagedObjectContextDidSaveNotification
, this will allow you to observe only saves on the given moc.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:managedObjectContext];
Solution 2: If you are using the Core Data technique of merging mocs created on background threads, you cannot easily solve in the suggested way, so the alternative is to change your method that handles notification in order to avoid merging when the persistentStoreCoordinator
of the saved moc doesn't match the persistentStoreCoordinator
of your main moc.
- (void)managedObjectContextDidSave:(NSNotification *)notification {
if ([NSThread isMainThread]) {
NSManagedObjectContext *savedMoc = notification.object;
// Merge only saves of mocs that are not my managedObjectContext
if (savedMoc == self.managedObjectContext) {
return;
}
// Merge only saves of mocs that share the same persistentStoreCoordinator of my managedObjectContext (i.e.: ignore the save of Google Analytics moc)
if (savedMoc.persistentStoreCoordinator != self.managedObjectContext.persistentStoreCoordinator) {
return;
}
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
else {
[self performSelectorOnMainThread:@selector(handleBackgroundContextSaveNotification:) withObject:notification waitUntilDone:YES];
}
}
I was having the exact same problem.
I've managed to find the solution in my case: I was registering to NSManagedObjectContextDidSaveNotification
without specifying the context:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
Removing this listener solved my out of memory problems.
Cheers
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