NSNotificationCenter crashes my app - ios

I've added a NSNotificationCenter observer that calls 2 selectors with the same name on two different view controllers.
It works, but when I run the app sometimes it crashes with this error message:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
or
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
Anyone has an idea why it crashes? Thank you!
.
My code:
fetchFromParse:
-(void)sendAllStores
{
[[NSNotificationCenter defaultCenter]postNotificationName:#"getStoresArrays" object:nil userInfo:self.storesDict];
}
firstVC.m:
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(getStoresArrays:) name:#"getStoresArrays" object:nil];
}
-(void)getStoresArrays:(NSNotification*)notification
{
NSLog(#“Working”);
}
secondVC.m:
-(void)prepareArrays
{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(getStoresArrays:) name:#"getStoresArrays" object:nil];
}
-(void)getStoresArrays:(NSNotification*)notification
{
NSLog(#“Working”);
}
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.secVC=[[secondVC alloc] init];
[self.secVC prepareArrays];
fetchFromParse *fetchFromParseObj=[[fetchFromParse alloc] init];
[fetchFromParseObj getStoresFromParse];
Return YES;
}

Notification Crashes
Typically you might see a crash of this nature if you attempt to close a view controller without removing the observer. So that part of the answer provided by Mr. Patil, is absolutely required. Your use-case will vary about where and how you remove the observer. If you remove it too soon you might end up crashing if you try to reference any related objects.
So you might want to remove the self.storesDict object or at least verify that it is not nil before using it.
Debugging
Here's a quick tutorial on debugging with Xcode. It's for an older version but it should get you up to speed quickly. You can also review Apple docs for more on collecting crash logs.
Logs
You should go to the reports navigator and copy a bit more of your log so that we can determine a more precise cause of the issue. You can add a breakpoint right before the faulty code and then step through the issue. Copy the debug console if nothing else.
Getting the crash log
You can open Xcode go the Window menu select Devices. Select your attached device (iPhone/iPad) and click the View Device Logs button. From the list select your app name/crash date. Copy the data to the question.
More Information
The more information you provide on crashes the more likely we can help you. I suspect that the answer is you are either trying to access something that is nil or not releasing the observer at the right time. It might not be appropriate to release the observer when your view disappears but you have not provided enough information to make that obvious.
How do the different view controllers work together? Are you certain that the Notifications is causing the crash?? Put a breakpoint at the post notification and in each selector and then debug the app until it crashes. You will need to identify the conditions that precede the crash. I'll refine this answer if you let me know when you update the question.

First thing I would do is do a global search and make sure no other classes are listening to that notification. Then I would make sure that in the classes that you have added an observer for that notification you do the following:
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

Add this
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//Has to be unregistered always, otherwise nav controllers down the line will call this method
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
hope it helps.

Are you sure that 'addObserver', 'postNotification' and 'removeObserver' are always called on the main thread?
Please doublecheck with:
if ([NSThread isMainThread])

Related

How to reset the app after the midnight

I am working on Pedometer application via using CMPedometer Class. It works fine and I cross-checked the value via built in app- HealthKit. However, when I wake up this morning and I was still seeing the yesterday value.
Then I killed the app and reopened it again, then it showed me today value.
My question is how to handle this issue?
you can use applicationWillEnterForeground Delegate in your ViewController or Model ...
( if you want handle in specific VC or model you must register it ) or you can use this Delegate from your appDelegate
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateData) name:UIApplicationDidBecomeActiveNotification object:[UIApplication sharedApplication]]; // Dont forgot about removeObserve
-(void)updateData{
[[CMPedometer sharedInstance]update]; // update your data
// now you should update Your UI
[self.tableView reloadData];}

iOS Game Controller framework : How to use "startWirelessControllerDiscoveryWithCompletionHandler"

When game controller be paired from system setting, all is fine.
But I want to discover & pair game controller in my app.
Actually I found that it seems feasible by Apple's docs.
doc link : Discovering and Connecting to Controllers.
I have a game controller which is in pairing...
But I found the log of function "startWirelessControllerDiscoveryWithCompletionHandler" never be shown.
Seems the behavior does not conform.
I call "startWirelessControllerDiscoveryWithCompletionHandler" when app load...
I also call "stopWirelessControllerDiscovery", but still same.
- (void)viewDidLoad {
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(gameControllerDidConnect:) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(gameControllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil];
[GCController startWirelessControllerDiscoveryWithCompletionHandler:^{
NSLog(#"Finished finding controllers");
[self completionWirelessControllerDiscovery];
}];
...
}
- (void)completionWirelessControllerDiscovery {
if (isDebug) {
NSLog(#"%s-%d", __FUNCTION__, __LINE__);
}
}
Someone has experiences on this?
My experience has been that it does not work for pairing a controller - I have always had to pair my game controllers directly to the device. I've been developing a wrapper around GCController, so I've tested about 5 different MFi controllers with no luck getting GCController to manage the pairing process.

NSNotificationCenter callback while app in background

One question and one issue:
I have the following code:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:store ];
}
- (void) localCalendarStoreChanged
{
// This gets call when an event in store changes
// you have to go through the calendar to look for changes
[self getCalendarEvents];
}
These methods are in a class/object called CalendarEventReporter which contains the method getCalendarEvents (in the callback).
Two things:
1) If the app is in the background the callback does not run. Is there a way to make it do that?
2) When I bring the app back into the foreground (after having changed the calendar on the device) the app crashes without any error message in the debug window or on the device. My guess is that the CalendarEventReporter object that contains the callback is being garbage-collected. Is that possible? Any other thoughts on what might be causing the crash? Or how to see any error messages?
1) In order for the app to run in the background you should be using one of the modes mentioned in the "Background Execution and Multitasking section here:
uses location services
records or plays audio
provides VOIP
services
background refresh
connection to external devices
like through BLE
If you are not using any of the above, it is not possible to get asynchronous events in the background.
2) In order to see the crash logs/call stack place an exception breakpoint or look into the "Device Logs" section here: Window->Organizer->Devices->"Device Name" on left->Device Logs on Xcode.
To answer your first question, take a look at https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
What I did to get code running in the background is to do something like
In the .h file
UIBackgroundTaskIdentifier backgroundUploadTask;
In the .m file
-(void) functionYouWantToRunInTheBackground
{
self.backgroundUploadTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
//code to do something
}
-(void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUploadTask];
self.backgroundUploadTask = UIBackgroundTaskInvalid;
}
The code above I pretty much learned from objective c - Proper use of beginBackgroundTaskWithExpirationHandler
As for your second question, you should set a breakpoint where code is supposed to run when you bring the app back to the foreground. No one can figure out why an app crashes if not given enough code or information.
The solution to the second part of the question was to raise the scope of the object containing the callback code. I raised it to the level of the containing ViewController. This seems to work. I still can't figure out how to raise the Notification (i.e. execute the call back) if the notification comes while the app is in the background/suspended. This prevented the object containing the callback from being cleaned up.

iOS Google Analytics memory growing out of control FAST

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
Im working on XMPP Framwork and as soon i fixed GA crash. This one failed https://github.com/robbiehanson/XMPPFramework/blob/master/Extensions/CoreDataStorage/XMPPCoreDataStorage.m
ANy easy way to solve this issue.
https://github.com/robbiehanson/XMPPFramework/issues/577

How to avoid deallocated objects being accessed in callbacks/etc?

The issue has been discussed here and here, but I wonder if there is a more solid way to solve this whether you have delegates or not - when a function is called after a delay.
At a certain point in a program, at a button push, an object - a CCLayer - is created. That layer creates several objects, some of them at callbacks. That created object layer has a "back" button which destroys it. I am running into a problem when the callbacks, etc are triggered AFTER that object is destructed and try to access objects that don't exist anymore - where the "message sent to deallocated instance 0x258ba480" gives me this good news. How do I avoid that?
1) Is there a way to kill the callbacks (because I obviously don't need them anymore)
2) should/can I test for the existence of these possibly non-existent objects at the callbacks themselves
3) something else?
(My callback is code for checking for an internet connection that I copied from this illustrious website - may it live long and prosper-, using Reachability, and I could solve the problem by simply moving it to the main view and setting a flag on the child view, but I don't want to.)
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Yayyy, we have the interwebs!");
//I do the net stuff here
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Someone broke the internet :(");
noNetMessageLabel.visible=true; //<-------this goes kaboom
noNetFlag=true;
});
};
[internetReachableFoo startNotifier];
}
There are basically two ways to avoid deallocated delegates from being messaged:
Hold onto the objects you want to message later. That way they won’t get deallocated. This is the case with block callbacks – if a block references some object, the object gets retained until the block ceases to exist. If you message some objects from a block and hit a deallocated object, you must have screwed up the memory management somewhere.
Clear the delegation link before you release the delegate. Nowadays this is usually done using weak, zeroing properties that are automatically set to nil when the referenced object is deallocated. Very convenient. Not your case.
You might consider several options:
First, you may just check for existence of an object before passing message to it:
if (noNetMessageLabel)
noNetMessageLabel.visible = true;
But personally I consider that as a bad architecture.
More wise decision, from my point of view, would be move the code of displaying any alert regarding internet connectivity to the model.
Create method like this in AppDelegate or in the model:
- (NSError*)presentConnectivityAlert
{
if () //any error condition checking appropriate
[[NSNotificationCenter defaultCenter]
postNotificationName:#"connectivityAlert"
object:self
userInfo:userInfo];
}
Also you may consider moving internet checking code to the model too.
In the ViewControllers of your app implement listening to this notification.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveRemoteNotification:)
name:#"connectivityAlert"
object:nil];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:#"connectivityAlert"
object:nil];
}
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (self.isViewLoaded && self.view.window) {
//Present the user with alert
}
}
Thus you have more general and quite versatile approach to handle connectivity issues throughout all your application.
Is there a way to kill the callbacks
It's not possible to cancel block (in your case), but it's possible to cancel NSOperation in NSOperationQueue. But that will require to rewrite your implementation of Reachability.

Resources