Can someone point me in the right direction. When I load this file in as a nib, and unload it, and reload it instruments says I'm getting a memory leak. Specifically, it's says that where I set the compareOptions NSMutableArray, and where I call [vc release].
CompareOptions is a synthesize property that is also released in the dealloc.
Many thanks in advance.
- (void)viewDidLoad{
[super viewDidLoad];
//NSLog(#"Comparison.viewDidLoad");
self.compareOptions = [[NSMutableArray alloc] init];
self.tabs = [[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)];
//NSDictionary * currComparison = (NSDictionary*)[data objectAtIndex:0];
//NSArray * correctOptions = [currComparison objectForKey:#"correct"];
for(int i = 0; i < 3; i++)
{
UIViewController * vc = [[UIViewController alloc] initWithNibName:#"ComparisonOptions" bundle:nil];
ComparisonOptions * options = (ComparisonOptions *)vc.view;
[options setup];
options.index = i;
//options.frame = CGRectMake(355 + (306 * i), 475, options.frame.size.width, options.frame.size.height);
//[options setCorrect:[correctOptions objectAtIndex:i]];
[vc release];
[self.view addSubview:options];
[self.compareOptions addObject:options];
}
[self.view addSubview:self.tabs];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tabSelectedHandler:) name:#"tabSelected" object:nil ];
[self update:0];}
how does the property of compareOptions and tabs look like? Does it retain your objects?
If it does retain, then you'll get a double retain if you use the setter and alloc.
self.compareOptions = [[NSMutableArray alloc] init];
^ retains ^^^^^ retains
self.tabs = [[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)];
^ retains ^^^^^ retains
you could use this instead
self.compareOptions = [NSMutableArray array];
self.tabs = [[[ComparisonTabs alloc] initWithFrame:CGRectMake(450, 85, 650, 50)] autorelease];
UIViewController * vc = [[UIViewController alloc] initWithNibName:#"ComparisonOptions" bundle:nil];
ComparisonOptions * options = (ComparisonOptions *)vc.view;
[vc release];
[self.view addSubview:options];
vc.view (ie options) will be deallocated at the same time vc will be deallocated. And this happens when you call [vc release]. You can't use options after this.
You should release vc after you've added the view to the subview.
And you should think about better class names. I would never assume that ComparisonOptions is a View. It sounds more like NSCaseInsensitiveSearch etc. You know, like it would be an option.
Related
I have created two view controllers in which there is UIImage animations.Its crashing frequently and showing memory leaks in xcode instrument.
My Controllers Code-
- (void)viewDidLoad {
NSArray *firstArray;
firstArray = [NSArray arrayWithObjects:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0001" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0002" ofType:#"png"]],
::
::
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"up0035" ofType:#"png"]], nil];
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
imgView = UIViewContentModeScaleToFill;
[imgView setAnimationImages:firstArray];
imgView.animationDuration = 1.75;
imgView.animationRepeatCount = 0;
[imgView startAnimating];
[self.view addSubview: imgView];
}
- (void)dealloc {
[super dealloc];
[imgView release];
imgView = nil;
}
And I am changing viewcontrollers as my rootviewcontroller by getting appdelegate object and calling following appdelegate function in my Appdelegate.m(Please suggest any good approach)
- (void)changeRootViewController:(NSString *)controllerName
{
if(self.viewController){
[self.viewController.view removeFromSuperview];
self.viewController=nil;
}
if (controllerName == #"ViewController") {
ViewController *lviewController =[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
} else if (controllerName == #"MainViewController") {
// Use a different VC as roowViewController
MainViewController *lviewController =[[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
} else if (controllerName == #"SecondViewController") {
// Use a different VC as roowViewController
SecondViewController *lviewController =[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
}
[self.window makeKeyAndVisible];
}
And calling this in my respective controlers button pressed as -
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate changeRootViewController:#"ViewController"];
OR
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate changeRootViewController:#"MainViewController"];
I want to manage view controllers from my main controller swapping controllers in and out without Navigationbar.Please help me figure out the best possible approach and avoid leaks as well.
Look at this piece of code:
if (controllerName == #"ViewController") {
ViewController *lviewController =[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.viewController = (RootViewController *)lviewController;
[lviewController release];
lviewController.view = nil;
[self.window setRootViewController:self.viewController]; //LEAKS 100%
}
When you alloc "lviewController", its retain count is 1;
when you do "self.viewController" as I suppose viewController is a retained property, then lviewController retain count is increased to 2;
then you release it, balancing the previous alloc, and retain count returns to 1;
finally you assign it to rootViewController, which is a retained property, so again lviewController retain count is 2;
finally when you "swap" view controller in your window, the lviewController is released, so it retain count becomes 1. As you can see, it will never be dealloc'd. This means that each time you call this function, you make a leak.
You require to change some of your statement in your code ..
This line have no meaning ..
imgView = UIViewContentModeScaleToFill;
There is a function by using that you have to set the value of the UIImageView property to UIViewContentModeScaleToFill find that in a UIImageView documentation.
[imageView setContentMode:UIViewContentModeScaleToFill];
The same with this...
lviewController.view = nil; //Remove this from your code ..
And finally change the implmentation for the dealloc function and remember [super dealloc]; should be in the last in any implementation of dealloc .
- (void)dealloc {
[imgView release];
imgView = nil;
[super dealloc];
}
The probable leak is due to the code inside "SecondViewController", "MainViewController", "ViewController" classes. And the reason for crash is you are releasing image view after [super dealloc]
try this,
- (void)dealloc {
[imgView release];
imgView = nil;
[super dealloc];
}
I need to access to parentViewController after presentMoalViewController.
This is my method that I call in the firstViewController to view the secondViewController:
- (void)viewData {
SecondViewController *viewCtrl = [self.storyboard instantiateViewControllerWithIdentifier:#"select_data"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewCtrl];
navigationController.navigationBar.barStyle = UIBarStyleBlack;
UIBarButtonItem *saveButton = [[[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStyleDone target:viewCtrl action:#selector(saveData)] autorelease];
viewCtrl.navigationItem.rightBarButtonItem = salvaButton;
UIBarButtonItem *undoButton = [[[UIBarButtonItem alloc] initWithTitle:#"Undo" style:UIBarButtonItemStyleBordered target:viewCtrl action:#selector(backView)] autorelease];
viewCtrl.navigationItem.leftBarButtonItem = annullaButton;
[self presentModalViewController:navigationController animated:YES];
}
When I click on saveButton, I try to access to parentViewController in this way, but it not work:
- (void) saveData {
FirstViewController *parentView = (FirstViewController*)[[self navigationController] presentingViewController];
parentView.dataString = [[NSMutableString alloc] initWithFormat:#"new string"];
[parentView performSelectorInBackground:#selector(myMethod) withObject:nil];
[self dismissModalViewControllerAnimated:YES];
}
It would be much better if you used a delegate/protocol in your second viewController that the first viewController could set itself as. You can find more info here Pass data back to the previous controller or simply doing a search. This design pattern is used virtually everywhere in iOS programming.
The big advantage is that then your second viewController becomes independent of whoever presented it, and can be easily reused. The technical term would be 'decoupling'.
Last time I did this I used notifications:
[[NSNotificationCenter defaultCenter] postNotificationName:#"saveData" object:dataString];
in your parent vc view didload:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveDataNow:) name:#"saveData" object:nil];
For navigationController you can declare a property called something like myController, then you have to initiate your navigationController like this:
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewCtrl];
navigationController.navigationBar.barStyle = UIBarStyleBlack;
navigationController.myController = self;
UIBarButtonItem *saveButton = [[[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStyleDone target:viewCtrl action:#selector(saveData)] autorelease];
viewCtrl.navigationItem.rightBarButtonItem = salvaButton;
UIBarButtonItem *undoButton = [[[UIBarButtonItem alloc] initWithTitle:#"Undo" style:UIBarButtonItemStyleBordered target:viewCtrl action:#selector(backView)] autorelease];
viewCtrl.navigationItem.leftBarButtonItem = annullaButton;
[self presentModalViewController:navigationController animated:YES];
In your parentViewController Class you declare your Method
- (void) saveData {
FirstViewController *parentView = (FirstViewController*)[[self navigationController] presentingViewController];
parentView.dataString = [[NSMutableString alloc] initWithFormat:#"new string"];
[parentView performSelectorInBackground:#selector(myMethod) withObject:nil];
[self dismissModalViewControllerAnimated:YES];
}
In your navigationController you can then call [myController saveData];
This should work, if you have any questions, feel free to ask!
You can access your parent view controller using following-
- (void) saveData {
NSMutableArray *activeControllerArray = [self.navigationController.viewControllers mutableCopy];
FirstViewController *parentView;
for(int i = 0, i <[activeControllerArray count], i++) {
if([[activeControllerArray objectAtIndex:i] isKindOfClass:[FirstViewController class]) {
parentView= [activeViewController objectAtIndex:i];
break;
}
}
parentView.dataString = [[NSMutableString alloc] initWithFormat:#"new string"];
[parentView performSelectorInBackground:#selector(myMethod) withObject:nil];
[self dismissModalViewControllerAnimated:YES];
}
I wanted to double check that I was doing correct memory management. Is this correct? Do I have the correct amount of releases.
In my .h file:
UITableView *_sortOrderTableView;
#property (nonatomic, retain) UITableView *SortOrderTableView;
In my .m file:
In dealloc
[_sortOrderTableView release];
My code that presents the popover is this:
- (IBAction)sortButtonOrderPressed:(id)sender {
UIViewController *sortOrderController = [[UIViewController alloc] init];
self.SortOrderTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 200, 100)];
self.SortOrderTableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"App_Background.png"]];
self.SortOrderTableView.bounces = NO;
self.SortOrderTableView.scrollEnabled = NO;
sortOrderController.view = self.SortOrderTableView;
sortOrderController.contentSizeForViewInPopover = CGSizeMake(200, 100);
self.SortOrderTableView.delegate = self;
self.SortOrderTableView.dataSource = self;
self.SortPopover = [[UIPopoverController alloc] initWithContentViewController:sortOrderController];
[self.SortPopover presentPopoverFromRect:_sortButtonOrder.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[self.SortOrderTableView release];
[sortOrderController release];
}
Remove [self.SortOrderTableView release];, it should be [_sortOrderTableView release]; or self.SortOrderTableView = nilanyway, but you're already calling that in your dealloc method, so there is no need to release it here. If you want to release it though, use self.SortOrderTableView = nil.
Apart from that you will also have to release your SortPopover's instance variable in your dealloc method.
According to the docs, you do one release per alloc or retain (etc)
However what about when using retain propertys?
eg:
HEADER
#property(retain)UIView *someView;
IMPLEMENTATION
/*in some method*/
UIView *tempView = [[UIView alloc] init]; //<<<<<ALLOC - retain count = +1
[tempView setBackgroundColor:[UIColor redColor]];
self.someView = tempView; ///<<<<<RETAIN - retain count = +2
[tempView release]; ///should I do this?
or a different version of the IMPLEMENTATION
self.someView = [[UIView alloc] init]; //<<<<<ALLOC & RETAIN - retain count = +2
//now what??? [self.someView release]; ????
EDIT: I didn't make it clear, but I meant what to do in both circumstances, not just the first.
/*in some method*/
UIView *tempView = [[UIView alloc] init]; //<<<<<ALLOC - retain count = +1
[tempView setBackgroundColor:[UIColor redColor]];
self.someView = tempView; ///<<<<<RETAIN - retain count = +2
[tempView release]; ///should I do this? // YES!!!!
And you should also release all retain properties in your dealloc method, before [super dealloc].
Your first version is correct. There's only one ongoing reference to the view, so a retain count of 1 is appropriate.
For the second sample, you can use autorelease:
self.someView = [[[UIView alloc] init] autorelease];
Right, this may not be possible but maybe it it...
I have 3 UIViewControllers, each with their own XIB files which contain the layout for each slide of a presentation (each slide will contain video, images, other interactive elements). I have hooked them all up to a UIScrollView by adding them one by one, see below:
page1 *vc = [[page1 alloc] initWithNibName:#"page1" bundle:nil];
vc.view.frame = CGRectMake(0.0f, 0.0f, 1024.0f, 768.0f);
[sv addSubview:vc.view];
[vc release];
page2 *vc2 = [[page2 alloc] initWithNibName:#"page2" bundle:nil];
vc.view.frame = CGRectMake(1024.0f, 0.0f, 1024.0f, 768.0f);
[sv addSubview:vc2.view];
[vc2 release];
page3 *vc3 = [[page3 alloc] initWithNibName:#"page3" bundle:nil];
vc.view.frame = CGRectMake(2 * 1024.0f, 0.0f, 1024.0f, 768.0f);
[sv addSubview:vc3.view];
[vc3 release];
What I want to be able to do is create an array of UIViewController and loop around the array to add them, rather than one by one. First off, I can't seem to create an array of UIViewControllers, my code is as follows:
NSArray *pages = [[NSArray alloc] initWithObjects:page1, page2, page3, nil];
and the error is: expected expression before 'page1'.
I did manage to load up the UIViewControllers with the following code:
for (int i = 1; i <= 3; i++) {
UIViewController *vc = [[UIViewController alloc] initWithNibName:[NSString stringWithFormat:#"page%i", i] bundle:nil];
vc.view.frame = CGRectMake((i-1) * 1024.0f, 0.0f, 1024.0f, 768.0f);
[sv addSubview:vc.view];
[vc release];
}
But even though the content from the XIBs was displayed, the viewDidLoad function of the loaded in UIViewControllers is never fired.
So, does any one have any tips here for loading up an array of UIViewControllers into a UIScrollView?
Thank you!
What is page1 *vc? Is page1 the name of your custom view controller class? It should start with a uppercase letter, i.e. Page1ViewController *vc.
Your array needs instances of the classes, not the classes itself.
NSArray *pages = [[NSArray alloc] initWithObjects:vc, vc2, vc3, nil];
But then, you shouldn't release the view controllers until you added them to your pages array, i.e. put [vc release]; [vc2 release]; etc. after your NSArray *pages = ...;