Memory Leak in initWithContentRect and setContentView - objective-c

In my application, I am creating a window and setting a content view. Inspector is showing me some memory leaks.
In setContentView: I think it releases previous NSView.
My code is given below:
//contentrect is NSRect which is already initialized.
//stylemask is NSUInteger which is also initialized.
//window_ is of NSWindow type.
window_ = [NSWindow alloc];
//Here, I am getting memory leak.
window_ = [window_ initWithContentRect:contentrect styleMask:stylemask
backing:NSBackingStoreBuffered defer:NO];
//Set window delegate to receive close notication.
[window_ setDelegate:delegate_];
//I believe this is the behavior is by default.
//[window_ setReleasedWhenClosed:YES];
//Setting the windows title.
[window_ setTitle:title_];
//Setting the window frame screenrect which is also initialized.
[window_ setFrame:screenrect display:YES animate:YES];
//MyView is inherited from NSView.
//Set MyView instead of default NSView.
//Set as it have same content rectangle.
contentrect = [[window_ contentView] frame];
//Allocate MyView.
MyView * view = [[MyView alloc] initWithFrame:contentrect];
[window_ setContentView:view];
[rWindow orderFront:nil];
Edit:
I am not getting memory leak in setContentView which I called, but in setContentView which is called inside as initWithContentRect.

You have to balance retain/release count
you have to release window_ somewhere, one place will be in dealloc
- (void)dealloc
{
[window_ release]; window_ = nil;
// other releases
[super dealloc];
}
it is a bad idea to alloc init in different line
similarly, you have to release your view by add [view release]; after setContentView
or autorelease it
MyView * view = [[[MyView alloc] initWithFrame:contentrect] autorelease];
if you have no idea how management works in Objective-C, you better switch to ARC first and learn details about how it works.

Related

Do I need to remove views from superviews in dealloc?

If I alloc/init a view and add it to a another view in code (I did not use a xib) - do I need to remove it when the containing UIViewController's dealloc message is sent? I have seen this code in certain places, and wondered is it necessary under some circumstances to free memory?
Thanks,
Marc
If you do this,
UIView *v = [[UIView alloc] init];
[self.view addSubview:v];
[v release];
or
UIView *v = [[[UIView alloc] init] autorelease];
[self.view addSubview:v];
,the v will be released when its parent view release;
When the parent view use addSubview, it will retain the subview, and will release the subview when it is released.
This isn't necessary. All UIView subclasses hold subviews array, which gets released in the final UIView dealloc message, which releases your views.

UIPopover on ARC

I am using ARC on an iPad app with the code below, the popover flashes on the screen, but doesn't stay.
What I am doing wrong?
Please help
- (IBAction)photoLibraryAction:(id)sender
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setDelegate:self];
UIPopoverController *pop1 = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[pop1 setDelegate:self];
[pop1 presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[pop1 setPopoverContentSize:CGSizeMake(320, 400)];
}
if ([pop1 isPopoverVisible])
{
// Popover is not visible
[pop1 dismissPopoverAnimated:YES];
}
}
In ARC, pop1 will be released right after -photoLibraryAction: returns, because ARC doesn't know that -presentPopoverFromBarButtonItem:permittedArrowDirections: makes the object usable beyond its scope.
You'll have to add an instance variable for your popover controller so ARC doesn't release it. Your if-statement is invalid, too, because when the method returns, pop1 is no longer available for you to use. You'll have to use an instance variable there as well.

Memory Crash in UIPopoverController

I've now invested days in trying to figure out what is going on and for the life of me I can't see what I am doing wrong. I am popping up a UIPopover when the user touches a point on the screen. The popover has a tab controller and table view that displays information about that point. But when the popover is dismissed, it crashes claiming that:
-[UIAnimator removeAnimationsForTarget:]: message sent to deallocated instance
Here is the code that loads the view controller:
MyViewController *popView = [[MyViewController alloc] init];
myPop = [[UIPopoverController alloc] initWithContentViewController:pop];
[popView release];
myPop.delegate = self;
[airportPop setPopoverContentSize:popView.view.frame.size];
[airportPop presentPopoverFromRect:CGRectMake(location.x,location.y,1,1) inView:self.mainView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
- (void)dismissPopover {
if( myPop != nil ) {
[myPop dismissPopoverAnimated:YES];
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
}
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[myPop release];
myPop = nil;
}
The actual MyViewController is just a UIViewController that with (abridged for brevity) init:
- (id)init
{
self = [super init];
//create a newview
self.view = popView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, POPUP_HEIGHT)];
[popView release];
topBar = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, 30)];
....
[popView addSubview:topBar];
[topBar release];
//create a table view
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, POPUP_WIDTH, POPUP_HEIGHT-30-49)];
table.delegate = table.dataSource = self;
....
//create a tab bar
tabBar = [[UITabBar alloc] initWithFrame:CGRectMake(0, POPUP_HEIGHT-49, POPUP_WIDTH, 49)];
tabBar.delegate = self;
[popView addSubview:tabBar];
[popView addSubview:table];
[tabBar release];
[table release];
return( self );
}
Dealloc is nothing more than [super dealloc] since everything is essentially owned by the view and the view controller will take care of it. When the myPop is released, in DidDismissPopover, the view is also released, so that seems to work okay. But very soon thereafter, I get the crash.
Do I need to do something special to discard the tab view or table view when the popup dismisses?
I am using an autorelease on the cells in the table, should I stop doing that?
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
Thank you in advance for any help!!! Any ideas at all are greatly appreciated!!
-Kevin
[myPop dismissPopoverAnimated:YES] will continue to access you object even after the method call because you set YES for the animation (there is a timer and other stuff going under the hood to perform the animation for that)
So, instead of immediately releasing the object, you could mark it as autorelease to postpone this action, which actually might solved it or not.
Or postpone the release to a time after that makes tyou sure thta the animation will be finished. You could use GCD for that (if you are using iOS 4+) and as the default time for animation in UIKit is 0.3s, the code bellow should do the trick.
double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
});
EDIT: You should use this time only for the test propose as it is far from being the right way to release an object.
You should store a pointer to your UIPopover and release it in your class dealloc method.
Add following keys in yor Exectables info->Arguments tab-> enviroment variables
NSZombieEnabled = YES
CFZombie = 5
MallocStackLoggingNoCompact = 1
then when you get crash automatically you get a message
some thing like this
(gdb) continue
2011-06-09 11:46:08.404 test [6842:40b] * -[_NSArrayI
release]:message sent to deallocated instance 0X64a4900
then type
(gdb) info malloc-history 0x64a4900
it will give you complete history.
May be it helps you to find the place.
also you can use where command when you got crash.
The fastest way to avoid waiting for animation to end is to set popoverController.delegate = nil as soon as you dismiss the popup or the Popover Delegate method
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
is called.

Create subview on awakeFromNib

I'm trying to create a NSImageView programmatically as a subview of another NSImageView when awakeFromNib is called.
My code is as follows (Fader is defined in MyImageView.h):
#implementation MyImageView
- (void)awakeFromNib {
Fader = [NSImageView initWithFrame: [self frame]];
}
I get the warning message "NSImageView may not respong to +initWithFrame". When I build, the app simply frizzes without showing anything, and I have to "force quit".
What am I doing wrong?
You’ve forgotten to send +alloc in order to allocate the object. Change that line to:
Fader = [[NSImageView alloc] initWithFrame: [self frame]];

Using Instruments to improve memory-management with modal view controllers

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.