Navigation Control Problem - objective-c

I am using some methods to load a new .xib and go back to the main menu. However after about five time it crashes by using too much memory. I need to be able to go back to the main menu and to the game many times. Any other methods I should use for the navigation controls.
Main Menu part:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
Game part to return to main menu:
[self.navigationController popViewControllerAnimated:NO];
Here is the viewdidLoad
{
- (void)viewDidLoad {
[super viewDidLoad];
[self StartTimer];
TotalSeconds = 0;
GameCenterTotalSeconds = 0;
timeSec = 0;
timeMin = 0;
Background = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)] autorelease];
Background.image = [UIImage imageWithContentsOfFile:[ [ NSBundle mainBundle] pathForResource:#"Background" ofType:#"png"]];
[self.view addSubview:Background];
timeLabel.textColor = [UIColor redColor];
[self.view addSubview:timeLabel];
NumberLabel = [[[UIImageView alloc] initWithFrame:CGRectMake(0, -4, 60, 70)] autorelease];
NumberLabel.image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"Number" ofType:#"png"]];
[self.view addSubview:NumberLabel];
QuestionNumber = [[[UILabel alloc] initWithFrame:CGRectMake(23, 17, 20, 20)] autorelease];
QuestionNumber.text = #"1";
QuestionNumber.textColor = [UIColor redColor];
QuestionNumber.backgroundColor = [UIColor clearColor];
[QuestionNumber setFont:[UIFont fontWithName:#"Marker Felt" size:30]];
[self.view addSubview:QuestionNumber];
numberLives = 1;
appDelegate = (OppositeMoronTestAppDelegate *)[[UIApplication sharedApplication]delegate];
musicButton = [[[UIButton buttonWithType:UIButtonTypeCustom] retain] autorelease];
musicButton.frame = CGRectMake(5, 283, 35, 35);
musicButton.backgroundColor = [UIColor clearColor];
if (appDelegate.shouldPlayMusic == YES) {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOn" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOff) forControlEvents:UIControlEventTouchUpInside];
} else {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOff" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOn) forControlEvents:UIControlEventTouchUpInside];
}
[self.view addSubview:musicButton];
[self showQuestion1];
}
}

try autorelease on your view controller:
GameViewController* game = [[[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil] autorelease];
The navigation controller will take ownership of the view controller passed to it, so you don't have to keep a reference to it. But you can't keep allocating GameViewControllers over and over without releasing them. autorelease is useful for that. You could also release it after you've passed it to the navigation controller if you prefer:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
[game release];
game = nil;
EDIT:
So if you're already releasing the game object, then it must be a memory leak within the GameViewController class itself.
Annything you alloc, copy or retain in your GameViewController class you're supposed to release in the dealloc method (and maybe also in the viewDidUnload method if you're alloc/copy/retaining in the viewDidLoad method).
The iOS Memory Management Programming Guide might be helpful if you want to get into more detail.
If you want to post the relevant code from the GameViewController class, I'm sure someone will be able to help you pin down the memory leak.
You can also try the Leaks tool in Instruments
EDIT 2:
I'm assuming you have several IBOutlets connected to properties in your GameViewController class...
don't know if you're already doing this, but in your viewDidUnload method AND on your dealloc method you have to set all of these IBOutlets properties to nil in order to release them, like so:
- viewDidUnload
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
}
- dealloc
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
[super dealloc];
}
In general, if you have any properties declared with retain, that means that when you set that property the object will be retained. If you set that property to nil, the object that was there will be released for you. So any properties with the retain keyword should be set to nil (or the backing ivar released).

Related

UIPopoverController trouble

I'm trying to create a popover with the following code:
//.h
#interface LoginViewController : UIViewController <UIPopoverControllerDelegate>
....
#end
//.m
- (IBAction)popoverTest:(id)sender
{
UIViewController *popoverContent = [[UIViewController alloc] init];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
UILabel *nameLabel = [[UILabel alloc] init]; //edited, fixed UILabel allocation
nameLabel.text = #"Person's name";
[nameLabel sizeToFit];
nameLabel.frame = CGRectMake(10, 10, nameLabel.frame.size.width, nameLabel.frame.size.height);
[myView addSubview:nameLabel];
popoverContent.view = myView;
popoverContent.contentSizeForViewInPopover = CGSizeMake(300, 300);
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContent];
popoverController.delegate = self;
[popoverController presentPopoverFromRect:((UIButton *)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
NSLog(#"ran all code");
}
I created a UIView, put a label as a subview and then assigned my view to the UIViewController.view. Then I created a popover controller, sized the popover controller, set the delegate and presented it from the button's frame.
I receive a SIGABRT and the app crashes.
Is there something I'm missing?
EDIT: I fixed the UILabel allocation. The problem is always there.
Your code is quite strange.
For example why do you create a view and do you add it to the content view of your popover?
Then, you have to make attention to memory leaks. There a lot of them in your code.
That said, here a simple example for display a UIViewcontroller within a UIPopoverController.
- (IBAction)yourAction:(id)sender
UIButton* senderButton = (UIButton*)sender;
UIViewController* yourController = [[UIViewController alloc] init];
UIPopoverController* pop = [[UIPopoverController alloc] initWithContentViewController:yourController];
pop.delegate = self;
pop.popoverContentSize = CGSizeMake(300, 300);
self.popover = pop;
[pop presentPopoverFromRect:senderButton.bounds inView:senderButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
pop.passthroughViews = nil;
[yourController release];
[pop release];
}
where self.popover is a #property with a retain policy. In this manner in UIPopoverControllerDelegate methods (or wherever you want), you can release your popover and/or dismiss it.
Hope it helps.
P.S. Check the code because I've written by hand.
Edit
Usually when you create a popover, its content view controller is or a custom UIViewController or a UINavigationController (in this case you want to take advantage of its navigation bar).
For example, instead of the simple UIViewController, you could create a custom one
//.h
#interface CustomUIViewController : UIViewController
#end
//.m
#implementation CustomUIViewController
// other code here...
- (void)viewDidLoad
{
// here you are sure that the view has been loaded in memory (alternatively override loadView method), so
UIView* greenView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
green.backgroundColor = [UIColor greenColor];
[self.view addSubview:greenView];
[greenView release];
}
#end
and use it within a a popover
- (IBAction)yourAction:(id)sender
UIButton* senderButton = (UIButton*)sender;
CustomUIViewController* yourController = [[CustomUIViewController alloc] init];
// same as before...
}
solved my issue from this question.
short answer: ARC doesn't retain the UIPopoverController.
i made it an instance variable and works just fine.
The problem seems to be the following:
UILabel *nameLabel;
nameLabel.text = #"Person's name";
nameLabel is uninitialized. It points to random memory, and when calling -setText: through the text-property, your application crashes.
Apart from this, you have a few memory leaks. Remember: If you call alloc, you have to release it somewhere.
(I'm not sure how this works with the new automatic alloc/release).

ModalViewController not presenting on button press

I have this ViewController with on it a UIButton. This button fires a method and this should present a modalVC in his place. Yet for some reason it's not working anymore. I've been using the same code before without any issues but yet it bugs me.
-(void)showModalVC //the method that's being fired by the button.
{
NSLog(#"modalVC to create a table"); //this log is being printed so the button fires as proper.
self.myModalVC = [[MyModalViewController alloc] init]; //a local var gives same results.
self.myModalVC.dismissDelegate = self; //the delegate is handled as proper.
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:self.myModalVC];
//navController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:navController animated:YES];
[self.myModalVC release];
[navController release];
}
What could be a reason for a ModalVC not to pop over my current view from where ever it is called?
I've been using this very same method before (in other projects in other contexts) so I'm dazzled it hasn't been working yet. The method is fired and it passes every line of code without crash.
If you have an idea. post it here.
Thanks.
The first thing I thought of is that you are creating a leaked object at this line:
self.myModalVC = [[MyModalViewController alloc] init];
self. will retain myModalVC, alloc will also retain it and you are probably releasing it only in dealloc method.
In all other means the code looks quite working. But looking at how you are using self. prefix, maybe you've got a memory issue somewhere else in your application? Try reading about properties and accessor methods.
-(void)showModalVC
{
self.myModalVC = [[ModalVC alloc] init];
self.myModalVC.dismissDelegate = self;
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:self.myModalVC];
navController.modalPresentationStyle = UIModalPresentationFormSheet; //or something similar, this one is used on an iPad
UILabel *navTopItemTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 44)];
navTopItemTitle.text = #"Modal shizzle";
navTopItemTitle.backgroundColor = [UIColor clearColor];
navTopItemTitle.textColor = [UIColor whiteColor];
navTopItemTitle.textAlignment = UITextAlignmentCenter;
[navController.navigationBar.topItem setTitleView:navTopItemTitle];
[self presentModalViewController:navController animated:YES];
[self.addTabViewController release];
[navController release];
}
Problem solved.

Creating a common UIToolbar helper file and methods for Objective C

I have build a view with a UIToolbar which is working great.
This toolbar will be appearing right across the app, and right now I am copy/pasting the code into lots of different files.
I do not want to repeat myself, and am looking to create a helper file that will include the toolbar setup and the methods linked to the toolbar in every file I need.
I've tried putting the following code into a .h .m file and inheriting from UIView, but there is a problem because there is a reference to self.navigiationItem
Is there a way that I can create a common Objective C file that will have all the code and methods I want to use?
Thanks.
- (void)viewDidLoad
// ...
// appears in viewDidLoad
// ---- TOOLBAR -----------//
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 100.0, 44.01f)];
//[toolbar setBackgroundColor:[UIColor blackColor]];
//[toolbar setTintColor:[UIColor redColor]];
//[toolbar.layer setBorderColor:[[UIColor redColor] CGColor]];
// Bar buttons
UIBarButtonItem *barReloadBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(btnReload:)];
[barReloadBtn setStyle:UIBarButtonItemStyleBordered];
// Profile bar button
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"111-user" ofType:#"png"]];
UIBarButtonItem *barProfileBtn = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStyleBordered target:self action:#selector(btnProfile:)];
// Button array
NSMutableArray *buttons = [[NSMutableArray alloc] init];
[buttons addObject:barProfileBtn];
[buttons addObject:barReloadBtn];
[toolbar setItems:buttons];
// Set nav items
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
// memory cleanup
[image release];
[buttons release];
[barReloadBtn release];
[barProfileBtn release];
[toolbar release];
// ---- /TOOLBAR -----------//
}
#pragma mark - IBActions
-(IBAction) btnProfile:(id)sender
{
UserProfileVC *userProfileVC = [[UserProfileVC alloc] initWithNibName:#"UserProfileVC" bundle:[NSBundle mainBundle]];
UINavigationController *tmpNavCon = [[UINavigationController alloc] initWithRootViewController:userProfileVC];
[self.navigationController presentModalViewController:tmpNavCon animated:YES];
[tmpNavCon release];
[userProfileVC release];
}
-(IBAction) btnReload:(id)sender
{
NSLog(#"Not done yet");
}
navigationItem is a property of UIViewController, not UIView. If you've got common functionality like this, I would inherit from UIViewController, add your custom logic to viewDidLoad (or wherever is appropriate) and then inherit your view controllers from that class. Just make sure you call [super viewDidLoad] from your subclasses' implementations of viewDidLoad.

A Tale of Two UIViewControllers

So I think I'm going crazy.
I've updated to XCode 4.2 so I'd have the IOS 5 SDK.
I have an app that's been working great until the upgrade. Now, I have a strange problem. It does not work on my IOS 5 iPad 2, nor will it work in the IOS 5 simulator. Works fine in the 4.3 simulator.
For the purposes of this question, I have two classes based on UIViewController. They do not use NIB files. One, called HistoryBrowser, works great. The other, NoteBrowseViewController, constructed along the same lines, does not.
From NoteBrowseView.Controller.h:
#interface NoteBrowseViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, UIActionSheetDelegate, UISearchDisplayDelegate, UITabBarDelegate, MKMapViewDelegate> {
UITableView* tableView;
... buncha other vars ...
}
#property (retain, nonatomic) UITableView* tableView;
... buncha other properties ...
From NoteBrowseViewController.m:
#synthesize tableView
- (id)initWithEditing:(BOOL)inEditingMode inserting:(BOOL)inserting {
self=[super init];
if (self) {
self.isInserting=inserting;
self.isEditing=inEditingMode;
self.viewIsMap=NO;
self.insertType=defInsertTypeLink;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0],#"viewSortOrder",
[NSNumber numberWithInt:1],#"priorityView",
[NSNumber numberWithBool:NO],#"simpleView",
nil];
[defaults registerDefaults:appDefaults];
self.viewIsSimple=[[NSUserDefaults standardUserDefaults]boolForKey:#"simpleView"];
}
return self;
}
-(void)loadView {
self.view=[[UIView alloc]initWithFrame:[[UIScreen mainScreen] applicationFrame]];
UIButton* aButton;
UIImage* buttonImage;
UIBarButtonItem* spacer=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UISegmentedControl* sc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Alpha",#"Edit", #"View", nil]];
[sc addTarget:self action:#selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[sc setWidth:55.0 forSegmentAtIndex:0];
[sc setWidth:55.0 forSegmentAtIndex:1];
[sc setWidth:55.0 forSegmentAtIndex:2];
sc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:#"viewSortOrder"];
sc.segmentedControlStyle=UISegmentedControlStyleBar;
UISegmentedControl* prisc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Open",#"All", nil]];
[prisc addTarget:self action:#selector(prioritySegmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[prisc setWidth:55.0 forSegmentAtIndex:0];
[prisc setWidth:55.0 forSegmentAtIndex:1];
prisc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:#"priorityView"];
prisc.segmentedControlStyle=UISegmentedControlStyleBar;
UIBarButtonItem* segmentedButton=[[UIBarButtonItem alloc]initWithCustomView:sc];
UIBarButtonItem* prioritySegmentedButton=[[UIBarButtonItem alloc]initWithCustomView:prisc];
buttonImage=[UIImage imageNamed:#"13-plus.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* addButton=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(addButton:) forControlEvents:UIControlEventTouchUpInside];
buttonImage=[UIImage imageNamed:#"187-pencil.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* editButton=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(editButton:) forControlEvents:UIControlEventTouchUpInside];
buttonImage=[UIImage imageNamed:#"21-circle-east.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* simplify=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(simplify:) forControlEvents:UIControlEventTouchUpInside];
buttonImage=[UIImage imageNamed:#"25-circle-west.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* complexify=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(complexify:) forControlEvents:UIControlEventTouchUpInside];
buttonImage=[UIImage imageNamed:#"243-globe.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* map=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(mapify:) forControlEvents:UIControlEventTouchUpInside];
buttonImage=[UIImage imageNamed:#"162-receipt.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* notes=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:#selector(showNotes:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem* cancelButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(cancelButtonPressed:)];
self.doneToolbar=[NSArray arrayWithObjects:spacer, cancelButton, nil];
self.toolbar=[[[UIToolbar alloc]init]autorelease];
if(self.isEditing) {
self.complexToolbar=[NSArray arrayWithObjects:simplify, spacer, segmentedButton, prioritySegmentedButton, spacer, cancelButton, nil];
self.simpleToolbar=[NSArray arrayWithObjects:complexify, spacer, cancelButton, nil];
}
else {
self.complexToolbar=[NSArray arrayWithObjects:simplify, map, spacer, segmentedButton, prioritySegmentedButton, spacer, addButton, editButton, cancelButton, nil];
self.simpleToolbar=[NSArray arrayWithObjects:complexify, map, spacer, addButton, editButton, cancelButton, nil];
}
self.mapToolbar=[NSArray arrayWithObjects:notes, spacer, prioritySegmentedButton, spacer, cancelButton, nil];
if (self.viewIsSimple) {
[self.toolbar setItems:self.simpleToolbar animated:YES];
}
else {
[self.toolbar setItems:self.complexToolbar animated:YES];
}
self.toolbar.autoresizingMask=UIViewAutoresizingFlexibleWidth;
[self.toolbar sizeToFit];
CGFloat toolbarHeight = [self.toolbar frame].size.height;
CGRect rootViewBounds=self.view.bounds;
CGFloat rootViewHeight = CGRectGetHeight(rootViewBounds);
CGFloat rootViewWidth = CGRectGetWidth(rootViewBounds);
[self.toolbar setFrame:CGRectMake(0,0, rootViewWidth, toolbarHeight)];
self.mapView=[[[MKMapView alloc]initWithFrame:CGRectMake(0, toolbarHeight, rootViewWidth, rootViewHeight-(self.isInserting?49:0)-toolbarHeight)]autorelease];
self.mapView.delegate=self;
self.mapView.zoomEnabled=YES;
self.mapView.scrollEnabled=YES;
self.mapView.autoresizingMask=UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
self.tableView=[[UITableView alloc]initWithFrame:CGRectMake(0, toolbarHeight, rootViewWidth, rootViewHeight-(self.isInserting?49:0)-toolbarHeight)
style:UITableViewStylePlain];
self.tableView.delegate=self;
self.tableView.dataSource=self;
self.tableView.autoresizingMask=UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 44, rootViewWidth, 44.0)];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.tableView.tableHeaderView = searchBar;
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;
[self.view addSubview:self.tableView];
[self.view addSubview:self.toolbar];
if (self.isInserting) {
UITabBarItem* item1=[[[UITabBarItem alloc]initWithTitle:#"Link" image:nil tag:defInsertTypeLink]autorelease];
UITabBarItem* item2=[[[UITabBarItem alloc]initWithTitle:#"Contents Later" image:nil tag:defInsertTypeContentsLater]autorelease];
UITabBarItem* item3=[[[UITabBarItem alloc]initWithTitle:#"Contents Now" image:nil tag:defInsertTypeContentsNow]autorelease];
UITabBar* tabbar=[[UITabBar alloc]initWithFrame:CGRectMake(0, rootViewHeight-49, rootViewWidth, 49)];
tabbar.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
[tabbar setDelegate:self];
[tabbar setItems:[NSArray arrayWithObjects:item1, item2, item3, nil] animated:YES];
[tabbar setSelectedItem:item1];
[self.view addSubview:tabbar];
}
if(self.viewIsSimple) {
self.contentSizeForViewInPopover = CGSizeMake(200.0, 625.0);
}
else {
self.contentSizeForViewInPopover = CGSizeMake(450.0, 625.0);
}
self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.note=nil;
[sc release];
[prisc release];
[addButton release];
[prioritySegmentedButton release];
[cancelButton release];
[segmentedButton release];
[spacer release];
[editButton release];
[map release];
[simplify release];
[complexify release];
}
And finally, the a NoteBrowseViewController is instantiated thusly from another view controller:
self.noteBrowseViewController=[[NoteBrowseViewController alloc]initWithEditing:NO inserting:NO];
self.noteBrowseViewController.delegate=self;
self.popoverController = [[UIPopoverController alloc]initWithContentViewController:self.noteBrowseViewController];
self.popoverController.delegate = self;
[self.popoverController presentPopoverFromRect:((UIButton*)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
So this is what happens. If I run this in debug, and follow along, as soon as execution gets to this line:
CGRect rootViewBounds=self.view.bounds;
The program crashes with the following errors:
2011-10-20 11:42:02.703 ActionNote3[12332:15803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(0x1eb0052 0x2a60d0a 0x1e9d36e 0x1e9e220 0x1968cb6 0x5153b 0x50e11 0x50879 0x52e4a 0xdcc64e 0x4bcb3 0x72d1 0x1eb1ec9 0xd095c2 0xd0955a 0xdaeb76 0xdaf03f 0xdae2fe 0xd2ea30 0xd2ec56 0xd15384 0xd08aa9 0x20c7fa9 0x1e841c5 0x1de9022 0x1de790a 0x1de6db4 0x1de6ccb 0x20c6879 0x20c693e 0xd06a9b 0x2dbd 0x2d35)
terminate called throwing an exception
By watching things during the debug, I know self.view is being set up properly, and that loadView is being called when it should be. And it's not this line that makes it fail - it's anything that refers to self.view! what???
What really makes me crazy is that I have another subclassed view controller, HistoryBrowser, that is loaded in exactly the same manner... and it works fine.
So, aside from being upset that I cannot figure this out, i'd like to understand:
What's changed in XCode 4.2 (from 4.1) that would cause this, or is
this an IOS 5 issue?
What does this error mean, and what can I do
about it?
EDIT:
So based on suggestions from Abberant's answer below, I:
changed the initwithnib to just be init
removed the loadView method
added the full error message
added more relevant code to the init method
And now, referencing self.view works properly. But things have just gotten stranger.
Execution makes it past the first reference to self.view without error. However, it now halts at:
self.tableView.tableHeaderView = searchBar;
in the init method.
The error message received is eerily similar to the one I was receiving before:
2011-10-20 12:34:04.818 ActionNote3[13487:15803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(0x1eaf052 0x2a5fd0a 0x1e9c36e 0x1e9d220 0x1967cb6 0x50b4b 0x50421 0x4fe89 0x4d757 0x6641 0x1eb0ec9 0xd085c2 0xd0855a 0xdadb76 0xdae03f 0xdad2fe 0xd2da30 0xd2dc56 0xd14384 0xd07aa9 0x20c6fa9 0x1e831c5 0x1de8022 0x1de690a 0x1de5db4 0x1de5ccb 0x20c5879 0x20c593e 0xd05a9b 0x212d 0x20a5)
terminate called throwing an exception
So, if I comment that line out, execution procedes until it reaches this group of lines, where it fails when it attempts to do the performFetch.
NSError *error = nil;
if (![[self currentFetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
Debugging into the code for [self currentFetchedResultsController] shows that the fetched results controller is being set up properly and without issue.
The error received here is the same as above. The numbers in the first throw call stack change, but otherwise the error is the same.
So I'm left thinking that I have a larger problem going on here. I just have no idea how to track it down. As I said way up there somewhere, I have another view, constructed in the same manner, that loads fine.
Edit:
So NJones in his answer below suggested following Apple's guidelines by placing the appropriate code in loadView and viewDidLoad. So I did. I moved all view construction out of init and into loadView, and fired up the fetchedResultsController in viewDidLoad.
The app still crashes in the same spot:
self.tableView.tableHeaderView = searchBar
With the same error as noted above.
Thanks!
I would say you should use the loading model apple suggests. This is some of the code generated in xCode when you create a new UIViewController subclass (abridged to fit):
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
-(void)loadView{
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
-(void)viewDidLoad{
[super viewDidLoad];
}
*/
Based on just this, Since you are creating your view programmatically, I would say that most of what you have in your init method belongs in the loadView method.
So I found the answer, and it's not something anyone would have guessed without seeing a lot more code. But, hopefully this answer will help other people who might run into the same thing.
In what has become my loadView method (but was originally in my init method, and still is in the code above), I had the following lines:
UISegmentedControl* sc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Alpha",#"Edit", #"View", nil]];
[sc addTarget:self action:#selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[sc setWidth:55.0 forSegmentAtIndex:0];
[sc setWidth:55.0 forSegmentAtIndex:1];
[sc setWidth:55.0 forSegmentAtIndex:2];
sc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:#"viewSortOrder"];
sc.segmentedControlStyle=UISegmentedControlStyleBar;
segmentedControlValueChanged: would set a string var (and store the index of the selected button in user defaults). The string var was later used to determine sort order when firing up my fetchedresultscontroller.
Prior to IOS 5, when I set sc.selectedSegmentIndex, the method identified in the selector would get fired, and everything would get set up properly. In IOS 5, this is no longer the case, and the string var (setting a sort order) would not get set.
What's interesting is that upon execution this line:
self.tableView.tableHeaderView = searchBar;
would cause the fetchedReultsController to be accessed and, by accessing it, it would be initialized. When it went to be initialized, it would set the sort order and include a null value for the field name, hence the error message, which now makes sense.
So the root cause of the problem was that prior to IOS 5, setting the selectedSegmentIndex on a UISegmentedControl would fire the action associated with the control. Starting with IOS 5, this is no longer the case.
The original cause of the problem, which several people helped me resolve, was that I was not following the viewController load process properly. I am not using NIB files. Apple says that if you dont use a nib file, you have to define loadView to set up the view. If loadView is not defined, self.view is set internally, and viewDidLoad is called. The reason the app crashed at the first reference of self.view, then, is that I had logic in viewDidLoad to set up the fetchedResultsController. When I moved the view setup logic into loadView, references to self.view no longer started up the fetchedresultscontroller.
Been thinking about and looking for information on this for a while, but it might be any number of things. Perhaps the self.view gets called before the loadView somewhere, maybe it's because initWithNibName triggers something you don't want, maybe it's because your view is smaller than the screen/window, which is not advised, maybe the new iOS has a bug in it... With so little of your code, no way to debug and an incomplete error message, I can't see what causes it.
However, I might have a workaround:
How about removing that loadView override so that the viewController makes its own view, then add your mainView to that default view?
The exception says you are adding a nil to an array. I'm think tableViewHeader could be considered an objectAtIndex:0 since it's the first of an array of rows. Try putting this line above it :
NSLog(#"searchBar = %#",searchBar;
self.tableView.tableHeaderView = searchBar;
Edit(1):
Then try these:
NSLog(#"self = %#",self);
NSLog(#"self.tableView = %#",self.tableView);
NSLog(#"self.tableView.tableHeaderView = %#",self.tableView.tableHeaderView);
self.tableView.tableHeaderView = searchBar;
Edit(2):
if ([self.tableView respondsToSelector:#selector(setTableHeaderView:)]){
NSLog(#"tableView does respond");
[self.tableView setTableHeaderView:nil];
}
I don't think this will fix the problem, but it should help hunt down the problem.

Where's the memory leak?

I'm adding a custom view to a tableHeaderView with the following code:
imageButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
imageButton.frame = CGRectMake(120, 12, 64, 64);
imageButton.titleLabel.font = [UIFont systemFontOfSize:10];
imageButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
imageButton.titleLabel.textAlignment = UITextAlignmentCenter;
[imageButton setTitle:NSLocalizedString(#"Choose\nPhoto", #"Choose\nPhoto") forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(photoButtonPressed) forControlEvents:UIControlEventTouchUpInside];
// add existing image, if any, to button
if (child.thumbnailImage != nil) {
[imageButton setBackgroundImage:child.thumbnailImage forState:UIControlStateNormal];
}
// add button to view
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(22, 12, 70, 70)];
[headerView addSubview:imageButton];
// add view to table header
self.tableView.tableHeaderView = headerView;
The memory leak is showing on the alloc line above for the headerView UIView.
I'm declaring the UIView and the UIButton in the header file and releasing it in ViewDidUnload and dealloc, so I'm not sure what I'm doing wrong.
Also, this is only showing up on the device and not the simulator (just thought I'd mention).
Any help would be appreciated.
Thanks,
Rod
You're using the headerView setter method (self.headerView) so you need to release the UIView instance you assign to the headerView property, either using release or autorelease.
e.g.
UIView* newHeaderView = [[UIView alloc] initWithFrame...];
self.headerView = newHeaderView;
[newHeaderView release];
or
self.headerView = [[[UIView alloc] initWithFrame:...] autorelease];
The reason is because the headerView setter method automatically retains the object assigned to it.
Alternatively, you can set the headerView instance variable directly, without using the property setter method:
[headerView release]; // release any existing object
headerView = [[UIView alloc] initWithFrame:...];