I have a UIView which I create programmatically roughly like so:
#implementation SlideShowView
- (id)initWithImages
{
…
if (self=[super initWithFrame:CGRectMake(0, 0, 320, 480)])
{
// init objects here
}
return self;
}
- (void)dealloc
{
printf("dealloc slide show view\n");
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"unlockUI" object:nil ];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"setUserRating" object:nil ];
[mCurrentImageView release];
[mRightImageView release];
[mLeftImageView release];
[mImages release];
[queue cancelAllOperations];
[queue release];
[managingArray release];
[super dealloc];
}
with a uiview controller that contains this:
- (void)loadView {
{
...
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.slideShowViewObject = [[SlideShowView alloc] initWithImages];
[self.view addSubview:slideShowViewObject];
…
}
-(void)dealloc
{
[slideShowViewObject release];
[self.slideShowViewObject removeFromSuperview];
printf("dealloc of slideshow view controller\n");
[super dealloc];
}
this seems to be able to dealloc both the view controller and view but when I push this view controller back onto the stack I get the message with zombie enabled: -[SlideShowView retain]: message sent to deallocated instance 0x43ab160
should the alloc not be creating a new instance of the view when the controller is pushed? I don't understand and after spending many hours reading through other posts as well as looking at memory guides I would say I'm thoroughly stumped! Can someone offer any pointers? I only posted the code I deemed necessary but I can post more if need be.
Thanks so much!
First, you shouldn't call both [super init] and [super initWithFrame:], only one of the two in your UIView subclass.
Secondly, you should only set self.view in a view controller in the -loadView method, which is designed for you to create your views. Normally, unless you need to do certain types of setup such as initializing variables, etc, you shouldn't need to override -init in UIViewController subclasses.
finally, after finagling with the code again tonight I finally got it working. The key piece I was not releasing appears to be
[self.view release];
I suppose this makes perfect sense since I was adding the view as a subview of this alloc'd self view, but I had tried this before. Perhaps cleaning up the code is what made it work this time.
Related
In sesssion inactivity implementation for my project. I have created a NSNotification in RootViewController class of project.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: #"Close"
style: UIBarButtonItemStyleDone
target: self
action: #selector(closeModal)];
UIImage *image = [UIImage imageNamed:#"fidelity_logotype"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
[imageView setImage:image];
[self.navigationItem setTitleView:imageView];
self.navigationController.view.backgroundColor = [UIColor fidelityGreen];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:) name:#"ApplicationTimeout" object:nil];
}
- (void) applicationDidTimeout:(NSNotification *) notif
{
NSLog(#"I m here");
BCDSessionInactivityViewController *sessionView=[[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"InactivityViewController"];
sessionView.modalPresentationStyle = UIModalPresentationFormSheet;
sessionView.preferredContentSize = CGSizeMake(838,340);
[[self topViewController] presentViewController:sessionView animated:YES completion:nil];
}
and in logoutviewcontroller, i am removing this observer written below
- (IBAction)logoutbtn:(id)sender
{
NSLog(#"logout is called");
[sessionTimer invalidate];
sessionTimer = nil;
[[BCDTimeManager sharedTimerInstance]stopIdleTimer];
//[self dismissViewControllerAnimated:YES completion:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ApplicationTimeout" object:nil];
[self performSegueWithIdentifier:#"Thankyoupage" sender:self];
}
This is code where i posting the notification.
- (void)idleTimerExceeded {
NSLog(#"idle time exceeded");
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ApplicationTimeout" object:nil];
}
for first time login, it works fine whenever timer exceeds, i post a notification and model view is presesnted perfectly, but once user logs out, after that whenever the notification is posted, selector method is getting called twice
I am pretty sure that notification is getting posted only once.
Should i create notification in every view controller and then remove it when view unloads?
what i am doing wrong here?
You are adding the notification in RootViewController and trying to remove it from LogoutViewController. So that notification observer added to the RootViewController never gets removed. So each time you logout and login, the observer call will get increased by one. For fixing the issue, you need to remove the observer from the RootViewController object.
For fixing the issue you mentioned in your comment,
If I remove the observer in RootViewController , then if timers
exceeds in some other views, and notification observer is not called.
Also, i can't add observer on app delegate because we want timer
notification to be fired only after reaching rootviewController
Write two public methods in AppDelegate
One for adding observer (addObserver)
One for removing observer (removeObserver)
When you reach RootViewController, call the addObserver method for adding the observer
When logout is pressed, call the removeObserver for removing the observer
I'm trying to understand memory management to do some better apps, but was stopped at one point :
I use some UIButtons. So I alloc them, work with them etc. But i need to release them at one moment.
So I implement deallocmethod for all the object which are usefull all the time the UIViewController is on screen, and which need to be released when it desappeard. So in my UIViewController I implement :
-(void)dealloc
{
NSLog(#"Dealloc call");
[myButton release];
.... //Some objects release
[super dealloc];
}
But i never see the Dealloc call printed, so I think that it doesn't passed by the dealloc method when the UIViewController desappeard.
So, how does it work ? / What is false ?
Thanks !
EDIT : method to change of viewController :
-(void)myMethod
{
if (!nextviewcontroller)
{
nextviewcontroller = [[NextViewController alloc]init];
}
UIView *nextView = nextviewcontroller.view;
UIView *actualView = actualviewcontroller.view;
[actualviewcontroller viewWillAppear:NO];
[nextviewcontroller viewWillDisappear:NO];
[actualView removeFromSuperview];
[self.view addSubview:nextView];
[actualviewcontroller viewDidAppear:NO];
[nextviewcontroller viewDidDisappear:NO];
}
In my app I have a UIViewController subclass (VC for short) that I only use in one place in the entire app. In that place, I have been creating and pushing it like this:
MyViewController* VC = [MyViewController new];
[self.navigationController pushViewController:VC animated:YES];
[VC release];
but I was thinking that since this is the only place I am using a view controller of this type, I could do something like this so the settings used won't be reset each time the view controller is pushed onto the stack:
static MapsToSendPicker* VC = nil;
if(!VC) {
VC = [MapsToSendPicker new];
}
[self.navigationController pushViewController:VC animated:YES];
[VC release];
The problem with that code is that in VC's dealloc method, I release all of my instance variables and set them to nil, and finally I call [super dealloc]. This deallocates the static view controller, but the test if(!VC) isn't evaluated to true after (this would defeat the purpose of the whole idea if it were; then I'd have to recreate the view controller each time anyway).
My solution is overriding the dealloc method in MyViewController and not calling [super dealloc] at the end. This works, but the compiler raises a warning. How can I get rid of that warning while maintaining the functionality I gain with this design? Thanks!
Edit:
After a quick Google search, I have found this solution:
- (void)dealloc {
if(NO) {
[super dealloc];
}
}
but I would like something a little bit... cleaner. Any thoughts?
Remove the [VC release]; line and add [super dealloc] back. Everything will work properly and dealloc will never get called. Generally you should consider using NSUserDefaults in order to restore the VC properties instead of keeping the controller in memory all the time.
I need help with delete an array with a button from the SecondViewController in the view controller class.
At the moment I have this:
- (IBAction)deleteArrayFromSeconViewController:(id)sender // is the Button which should
// delete the array
{
self.textLabel2.text = #""; // this work fine
ViewController *vc = [[ViewController alloc]init];
[vc.textViewArray removeAllObjects]; // do not remove the objects?
}
What should I do to overtake an order from the SecondViewControllerClass in the ViewControllerClass?
I tried also this in the SecondViewControllerClass:
- (IBAction)deleteArrayFromSeconViewController:(id)sender
{
self.textLabel2.text = #"";
ViewController *vc = [[ViewController alloc]init];
[vc deleteTheArray];
}
to call this function in the ViewControllerClass:
- (void) deleteTheArray
{
[textViewArray removeAllObjects];
}
I'm not at all sure that this is the best way to do this, but you could post a custom NSNotification in your first view controller, and then pick it up in your second. Code Example:
//Post in your first view controller when wanting to delete the array
[[NSNotificationCenter defaultCenter] postNotificationName:#"DeleteArrayNotification" object:nil userInfo:nil /*include any items your reciever might wish to access*/];
and then in your second view controller, you could add yourself as an observer in the -(void)awakeFromNib Method. Put in your awake from nib method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deleteArrayNotification:) name:#"DeleteArrayNotification" object:nil];
And then implement the deleteArrayNotification: method:
- (void)deleteArrayNotification:(NSNotification *)notification {
[array removeAllObjects];
[array release]; //Delete this line if your project is using ARC
}
I am highly doubtful that this is good coding practice to implement NSNotification like this, but I thought it might be of use!
More information can be found under the Apple developer documentation, under NSNotification and NSNotificationCenter.
Hope I can help!
Ben
I feel like I don't understand something fundamental here. I've been working on memory management in my app while using Instruments to check out live allocations. I have a modal view controller (settingsViewController) that has an image for a background. One thing I noticed was that even after settingsViewController dealloc is called, there still is a live Malloc 520 KB, ImageIO is the responsible library. I'd expect live memory to drop back down after dismissing settingsViewController. Is the UIImageView still hanging around somewhere?
Here is how I load the image in viewDidLoad, as well as dismiss the view controller when I'm finished.
- (void)loadView {
[super loadView];
////// background ////////
UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"settings_background.png"]];
[self.view addSubview:background];
[background release];
//////////////////////////
}
- (void)viewDidLoad {
[super viewDidLoad];
///////// done button //////////
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneButtonPressed)];
self.navigationItem.leftBarButtonItem = done;
[done release];
////////////////////////////////
}
- (void) doneButtonPressed {
[self dismissModalViewControllerAnimated:YES];
}
- (void)dealloc {
NSLog(#"settingsViewController dealloc"];
[super dealloc];
}
At this point, this is all that is in the settingsViewController, so no need to do anything special in dealloc, right? Here is how I'm showing testViewController in the rootViewController.
- (void) loadSettingsView {
SettingsViewController *settingsViewController = [[SettingsViewController alloc] init];
UINavigationController *settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
[self presentModalViewController:settingsNavigationController animated:YES];
[settingsViewController release];
[settingsNavigationController release];
}
I'd like to make sure I understand what is going on before moving forward. I have several different modal view controllers, each with a different image as a background. Since each one creates a Malloc 520 KB, I end up using 2-3 MB of precious memory for no good reason. What is holding on to that 520 KB?
When you use the +[UIImage imageNamed:] method, the framework caches the image data for you. That's why you see it hold onto some memory even after your view is released. If you're working in the simulator and you want to see it release that memory, send the simulator a memory warning after you've dismissed your view. The image framework should then release the cached image data.