My application crashes when simulating Memory warning in simulator with error:
[UINavigationController retain]: message sent to deallocated instance
I'm using ARC.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_window = window;
[self startWithFlash];
return YES;
}
- (void)startWithFlash
{
[self.window.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
__weak typeof (self) weakSelf = self;
WPRSplashViewController *splashViewController = [[WPRSplashViewController alloc] initWithNibName:#"WPRSplashView" bundle:nil doneCallback:^{
[weakSelf startApplication];
}];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:splashViewController];
[self.window makeKeyAndVisible];
}
- (void)startApplication
{
WPRMainViewController *mainViewController = [[WPRMainViewController alloc] init];
UINavigationController * controller = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.menuController = [[PHMenuViewController alloc] initWithRootViewController:controller
atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
self.window.rootViewController = self.menuController;
[self.window makeKeyAndVisible];
}
This happens when somewhere in the app I call:
[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];
And right after that simulating memory warning.
Running Profile Tools with NSZombie enabled I get the following trace:
This is not the only place with such crash. In every place where I use UINavigationController as wrapper for view controller and I present it as modal view, after simulating memory warning I get this crash.
I had very similar issue in other place for which I've posted here another question but did not find a reasonable solution: Similar issue
Finally after days of investigations I've found a reason to all these crashes including the one described in "iOS memory warning sent to deallocated UIViewController"
The problem came out from PHAirViewController project. I still did not find out a real reason, but simply commenting out - (void)dealloc function in PHAirViewController.m file did the magic.
The main headache I got when was running Instruments to detect NSZombies. All traces were pointing to system classes like UINavigationController, UIImagePickerViewController etc... Due to this I started disabling ARC for parent controllers. In some places it helped but in some it didn't.
After a lot of voodoo I found out that every class (including system classes) was implementing UIViewCOntroller(PHAirViewController) Category and though - (void)dealloc function was always called on dismissing them.
Now the only thing left is to understand why this function is generating NSZombies. The interesting thing is that just commenting its content (which have only one line: self.phSwipeHandler = nil) does not have the same effect.
Quickfix: insert assert([NSThread isMainThread]); to various places in your code where you access appDelegate.window.rootViewController. This should be done for write- and for read-accesses to the property! This will reveal the culprit. appDelegate.window.rootViewController must not be accessed from any other thread than the main thread.
Generally, there are these reasons why this may happen:
You are using __unsafe_unretained variables.
You are using an unsafe_unretained property.
You are using non-ARC
You are accessing the same variable from different threads at the same time
You are accessing the same nonatomic, non-weak property from different threads at the same time
The fix for 1 and 2 is simple: Just don't use unsafe_unretained anymore.
The fix for 3 is: use ARC instead.
The fix for 4 and 5: use atomic properties instead, or synchronize access to your iVars. (Note that you must not access iVars from atomic properties directly as this breaks the atomicity.) Alternatively, use the property only from one thread, e.g. only from the main thread.
In your example, I assume that issue #5 applies. The culprit should be some non-main-thread accessing rootViewController from UIWindow.
It is likely you are using an assign or __unsafe_unretained property somewhere in your code. Delegates should always be of type weak, so that the reference to the delegate object is nil'ed out on deallocation.
Also, calling:
[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];
... from within another class in your app is a bit of a smell. One that I've had many times. It means you have circular dependencies. Your app delegate is dependent on the class using this code (transitively, if not directly), and this class is dependent on your app delegate. Looking at your Instruments trace, it looks like you have adopted the delegate pattern else where, so you have some experience with decoupling. I would suggest bubbling that message up through a delegate chain, notification, or block.
Related
This is used to work fine for my pre-ARC code, but since refactoring all the project to be ARC compatible, I start getting this crash:
[CustomViewController respondsToSelector:]: message sent to deallocated instance
My project is an iPad app with a split view, but contrary to apple documentation, previous developer has put another view controller to be visible on app launch before split view. So I know this is not the right way to do, but as I said it used to work before ARC integration so I need to get a workaround with this.
The root view controller which contain a menu of items, each item display a detail form to be filled, then a click on next button move to the next detail screen, etc.
The issue starts when I click on home button put on root view to get back to the home view, here is the relevant code that move the user to the home screen:
//this method is in the appdelegate, and it gets called when clikc on home button located on the root view
- (void) showHome
{
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
Then when I click on a button to get back to the split view (where are the root/details view), the app crashes with the above description. I profiled the app with instruments and the line of code responsible of that is located in the RootViewController, in the didSelectRowAtIndexPath method, here is the relevant code:
if(indexPath.row == MenuCustomViewController){
self.customVC=[[CustomViewController alloc] initWithNibName:#"CustomVC"
bundle:nil];
[viewControllerArray addObject:self.customVC];
self.appDelegate.splitViewController.delegate = self.customVC;
}
customVC is a strong property, I tried to allocate directly and assign to the instance variable but that didn't help to fix the crash. Any thoughts ?
EDIT:
Here is the stack trace given by instruments:
[self.appDelegate.splitViewController setViewControllers:viewControllerArray];//this line caused the crash
[viewControllerArray addObject:self.appDescVC];//this statement is called before the above one
self.custinfoVC=[[CustInfoViewController alloc] initWithNibName:#"CustInfo" bundle:nil];//this statement is called before the above one
self.appDelegate.splitViewController.delegate = self.appDescVC;//this statement is called before the above one
custinfoVC and appDescVC are strong properties.
I solved this problem by setting my delegates and datasources to nil in the dealloc method. Not sure if it'll help you but its worth a try.
- (void)dealloc
{
homeController.delegate = nil;
//if you have any table views these would also need to be set to nil
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
You may want to setup the CustomViewController during app launch, and display the other views modally on top if necessary. The error message you're getting is because something is getting released by ARC prematurely. It might have not manifested before because pre-arc stuff wasn't always deallocated immediately. ARC is pretty serious about releasing stuff when it leaves scope
Hard to tell without seeing a lot more of the code involved, and what line it breaks on, etc.
This:
- (void) showHome {
//THIS: where is homeController allocated?
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
EDIT:
Add this line right above the line that causes your crash
for (id object in viewControllerArray) {
NSLog(#"Object: %#",object);
}
I had the same Problem.If you are not using "dealloc" method then use "viewWillDisappear" to set nil.
It was difficult to find which delegate cause issue, because it does not indicate any line or code statement for my App So I have try some way to identify delegate, Maybe it becomes helpful to you.
1.Open xib file and from file's owner, Select "show the connections inspector" right hand side menu. Delegates are listed, set them to nil which are suspected.
(Same as my case)Property Object like Textfield can create issue, So set its delegate to nil.
-(void) viewWillDisappear:(BOOL) animated{
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]){
self.countryTextField.delegate = nil;
self.stateTextField.delegate = nil;
}
}
I haven't done any iOS development since iOS 3, so my memory is a little hazy (though memory management was never anything I struggled with and my mind is quite clear on that).
I'm starting a new project and don't understand why the skeleton code is structured the way it is:
- (void)dealloc
{
[_window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]]
autorelease];
// ... snip ...
}
Why would the window object be autoreleased? I'm pretty sure it never used to be this way in older iOS versions.
Where does _window come from? Is this just another way to access [self window]?
I'd have written this as:
- (void)dealloc
{
[self.window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]];
// ... snip ...
}
It was always drummed into me never to release an autoreleased object, and in fact doing so usually results in a segmentation fault.
In you second example you are leaking the window object since, the alloc will give the object a retain count of 1, the you assign it _window via the property which will also retain the object assigned to it.
It's true you should not release an autorelease object, but in the dealloc you are releasing the iVar for the window property. You should always release any property that is declared as either retain or strong. (although not when using ARC).
The _window is now automatically generated as the iVar for the property window.
There some believes that you should not use self. properties in init or dealloc.
Thus the way I do it is:
[_window release], _window = nil;
This wil set the _window to nil after releasing it, making sure that if any other thread might want to use this it will be calling on nil. Which could prevent a crash but could alo create some weird behavior. This is totally up to you.
You should move to ARC, which is a complier option to add release/autolease at compiletime fro you. There is no need to added these your self if you set the property correctly when using ARC.
So I figured this out (mostly). The #property declaration has the strong attribute. Apparently this is new to ARC (which I'm not actually using) and is otherwise just an alias for retain, so self.window = [ ... ] retains the object, hence the autorelease.
Still not clear on the _window variable, but I assume it is just a shortcut.
Check what the window property memory descriptor is like. I assume it's strong/retain in which case when you set the window property, it's value is retained thus needs to be released in dealloc.
Following your code path.
[UIWindow alloc] = retainCount of 1.
autoreleasing = retainCount of 0
setting the self.window bumps the retainCount which now = 1 and is alive until...
in dealloc you release it, thus retainCount = 0 and the object is deleted
You probably missed out that in later iOS SDKs, auto synthesized properties automatically create instance variables with underscore prefixes. So you could also do self.window = nil in your dealloc.
1. Why would the window object be autoreleased? I'm pretty sure it never used to be this way in older iOS versions.
If window object is not autoreleased, then it results in leak. Since you are using
self.window = [[[UIWindow alloc] ....]autorelease];
self. syntax allows setter function to get called, which retains the object once. So the actual window object which we alloced using [UIWindow alloc] should be released, hence autorelease. The retain using self. syntax is conceptually released in dealloc. So for one alloc plus one retain we are releasing twice.
Where does _window come from? Is this just another way to access
[self window].
Well this question is already discussed here
As mentioned before - I'm an Objective-C newbie of the first order but having read 4 physical books on the subject and a bucket-load of ebooks and documentation I still can't find what I'm looking for.
I have a top-level content view controller that wants to configure its view property from the physical dimensions of the window property of the application delegate. This is something that several people have already asked questions on. ([UIScreen mainScreen] doesn't cut it for reasons already aired many times before on this forum). Therefore, the logical approach would be for the content view controller to read the frame of the application delegate's window. Now, the only answer I've found that comes close to this is to use [[[UIApplication sharedApplication] window] frame] - however, this only works once the window property has been made keyAndVisible. The content view controller needs to read the app delegate's window property before it gets to makeKeyAndVisible. The code goes in this order....
App Delegate:
- (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {
// This next line is a test window frame for R&D purposes....
[self setWindow: [[UIWindow alloc] initWithFrame: CGRectMake(0.0f, 20.0f, 320.0f, 320.0f)]];
if ([self window]) {
contentViewController = [[ContentViewControl alloc] initWithNibName: nil bundle: nil]; // Content view controller instantiated here
if (contentViewController) {
[[self window] addSubview: [contentViewController view]];
[[self window] layoutSubviews];
[[self window] makeKeyAndVisible]; // Window made key and visible here
return YES;
}
}
return NO;
}
In my content view controller's initWithNibName: nil bundle: nil method I have the following test code...
- (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil {
self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
if (self) {
NSLog(#"%#", NSStringFromCGRect([[[UIApplication sharedApplication] keyWindow] frame]));
// This does not work.
}
return self;
}
This does not work due to the fact that App Delegate's window is not yet key and visible.
So, my question is thus; What is the name of my App Delegate Class' instance? I know the App Delegate's Class name defaults to myApplicationNameAppDelegate but I'm after the instance name. I want to replace the call to [[UIApplication sharedApplication] keyWindow] by something like;
[myAppDelegatesInstanceName window].
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
Like I said, I'm a total noob to all of this and this is probably another dumb noobie question but it's one that nobody yet appears to have answered in a simple way.
(Procedurally - my home turf - there are a plethora of ways to get the window stuff down into other levels of scope ranging from making the window globally accessible to the whole program suite to passing it down as a specific parameter through the various function hierarchies - but this Objective stuff seems to depart from established, procedural practice).
If anyone can help, I'd be really grateful. This stuff is definitely not intuitive!
V.V.
You can access the app delegate through the singleton UIApplication instance:
[[[UIApplication sharedApplication] delegate] window];
This is a special case, though, because you can access an object (the UIApplication instance) whose delegate you know is the object you want to access. The general case:
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
You need to pass a reference to the target object to the object from which you want to access it. This is sometimes necessary but it also means that you introduce a tight coupling between these two objects, which you generally want to avoid if you can. So think about each case and see if there are other possible designs.
Swift 3
if let window = NSApplication.shared().windows.first {
// access window. properties now
}
Use this for Swift:
let window:UIWindow? = (UIApplication.sharedApplication().delegate?.window)!
Every time I turn the page in my app, I am removing and releasing the previous viewController - but for some reason it is still in memory. I know this, because after using the app for a while, I get 47 memory warnings - one from each view controller - if I had opened 47 pages before the memory warning occurred. I get 60 memory warnings if I had opened 60 pages before the memory warning occurred. And so on...
This is the code that runs from page to page:
UIViewController *nextController;
Class nextClass = [pageClasses objectAtIndex:(currentPageIndex - 1)];
nextController = [[nextClass alloc] initWithNibName:[pageNibs objectAtIndex:(currentPageIndex - 1)] bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
[currentPageController.view removeFromSuperview];
[self.view addSubview:nextController.view];
[currentPageController release];
currentPageController = nextController;
[currentPageController retain];
[nextController release];
Can anybody point to any issues they see?
Thanks so much!
As as aside, make sure you also nil any outlets your viewController has in viewDidUnload and generally do the opposite of any corresponding code in viewDidLoad. I see a lot of iOS code which doesn't do this and it stops the runtime properly unloading view controllers and associated views.
Have you played with the Behavior and Memory sections of the Window Attributes panel in IB's Inspector? This is where you usually control memory use and release stuff (outside of the code itself).
Try changing values for the view object in question, as well as on the window (or whatever it is for iPhone).
I believe it's because you're calling retain on currentPageController. I just recently asked a similar question and got a ton of clarification on memory management.
EDIT: What if you did something like:
[currentPageController.view removeFromSuperview];
[currentPageController release];
Class nextClass = [pageClasses objectAtIndex:(currentPageIndex - 1)];
currentPageController = [[nextClass alloc] initWithNibName:[pageNibs objectAtIndex:(currentPageIndex - 1)] bundle:nil];
[currentPageController performSelector:#selector(setDelegate:) withObject:self];
[self.view addSubview:currentPageController.view];
It cleans up the code a bit and won't leak memory.
I was wondering how the autorelese works on the iPhone. I though that once you send an autorelease to an object it is guaranteed to be retained in till the end of the scope of the block the autorelease was sent. Is that correct?
I was initializing a view from a NIB in the applicationDidFinishLaunching like below:
(void)applicationDidFinishLaunching:(UIApplication *)application {
loginViewController = [[[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil] autorelease];
[window addSubview: [loginViewController view]];
[window makeKeyAndVisible];
}
and the view did not show at all, all there was on the screen was the UIWindow
Now once I removed the autorelease from the end of the controller initialization all went smooth from there on.
What is this about?
Cheers,
K.
When you call autorelease, you give ownership of the object to the current autorelease pool. The run loop creates a new autorelease pool before it dispatches an event (such as applicationDidFinishLaunching:) and destroys that pool when the event finishes.
When you give ownership of your LoginViewController to the autorelease pool, it gets released just after the applicationDidFinishLaunching: returns. When the view controller deallocates itself, it removes its view from the superview (your window in this case).
Your application delegate should keep ownership of the LoginViewController and release it in the app delegate's dealloc method (or when you're done with your login and have moved on to another view).
To expand on Don's answer, it may be somewhat confusing to say "you give ownership of the object to the current autorelease pool." This might be misunderstood to mean the object is guaranteed to be destroyed when the autorelease pool is drained. This is not correct (though it will happen in this case). Sending -autorelease requests that the autorelease pool send a -release message when it is drained. If that -release message makes retainCount = 0, then the object will be destroyed.
So, in order to do what Don is recommending, you need to create a ivar to keep track of this view controller. His explanation of why the view vanishes is exactly right; but you don't want to just leak the view controller. You want to hold onto it, and release it when you're done with it.
#interface ... {
LoginViewController *_loginViewController;
}
#property (readwrite, retain) LoginViewController *loginViewController;
#implementation ...
#synthesize loginViewController = _loginViewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
self.loginViewController = [[[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil] autorelease];
[window addSubview: [loginViewController view]];
[window makeKeyAndVisible];
}
- (void)dealloc {
[_loginViewController release]; _loginViewController = nil;
[super dealloc];
}
The autoreleasepool is cleaned at the end of the runloop. This means as long as you invoke methods and do stuff, it's still there.
I don't see the error in your code, but the Window is retained properly in your example.
Since you're adding your LoginViewController to the autorelease pool it's being released at the end of the run loop. When that happens it also releases its' view and removes it from being displayed.