UIViewController released without viewWillDisappear: being called? - ios

I have a basic implementation of UITableViewController that presents a UIDocument. A reference to the document is kept as instance variable of the view controller.
In viewWillDisappear: I close the document if the view controller is removed (and not a sub-viewcontroller pushed to the stack):
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (!self.keepDocumentOpenAfterNavigatingAway) {
[self.deckDocument closeWithCompletionHandler:^(BOOL success){
// As side effect, this retains 'self' until
// the document is closed:
self.document = nil;
}];
}
}
Now, once in a while I receive crash reports caused by this (app is for iOS 7):
objc_msgSend() selector name: _setInConflict:
I suspect that maybe the view controller was deallocated without viewWillDisappear: being called. Then the document would have been deallocated along with it without being closed. When the conflict comes in from iCloud, the app crashes.
Maybe this happens while the app is in the background and runs low on memory? Is there such a scenario where a view controller would get deallocated without viewWillDisappear: being called?
EDIT: To clarify, this only happens in production, I was not able to reproduce this myself. That's why I'm assuming it might be related to a low memory/background scenario. I issued low memory warnings in the simulator, without any luck.

Related

App crashes sporadically when dismissing a presented UIViewController

Over the last few months we are facing the following issue intermittently in the app we are developing.
The App: It's a fairly complex iOS app with a tabbar-based navigation developed in Swift. It has a good number of pushes and presentations.
The issue: We sporadically encounter app crashes when presented view controllers are dismissed. These crashes are not reproducible, and you cannot find a scenario where this will definitely happen. The app does not crash indicating a line in our code. It is observed in various versions of both iOS 8 and iOS 9 devices. We never get any log pertaining to the crash.
As seen in the images, there's not much to go on with. But, on constant observation of crashes over the months, we've noticed something on the Thread 1 in the left bar of the Xcode window. The stack leading to the crash will always end with [UINavigationController viewWillAppear:]
Also, as seen in the stack is a line indicating '_CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER...' which made us suspect that an NSNotification selector is being called on a deallocated class. For this, we made sure that we removed all NSNotification observers in all classes before deallocations and also had logs in deinit methods of every class and made sure they were deallocating when required. This still didn't solve the problem.
Can somebody please help us with this issue?
Are you using a delegate pattern? If you are, then the delegate properties should always be declared as weak. I am guessing you have a strong reference to a delegate property somewhere and a controller that uses notifications isn't getting deallocated.

"Application windows are expected to have a root view controller" message when adding view immediately after launch, iOS 9 only

My app sends a request at startup and displays a brief message to users when it succeeds, by means of MTStatusBarOverlay. Unfortunately, my current implementation seems to run afoul of iOS 9's view life cycle paradigms. I get the message
Application windows are expected to have a root view controller at the end of application launch
and the app crashes. The app works fine on iOS 7 & 8.
From searching around online, it seems like this might occur when trying to add the message view to the view hierarchy before the root view controller is established for the app's UIWindow, but that doesn't seem to be the case here, see below.
Here is an excerpt of the UIApplicationDelegate implementation:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[...]
self.window.rootViewController = [[MyViewController alloc] init];
[...]
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[MyDataManager sendRequestWithCompletion:^{
// Displays a message with MTStatusBarOverlay
[self showSuccessOverlay];
}];
}
application:didFinishLaunchingWithOptions: is called before applicationDidBecomeActive: so it seems like there should never be an issue with rootViewController being established.
Why is this happening? What's different about iOS 9 that's causing the app to break?
MTStatusBarOverlay is a subclass of UIWindow, so instantiating one during app launch adds that UIWindow to the list that iOS checks for a populated rootViewController when launch completes.
I was able to work around the issue by instantiating and assigning a dummy controller before using the overlay, like so:
[MTStatusBarOverlay sharedInstance].rootViewController = [UIViewController new];
[[MTStatusBarOverlay sharedInstance] postMessage:#"Message"];

How to present iOS Keyboard extension using presentViewController

My goal is to present the iOS Keyboard with the method presentViewController:animated:completion whenever the keyboard has successfully instantiated, taking advantage of the smooth upward animation.
Some very brief background: the keyboard extension project is written in Objective-C, with KeyboardViewController.h/.m handling the logic and some basic view layout from a inputView.xib.
Based on this SO question, several of the the answers suggested using the approach of calling presentViewController from [UIApplication sharedApplication].keyWindow.rootViewController. The problem is, as this is an iOS extension, when I try to replicate this method, I get the error that sharedApplication is not available. I was wondering if a workaround exists where I could present the Keyboard with the method presentViewController, either somehow via itself or via a super? In my current attempts, calling [self presentViewController: self...] causes an exception.
Much appreciated!
It can't be done.
One can actually acquire a reference to [UIApplication sharedApplication] easily, the most straightforward (though liable to get you rejected in App Store review) would be:
Class appClass = NSClassFromString(#"UIApplication");
id app = [appClass performSelector:#selector(sharedApplication)];
Running on the iPhone 5 simulator, [app keyWindow] returns nil when called from the principal input view controller's viewDidAppear: method.
[UIApplication windows], however, does not, so we could try
[[[app windows].firstObject rootViewController] presentViewController:self
animated:YES
completion:nil];
...but it just throws an exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present
modally an active controller .'
Walking the view hierarchy starting from UIApplication windows] reveals an interesting fact: the window of our UIInputViewController isn't in this window hierarchy. self.view.window returns a separate _UIHostedWindow with no superView.
Furthermore, There is no variation across apps of the addresses in memory of windows returned from [UIApplication sharedApplication].
Conclusion: the UIApplication we get from sharedApplication is the one for our process, not for the host process. Presenting on views and windows it owns therefore is pointless, because its views aren't visible to the user.

Xcode 4.5 - App crashes from excessive memory usage, works fine on 4.3

I have an app that transitions from view controller to view controller using the code below:
[self addChildViewController:self.aNewViewController];
[self transitionFromViewController:self.currentViewController
toViewController:self.aNewViewController
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromBottom
animations:nil
completion:^(BOOL finished) {
[self.aNewViewController didMoveToParentViewController:self];
[self.currentViewController removeFromParentViewController];
self.currentViewController=self.aNewViewController;
}];
When I run it using Xcode 4.3 on an iOS 5.0 device, it runs very smoothly and uses about 30 - 50 mb. When i run it using Xcode 4.5 on any device it crashes as the app jumps from 30 - 70 - 100 - 130 mb each time I change View Controllers. It appears that the memory is not being released each time I leave a View Controller. I am using ARC.
Thanks in advance for any help you can provide.
Add a log message in dealloc() in each view controller involved here with some unique string and see if ANY VC involved in the transition is getting released. The one or ones not getting released are obviously the problem. Things to look for are retain cycles - ivars/properties of them that take a delegate or similar parameter that retains it. For example NSTimer retains the object it messages.
If you can find this, the solution is to code a new method in your VCs that prepares for release by insuring those objects are modified so as to not retain their owner (for a NSTimer, invalidate it then nil).
You can send this new message in the transitions completion block.

How can I get an EXC_BAD_ACCESS in a UIViewController's didReceiveMemoryWarning?

I'm implementing didReceiveMemoryWarning in a subclass of UIViewController. My code looks something like the following:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
[self cleanUp];
}
When my app actually received a memory warning, the app crashed with an EXC_BAD_ACCESS on the [self cleanUp] line (a method that does exist). How could this happen? As I understand it, the framework called the didReceiveMemoryWarning method, then released my class before it attempted to execute [self cleanUp]. Why would this happen? How can I prevent this?
Are you doing anything unusual in your view controller? What is the value of self when the crash occurs? Does it happen in both debug (optimization off) and release builds?
Try running with NSZombieEnabled. If this is a problem with unbalanced retain/release, that should help you find it.
The crash was actually happening inside the -(void)cleanUp method, though Xcode was pointing to the line that called [self cleanUp]. Inside the -(void)cleanUp the code was accessing elements in an array that were already released, hence the EXC_BAD_ACCESS. Thanks to all for the helpful suggestions.
Wild guess: call [self cleanup] first, then call super.
You can also simulate memory warnings in the sim, if that helps.

Resources