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.
Related
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.
Hello I have a memory issue in my iPad app. Each time I change from a view to another view (this transition is made with segues), the app is increasing the memory used and never releases the memory. It is always increasing the memory used.
Let's see an example:
I am in my first view "home" which has these lines in viewDidLoad and viewDidAppear
(void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
[self initializeHomeDataSources];
DateService* dateService = [[DateService alloc] init];
self.currentDate = [dateService today];
[self checkHomeStatus];
[self showEmptyHomeViews];
[self setUpFonts];
}
and this my view did appear method
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
_homeAutomaticUpdate = YES;
//This is a Thread
[NSThread detachNewThreadSelector:#selector(automaticHome) toTarget:self withObject:nil];
[self.phrasesView startPhrasesThread];
if ([InternetService internetConnection]) {
[self synchronizeHome];
}
if (self.scheduleDataSource.currentEvent) {
[self loadMessagesFor:self.homeDataSource.currentEvent];
[self loadLibraryFor:self.homeDataSource.currentEvent];
} else {
[self loadLibrary];
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
_homeAutomaticUpdate = NO;
}
All the IBOutlet's are defined as (nonatomic, strong).
Each time the HomeView is loaded the memory increases it's quantity and I don't know what is happening.
Can anybody help me here? This problem is causing me consternation.
I'm guessing that you're going "backwards" to previous controllers using segues. Is that true? If so, that's your problem -- unless you use an unwind segue, you should never go backwards using segues because they always instantiate new controllers. So, when going back to previous controllers, either use an unwind, or use code that reverses your forward segues -- that is, popViewControllerAnimated: if the forward segue was a push, and dismissViewControllerAnimated:completion: if the segue was a modal.
Few questions:
Is your app killed after a while, because of memory usage?
Why you are creating new thread in -viewDidAppear?
Have you tried to simulate memory warning?
(In simulator: Hardware -> Simulate Memory Warning or Shift + CMD + M)
Does the memory gets down after memory warning or not?
This is not a whole answer for your question but your outlets must be weak unless their not top level objects.
All the IBOutlet's used should be (nonatomic, weak). Try this out..
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'm displaying a modal view called "rule" from a round rect button. In that "rule" modal view i'm displaying another modal view called "newRule" when user clicks the Create Rule button.
When i'm quitting from the "newRule" modal view the app crashes. Here's the code i had written for quitting the "newRule" modal view.
[self dismissModalViewControllerAnimated:YES];
Nothing is displayed in the console. When i tried to debug the code, it displayed a EXC_BAD_ACCESS after the dealloc method. My dealloc method looks like this:
[label release];
label = nil;
[imageArray release];
imageArray = nil;
[languageElementsArray release];
languageElementsArray = nil;
[super dealloc];
Please help me.
Is the label a UILabel object? Also what are in the arrays? Views are automatically released once their superview is released, so releasing a subview after its superview has been released (or releasing the subview then the superview) will cause an crash similar to the one you describe
I'm experiencing something similar. When I comment out the last line ( [super dealloc] ), it then works. Does this make a difference for you?
If you happen to be using Automatic Reference Counting in Xcode 4.2, then you should not have a [super dealloc] at all—which would result in this error.
Of course, in that context you likely should not be releasing these other objects either.
So I'm having problems releasing some view controllers.
In essence the dealloc for the PhotoPostViewController never seems to get called, so I can't clear down the images contained within that are munching all the memory.
This is my UIViewController subclass, I can have up to 100 of these at any one time added as subviews to the main scroll view, the iPad gets tight for memory after that.
#interface PhotoPostViewController : UIViewController {
IBOutlet UIImageView *backgroundImage;
IBOutlet UIImageView *serviceImage;
}
Then in my main view class I have a method to create these views and add them to a scrollView. This method is typically called from a loop to create all the subviews I need.
- (void) addPost {
PhotoPostViewController *postView = [[PhotoPostViewController alloc] initWithNibName:#"PhotoPostViewController" bundle:nil];
[scrollView addSubview:[postView view]];
[viewControllers addObject:postView];
}
viewControllers is an NSMutableArray created in the main class init.
scrollView is a UIScrollView on my main view.
This all works fine, I know the limit of the memory usage on the iPad and keep within that at any given time, opening Popovers to give preview images and videos etc...
Doesn't run out of memory until I try to refresh the screen.
The code to do this is:
- (IBAction)didPressRefresh:(id)sender {
for(UIView *subview in [scrollView subviews]) {
[subview removeFromSuperview];
}
for(UIViewController *c in viewControllers) {
[c release];
}
[viewControllers removeAllObjects];
}
For the sake of simplicity I clear off all the subviews and try to release them before recreating the next set of subviews using the function above.
It removes them from the view, but runs out of memory adding the new set of view controllers. In my test cases the sets of view controllers are identical in content, so if it loads from clean first time, then it should load the second time and every other time after that if I release everything properly.
What actually happens is it crashes due to low memory when creating the second set of view controllers.
While debugging I've put breakpoints on the 'viewDidUnload' and 'dealloc' methods, but they never get hit.
It looks like the UIViewController itself is getting released, yet the UIImageViews within are not, clearly they'd usually get released by my code in the dealloc (or viewDidUnload) method.
So I'm confused.
Counting things it looks to me like the reference counts are fine. so how come the dealloc is not getting hit ?
Andi
You need to send the postView object the -release message after adding it to the viewControllers collection:
- (void) addPost {
PhotoPostViewController *postView = [[PhotoPostViewController alloc] initWithNibName:#"PhotoPostViewController" bundle:nil];
[scrollView addSubview:[postView view]];
[viewControllers addObject:postView];
[postView release];
}
The reason why you need to do this is because the collection sends the -retain message to all objects that are added to it, hence the memory leak and -dealloc not being hit.
EDIT:
Your -didPressRefresh: method should look like this:
- (IBAction)didPressRefresh:(id)sender {
[[scrollView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
[viewControllers removeAllObjects];
}