I use UINavigationController in my program,when I situated on forth or fifth controller in navigation stack,program recieve memory warning and I can't go backwards. I press back button, navigation bar animation is taking place,but controller isn't being popped,I still see old view. Can anyone help me handle this problem?
My subviews has property:
#property (nonatomic,retain) UITableView *searchTableView;
#property (nonatomic,retain) UISegmentedControl *categorySegmentedControl;
#property (nonatomic,retain) UISearchBar *searchTableBar;
#property (nonatomic,retain) UIView *footerView;
#property (nonatomic,retain) UINavigationItem *navigationItem;
My viewDidUnload method
- (void)viewDidUnload
{
[super viewDidUnload];
self.searchTableView = nil;
self.categorySegmentedControl = nil;
self.searchTableBar = nil;
self.navigationItem = nil;
self.footerView = nil;
}
My dealloc
-(void)dealloc
{
[super dealloc];
[searchTableView release];
[categorySegmentedControl release];
[searchTableBar release];
[navigationItem release];
[footerView release];
[currentValues release];
}
And I had noticed strange regularity - this bug appears only when I move from view without tab bar to view with tab bar.
It sounds like you may be doing some setup in your controller's viewDidLoad and/or releasing stuff in viewDidUnload that should be done in initWithNibName:bundle and dealloc instead.
viewDidLoad and viewDidUnload are not called when your controller is created and destroyed, they are called when the view inside your controller is created and destroyed, and this can happen at any time, especially if you receive a memory warning when the view controller is in the background (e.g. not the top view in a navigation controller).
Make sure that you write your controllers in such a way that if the viewDidUnload gets called, or if the viewDidLoad gets called multiple times, you won't lose data or break anything.
Sorry if this answer is a bit vague, but if you post the code for your view controllers, I can probably give you more specific advice.
Related
I have 2 view, named viewcontroller1,viewcontroller2. every viewcontroller has load about 3 pictures(load it in XIB) , when i try to use [self.navigationcontroller pushviewcontroller], to push view1 to view2, the memory cannt release.(i want to konw it cannt release viewcontrller1 or cannt release uiimageview)any error for my code ??
viewcontroller1 code just like this:
viewcontroller1.h
#interface ViewController : UIViewController
#property(strong,nonatomic)IBOutlet UIImageView *mainImageView;
#property(strong,nonatomic)IBOutlet UIImageView *topwallImageView;
#property(strong,nonatomic)IBOutlet UIImageView *buttonImageView;
viewcontroller1.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
self.mainImageView = nil;
self.topwallImageView = nil;
self.buttonImageView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
-(void)dealloc
{
[mainImageView release];
[topwallImageView release];
[buttonImageView release];
[super dealloc];
}
-(IBAction)main2ViewController:(id)sender
{
Main2ViewController *main2ViewController =[[Main2ViewController alloc]initWithNibName:#"Main2ViewController" bundle:nil];
[self.navigationController pushViewController:main2ViewController animated:NO];
[main2ViewController release];
}
You have to study more detailed about Object Communication and Memory management in iOS.
Also please try to use ARC. Automatic reference counting (ARC) was introduced in the iOS 5 sdk to free Objective-C programmers from having to handle memory management by making memory management the job of the compiler.
You can convert your project to ARC enabled using this link
You can't release viewcontroller1 and its IBOutlet properties while viewcontroller2 is visible or after pushed viewcontroller2. Because you pushed viewcontroller2 from viewcontroller1.viewcontroller1 is the container/parent of viewcontroller2. viewcontroller2 will not exist with out viewcontroller1
About the IBOutlet imageview images, you can set it to nil .Eg: yourImageview.image = nil; However it will not release the IBOoutlet Imageview memory.
IBOutlet imageview will get released when a release method called to viewcontroller1 because you released it properly in viewcontroller1 dealloc method :)
About your Main2ViewController method:You handled memory properly in your code
When you call the pushViewController , it will retain the controller which is pushed by default. note that your are not the owner. It will release automatically when the controller is popped out.However you have to release the allocated Main2ViewController. So your code is fine :)
I have a view controller that was presented using
[self presentModalViewController:myVC animated:YES];
this VC has several declared retained properties (#property) that I have to release on its dealloc.
The variables are declared as
#property (nonatomic,retain) myClass1 *myProperty;
#property (nonatomic,retain) myClass2 *myProperty2;
// etc... and then synthesized on .m
The problem is that when I dismiss the viewController using
[self dismissModalViewControllerAnimated:YES];
it crashes on the dealloc, when releasing the retained properties I have declared, with the error *modifying layer that is being finalized *
Apparently the the viewController is gone at the time its own dealloc runs and then it crashes.
How do I solve that? Thanks in advance.
edit
the code that presents the viewController is on the rootViewController and is this:
UIViewController *myVC = [[UIViewController alloc] init];
myVC.delegate = self;
UINavigationController *navigator = [[UINavigationController alloc] initWithRootViewController:myVC];
[self presentModalViewController:navigator animated:YES];
[navigator release];
[myVC release];
and this is the what the dealloc code on myVC contains
- (void) dealloc {
[myProperty1 release]; // see this properties at the beginning of this question
[myProperty1 release]; // if I comment these 2 relesases it stops crashing
[super dealloc];
}
myVC is dismissed from inside itself, but that's fine according to the docs. I have also tried to dismiss it from the rootviewController but it continues to crash. The only way to stop crashing is to disable the release lines on the dealloc.
How do you set the properties?
self.myProperty =
? My guess is that you do not retain them. Are you doing
myProperty = ...
by any chance, with neither a retain or self.? The setter you synthesize needs a chance to actually retain your newly created object...
So in full it should read something like this:
MyClass1 *aProperty = [[MyClass1 alloc] init];
self.myProperty1 = aProperty;
[aProperty release];
...i asked where you allocate and init "myProperty1" and "myProperty2"...
i'm afraid that you make confusion thinking that this:
#property (nonatomic,retain) myClass1 *myProperty;
#property (nonatomic,retain) myClass2 *myProperty2;
need that you release myProperty and myProperty2
well, you are wrong!
you are just declaring the kind of objects you are going to use, not allocating them
you need to release them just if you alloc them someWhere...
I am new to iOS programming and I have read some useful articles on the release and allocation of memory and thought that i understand the concept. But during the actual coding, I can't seem to really apply the theory. I am really confuse over the fundamentals of iOS programming and hope that someone can help out. Thanks in advance!
1st behaviour - Some of the apps i see can maintain the current windows state when the iPhone home button is pressed so that when the app is launch next time, it will display the last state it is in.
2nd behaviour - Some other apps will behave like its a new startup everytime it is launch. Previously displayed text, images...etc will be cleared and it will always start at the first page.
What I wanted to do is like the 2nd behaviour - clear everything when the home button is pressed and start afresh everytime it is launch.
I have created a tab bar project. The codes below will result in the 1st behaviour.
I have tried releasing all the tab controllers at applicationDidEnterBackground instead at dealloc but it didn't work. It will still display the last screen.
My questions...
1) If i call release, shouldn't it destroy the window as well? eg. [NavController1 release]. It seems that the window can still work as usual...
2) What should I change to result in the 2nd behaviour?
3) If i assign tabBarController.viewControllers = nil, does it mean that the memory for the pages attached to it previously will be release?
4) Do i need to release IBOutlets variables? eg. UIWindow *window, UITabBarController, UITextField...etc. It didn't seem to give me memory leak even if i don't release them. The reason of this might be because I don't know how to kill the app. I tried to do the double click on the iphone home button to end the app but i still not able to get the debug to reach the dealloc function. An article says that if you alloc or retain it, you should release it. Since i'm not allocating anything here, can i not release it? Or is it possible to set autorelease?
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
UINavigationController *NavController1;
UINavigationController *NavController2;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NavController1 = [[UINavigationController alloc] init];
NavController2 = [[UINavigationController alloc] init];
//This view controller inherits UIViewController and has a xib
FirstViewController *firstpage = [[FirstViewController alloc] init];
firstpage.title = #"First Page";
//These view controllers inherits UIViewController and delegate of UINavigationControllerDelegate and has a xib
SecondViewController *secondpage = [[SecondViewController alloc] init];
secondpage.title = #"Second Page";
[NavController1 pushViewController:secondpage animated:YES];
[secondpage release];
ThirdViewController *thirdpage = [[ThirdViewController alloc] init];
thirdpage.title = #"Third Page";
[NavController2 pushViewController:thirdpage animated:YES];
[thirdpage release];
// Add them as children of the tab bar controller
tabBarController.viewControllers = [NSArray arrayWithObjects:firstpage, NavController1, NavController2, nil];
// Don't forget memory management
[firstpage release];
// Add the tab bar controller's view to the window and display.
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)dealloc {
[NavController1 release];
[NavController2 release];
[tabBarController release];
[window release];
[super dealloc];
}
1) As long as the retain count is 1 your view controller will be released by calling release.
In your code, dealloc will never be called, and therefore your view controllers will not be released anyway. You need to release everything in applicationDidEnterBackground...
2) in applicationDidEnterBackground do something like:
[NavController1 release];
NavController1 = nil;
[NavController2 release];
NavController2 = nil;
tabBarController.viewControllers = nil;
then recreate everything again in
(void)applicationWillEnterForeground:(UIApplication *)application
3) doing this will release firstpage, NavController1 and NavController2 (and any other view controllers you may have added)
4) yes you should. You do not get a memory leak in this instance because the delegate never gets dealloc'd and therefore there is still a valid reference to the objects. No you cannot autorelease as autorelease will release the object when the call stack returns to the run loop.
i have a small UIView, i fill it with some string data from net.
i want to it load just once. thats why i call it in appdelegate class.
CurrencyView *currView;
#property (nonatomic, retain)CurrencyView *currView;
in application didFinishLaunchingWithOptions:
CurrencyView *view=[[CurrencyView alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
self.currView = view;
[view release];
then i call it in every viewcontroller (in tabs).
AppDelegate_iPhone *delegate = [AppDelegate_iPhone sharedAppDelegate];
self.currencyView= delegate.currView
[self.view addSubview: currencyView];
when i move in tabs, currView moves from tab to other tab. when look back to the previous tab, currencyview is gone. it stays on the last tab. i dont know why.
what should i do for putting currview in all views?
You can't put an UIView to different controller at the same moment. When you add it to another controller / view, it will be removed from is previous one.
So you should re-add it when you change of tab ^^ (by using a delegate method, for example. It depends what you are using ;-))
I think there isn't another solution but if someone has a better idea it can interest me.
If you want to share the same view instance, you can retrieve the instance from the appdelegate in the viewcontroller viewWillAppear: method, check if the view's superview is not nil and eventually remove it from the superview, then add it to the current view. Something like this
-(void)viewWillAppear {
AppDelegate_iPhone *delegate = [AppDelegate_iPhone sharedAppDelegate];
CurrencyView *cView = [delegate currView];
if(nil!=[cView superview) {
[cView removeFromSuperview];
}
[[self view]addSubview:cView];
}
Maybe you can remove the currView from the superview in the viewWillDisappear: method rather than in the above.
Still, this remain very error prone and not very clean. It would be better if you just allocate two instance of CurrencyView and let them share the common data/model.
If you are low on memory, I doubt a single view will kill you app, but if that's the case there are other way to reduce the memory footprint, see the Apple's documentation on memory management and view controller lifecycle.
If what you want is a copy of the view, your #property statement for *cView should be
#Property (nonatomic, copy) UIView* cView;
if what you have instead is
#Property (nonatomic, retain) UIView* cView;
You are trying to share the same object in multiple views, which you can't do. With the former each time you grab it from the superview you will be making a copy of it, and I think that will work.
I have a TabBar application with several nibs, most with a NavBar. It works pretty well, except for the "views" that are inside the "More" section of the tabBar.
As expected, it will put a NavBar to return to the "More" list, as well as the NavBar i've placed in the nib.
I've tried to remove the view controllers from the moreNavigationBar and put the top controller from my nib's navBar, but I get and extra view from somewhere:
- (void)viewDidLoad {
TestAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
UITabBarController *ctrl = appDelegate.rootController;
UINavigationController *navCtrl = ctrl.moreNavigationController;
[navCtrl popToRootViewControllerAnimated: NO];
[navCtrl pushViewController: navController.topViewController animated: YES];
navController = navCtrl;
[super viewDidLoad];
}
My AppDelegate:
#interface TestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITabBarController *rootController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *rootController;
The MainWindow nib is that of a Window-based project with a TabBarController, linked to the rootController in my app delegate.
The other nibs have a view + navigationController and I have a UITableViewController subclass as my Root View Controller.
If I could get this to work it wouldn't still solve my problem, because I want to allow the user to place this anywhere in the tabBar, so, I must have some way of knowing if there's a navigationBar.
So, my question is, how do you know if there's a navigationBar (in this case, if the tabBar's navigationBar is being shown) and, if so, how do I get my navigationController to "become" the tabBar's navigationController?
Or, if you have another idea on how to solve this problem, i'd also be appreciated :)
The recommendation from apple is that you have the TabBar controller contain the Navigation controllers and not the other way around. I have a setup more or less like this, and I have the More tab hold a Nav controller, basically like this:
#interface SomethingNavViewController : UIViewController {
UIView* aview;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIView *aview;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
In the NIB, I have a separate Nav controller in the view of the more panel, I haven't replaced the tab bar item's view with a nav controller view, I've just added a Nav controller to the view.
In my implementation file, I have:
- (void)viewDidLoad {
[super viewDidLoad];
[[self view] addSubview:[navigationController view]];
SomeOtherController *aController = [[[SomeOtherController alloc ] initWithNibName:#"SomeOtherController" bundle:nil ] autorelease];
aController.title = #"Artwalks";
// lots of application logic here.
[self.navigationController pushViewController:aController animated:YES];
[self.navigationController setDelegate:self];
}
One key thing about this is that I have implemented the navigationController's delegate method, which is really handy when you're just inserting the nav controller. I found when I didn't do this, my views don't get viewDidAppear messages, so I implemented the protocol and added this method:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController respondsToSelector:#selector(viewDidAppear:)]) {
[viewController viewDidAppear:animated];
}
}
and that solved a variety of my lingering problems.
Anyway, I hope this answer gave you the detail you needed. IF it didn't, please give more details about your question. I'm not quite sure what but I get and extra view from somewhere met, but it sounds like something I encountered before I found this solution.