Why is my tab bar controller crashing? - objective-c

I'm trying to create a iPhone app that uses a tab bar controller. The first tab works fine.
However, when I click the second tab in the tab bar the whole app crashes. I am trying to implement a table view in the second tab.What could be causing the crash?
Here is my code:
SecondViewController.h
#import <UIKit/UIKit.h>
#class Person;
#interface SecondViewController : UIViewController<UITableViewDelegate, UITableViewDataSource >{
UITableView *tableView;
NSArray *persons;
}
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic,retain ) NSArray *persons;
-(void)initPersons:(NSArray *) array;
#end
SecondViewController.m
#import "SecondViewController.h"
#import "Person.h"
#implementation SecondViewController
#synthesize tableView;
#synthesize persons;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
- (id)init {
if (self = [super initWithNibName:#"SecondViewController" bundle:nil]) {
//self.title = #"Slot";
UIImage* anImage = [UIImage imageNamed:#"cherry.png"];
UITabBarItem* theItem = [[UITabBarItem alloc] initWithTitle:#"table" image:anImage tag:0];
self.tabBarItem = theItem;
[theItem release];
}
return self;
}
-(void)initPersons:(NSArray *) array{
int size = [array count];
int i = 0;
NSMutableArray *aPersons = [NSMutableArray array];
while (i < size) {
Person *person = [[Person alloc]init];
NSString * name =[array objectAtIndex:i];
NSArray *chunks =[name componentsSeparatedByString: #" "];
person.firstName = [chunks objectAtIndex:0];
person.lastName = [chunks objectAtIndex:[chunks count]-1];
[aPersons addObject:person];
[person release];
i++;
}
self.persons=aPersons;
[aPersons release];
}
-(NSArray *)sortArray {
NSSortDescriptor *lastNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"lastName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSSortDescriptor *firstNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"firstName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObjects:lastNameDescriptor,
firstNameDescriptor, nil];
return [persons sortedArrayUsingDescriptors:sortDescriptors];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:
#"Amin Alrusayni", #"Berksan Ates",
#"Becca Bedell", #"Joseph Carioti",
#"Christopher Conry", #"Jeremy Dobbins", #"Timothy Fox",
#"Eric Green", #"Timothy Gruscinski", #"Daniel Gur",
#"Yousef Guzaiz", #"Tyler Herzog", #"Alicia Johnson", #"Scott Kazakis",
#"Nathan Kidwell", #"Dakota Kincer", #"Scott Moore",
#"Sean Reber", #"Michael Romeo", #"Alexander Shibble",
#"Joshua Shipley", #"Mitchell Slemc", #"Thomas Smith",
#"Christopher Wagner", #"Corey Zachrich", #"Ahmed Alalawi",
#"Abdullah Alqahtani", #"Daniel Angelis", #"Brian Bartman",
#"William Haverstock", #"Hui Hong", #"Rong Li",
#"Amitkumar Mali", #"Christian Newman", nil];
[self initPersons:array];
NSArray *sortedArray = [self sortArray];
for (Person *person in sortedArray)
{
NSString *fullName = [[person.firstName stringByAppendingString:#" "] stringByAppendingString:person.lastName];
NSLog(#"%#",fullName);
NSLog(#" ");
}
[super viewDidLoad];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
//commented out this function
/*
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}*/
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[tableView dealloc];
[super dealloc];
}
#pragma mark -
#pragma mark TableView DataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.persons count];
}
/*- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [persons objectAtIndex:row];
return cell;
}*/
#end

It would be easier to help if you post your code using markdown formatting as Moshe commented. I did notice a few things with a quick scan. I also can't tell from this what you have created in interface builder and if the UITabBarController and all outlets are properly configured. You may have other things going on besides the following but here's a start.
Make sure you release anything you retain. for example, in viewDidLoad you allocate array and never release it.
Similarly, don't release things you haven't retained. In initPersons you create the mutable array aPersons using an array constructor that returns an autoreleased object. You then have [aPersons release]. This will cause a crash b/c you are releasing an object you haven't retained.
Clean up properly in viewDidUnload and dealloc. In both of these you need to release tableView. In dealloc you have [tableView dealloc]. That should be [tableView release]
Your data source creation is overly complicated but I do see a clear problem. You are setting self.persons in initPersons which is fine. You then sort the array and store it in sortedArray. Assuming your intent is to keep the array sorted, you are currently discarding the sorted array. self.persons remains the unsorted version. While this is not a crash, I doubt this was your intent.
You need to implement tableView:cellForRowAtIndexPath: This will absolutely cause a crash if missing. That's assuming you have the tableView delegate and dataSource configured correctly. You should add the protocols UITableViewDataSource and UITableViewDelegate to the interface definition for SecondViewController so you'll get the proper compiler warnings regarding implementing required protocol methods.

– tableView:cellForRowAtIndexPath: is required method of UITableviewDatasource protocol.so enable it.be happy
if u dont want any implementaion in it then just leave it with blank definition.
{
return cell;
}

Related

Loading NSArray UITableViewController

I have a tabbed view controller and I put a table view in it but when I run the program I got this EXC_BAD_ACCESS signal.
The array does not get loaded and produces this error.
Here is my code:
ContactsViewController.h
#import <UIKit/UIKit.h>
#interface ContactsViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate>
#property (nonatomic ,retain) NSArray *items;
#end
ContactsViewController.m
#import "ContactsViewController.h"
#interface ContactsViewController ()
#end
#implementation ContactsViewController
#synthesize items;
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", "item3", nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
return cell;
}
#end
The problem with your code lies (as the debugger tells you) in the following line:
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", "item3", nil];
Take a closer look at "item3". There is no # sign in front of it so it is not an NSString object but a plain old C string. You can only put objects into an NSArray.
Change it to
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", #"item3", nil];
or even simpler
item = #[#"item1", #"item2", #"item3"];
Without the rest of the code it's not possible to say for sure what the problem is, but you should access the variable through the property in the following way.
self.items = ...
Also consider using the shorthand array notation, like this.
self.items = #[#"Item 1", #"Item 2", #"Item 3"];
IMO: The only time you should use variables directly is in an overriden property accessory.
Also note that if you do want to use the variable directly you should change the synthesize command to the following #synthesize items = variableName; This puts a name, variableName on the underlying variable used in the property. You can then access the variable without going through the property.

Create an array of custom Object to fill it and take his content to fill a UITableView

I have a Class called Product that contains some propeties, I wanna make that my class will be the base for an list of Product called Products. And this list can access in a UITableView to fill it with the content of Products.
Also, the content of each product will be filled by a web service.
My code is:
#interface Product : NSObject
{
int identifier;
NSString* title;
NSString* quantity;
float price;
UIImage* image;
}
#property (nonatomic, assign) int identifier;
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) NSString* quantity;
#property (nonatomic, assign) float price;
#property (nonatomic, retain) UIImage *image;
-(id)initWithProduct:(int) identifier withTitle:(NSString*)title numberUses:(NSString*)uses withPrice:(float)price withImage:(UIImage*)image;
#end
With his .m
#implementation Product
#synthesize identifier = _identifier;
#synthesize title = _title;
#synthesize price = _price;
#synthesize quantity = _quantity;
#synthesize image = _image;
- (void)dealloc {
NSLog(#"article dealloc \n");
[self.title release];
[self.quantity release];
[self.image release];
[super dealloc];
}
- (id)init {
self = [super init];
if(self) {
self.title = [[[NSMutableString alloc] init] autorelease];
self.identifier = 0;
self.price = 45.0;
self.quantity = [[[NSMutableString alloc] init] autorelease];
self.image = [[[UIImage alloc] init] autorelease];
}
return self;
}
-(id)initWithProduct:(int) inIdentifier withTitle:(NSString*)inTitle numberUses:(NSString*)inQuantity withPrice:(float)inPrice withImage:(UIImage*)inImage
{
self = [super init];
if (self) {
if (title!= nil) {
self.title = inTitle;
}
if (quantity!= nil) {
self.quantity = inTitle;
}
if (image!= nil) {
self.title = inTitle;
}
self.price = inPrice;
self.identifier = inIdentifier;
}
return self;
}
#end
My UITableView header is:
#interface TableView : UIViewController
< UITableViewDataSource
, UITableViewDelegate
>{
NSMutableArray *products;
}
in the m. I have:
EDIT
Now the Title of my cell is shows as (null)
- (void)viewDidLoad
{
[super viewDidLoad];
products = [[NSMutableArray alloc] init];
[products addObject:[[Product alloc] initWithProduct:1 withTitle:#"df" numberUses:#"dsf" withPrice:12.3 withImage:[UIImage imageNamed:#"btn_invite"]]];
[products addObject:[[Product alloc] initWithProduct:1 withTitle:#"2" numberUses:#"dsf" withPrice:12.3 withImage:[UIImage imageNamed:#"btn_invite"]]];
[products addObject:[[Product alloc] initWithProduct:1 withTitle:#"4" numberUses:#"dsf" withPrice:12.3 withImage:[UIImage imageNamed:#"btn_invite"]]];
[products addObject:[[Product alloc] initWithProduct:1 withTitle:#"4" numberUses:#"dsf" withPrice:12.3 withImage:[UIImage imageNamed:#"btn_invite"]]];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Controller *cell = (Controller*)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSString* nameNib = UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM() ? #"Controller" : #"ControllerIph";
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:nameNib owner:self options:nil];
cell = [nib objectAtIndex:0];
}
Product* obj = [products objectAtIndex:indexPath.row];
cell.title.text = [NSString stringWithFormat:#"%#", obj.title];
return cell;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 4;
}
Thanks in advance!
Not an answer to your question, but a few comments regarding your code:
You have 4 memory leaks when you add the 4 new instances of Product to your array. The array retains the objects you add to it, so you should autorelease the Product instances when you add them.
In dealloc you should say self.title = nil instead of [self.title release]. Although your version works, it leaves you with instance variables that contain references to deallocated objects (= dangling pointers). Because this happens in dealloc and the Product object is going away soon, it won't hurt you now, but you will get bitten at some point in the future if you keep to this style.
You don't need to declare the instance variables, and you don't need to synthesize the properties. The compiler does all this for you.
In initWithProduct you must not check things like if (title!= nil) - just say self.title = inTitle straightaway. After all, initWithProduct is an initializer just like init, so title cannot contain anything at this point.
Hope this helps.
EDIT
Actually, the last point is probably why you see "(null)" for the title of your cells. Your check if (title!= nil) is never true because the instance variable title is nil during the initializer, so the assignment self.title = inTitle never happens.
In - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath you do not initialize cells for your TableView. Add the below code to this method before the code that you already have
static NSString *CellIdentifier = #"Cell Identifier";
UITableViewCell *cell;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
and make sure you return cell; at the end of that method
Also make sure you implement the rest of the necessary TableView delegate methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

NSMutableArray gets reset after calling modalViewController

IT IS SOLVED BY #T-X. I've marked the changes with "// SOLUTION" in the code!!!
I have the following issue, if I presentModalViewController, the NSMutableArray "projectsArray" gets reset.
Here is my code:
Here the .h file:
// ViewController.h
#import <UIKit/UIKit.h>
#class AddProject;
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
IBOutlet UITableView *projectsTableView;
UIAlertView *reallyDelete;
BOOL candelButtonClicked;
NSIndexPath *actualIndexPath;
NSMutableArray *projectsArray;
NSInteger ID;
NSString *NAME;
AddProject *addProject;
**ViewController *viewContr; // SOLUTION**
}
- (IBAction)addNewProject:(id)sender;
- (void)addToTableView:(NSString *)projectName;
#property (nonatomic, retain) UIAlertView *reallyDelete;
#property (nonatomic) NSInteger cancelButtonIndex;
#property IBOutlet UITableView *projectsTableView;
#property (nonatomic, retain) AddProject *addProject;
**#property (nonatomic, retain) ViewController *viewContr; // SOLUTION**
#end
Here the .m file:
// ViewController.m
#import "ViewController.h"
#import "CustomTableCell.h"
#import "AddProject.h"
#interface ViewController ()
#end
#implementation ViewController
**#synthesize viewContr; // SOLUTION**
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
projectsArray = [[NSMutableArray alloc] init];
ViewController *element = [[ViewController alloc] init];
element->ID = 1;
element->NAME = #"Demo project";
NSLog(#"viewdidload");
[projectsArray addObject:element];
NSLog(#"1. Array length: %d", [projectsArray count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([projectsArray count] < 1) {
NSLog(#"2. Array length: 0");
return 0;
} else {
NSLog(#"2. Array length: %d", [projectsArray count]);
return [projectsArray count];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *customTableCellIdentifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:customTableCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
ViewController *element = [[ViewController alloc] init];
element = [projectsArray objectAtIndex:indexPath.row];
cell.nameLabel.text = element->NAME;
cell.prepTimeLabel.text = [NSString stringWithFormat:#"%i", element->ID];
NSLog(#"3. Array length: %d", [projectsArray count]);
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
reallyDelete = [[UIAlertView alloc] initWithTitle:#"Deleting" message:#"Do your really want to delete that row?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[reallyDelete show];
actualIndexPath = indexPath;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != [reallyDelete cancelButtonIndex])
{
//If "Yes" clicked delete
[projectsArray removeObjectAtIndex:actualIndexPath.row];
NSLog(#"delete row");
[self.projectsTableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:actualIndexPath, nil] withRowAnimation:YES];
[self.projectsTableView reloadData];
}
}
#pragma mark Edit
- (IBAction)addNewProject:(id)sender {
NSLog(#"4. Array length: %d", [projectsArray count]);
AddProject *addProjectView = [[AddProject alloc] initWithNibName:#"AddProject" bundle:nil];
NSLog(#"4.1 Array length: %d", [projectsArray count]);
addProjectView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
NSLog(#"4.2 Array length: %d", [projectsArray count]);
addProjectView.modalPresentationStyle = UIModalPresentationFormSheet;
NSLog(#"4.3 Array length: %d", [projectsArray count]);
**addProjectView.viewContr = self; // SOLUTION**
[self presentModalViewController:addProjectView animated:YES];
NSLog(#"4.4 Array length: %d", [projectsArray count]);
addProjectView.view.superview.frame = CGRectMake(addProjectView.view.center.x/2.5, addProjectView.view.center.y/3, 800, 600);
NSLog(#"4.5 Array length: %d", [projectsArray count]);
}
- (void)addToTableView:(NSString *)projectName
{
NSLog(#"Project Array: %#", projectsArray);
NSLog(#"addToTableView: %#", projectName);
NSLog(#"5. Array length: %d", [projectsArray count]);
ViewController *element = [[ViewController alloc] init];
element->ID = 2;
element->NAME = projectName;
[projectsArray addObject:element];
NSLog(#"6. Array length: %d", [projectsArray count]);
[self.projectsTableView reloadData];
[self.projectsTableView setNeedsDisplay];
}
- (void)viewDidUnload
{
[self setProjectsTableView:nil];
self.projectsTableView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
Here the .h file of the modal presented view:
// AddProject.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AddProject : UIViewController
{
IBOutlet UITextField *projectName;
ViewController *viewContr;
}
- (IBAction)back:(id)sender;
- (IBAction)next:(id)sender;
#property (nonatomic, retain) ViewController *viewContr;
#end
The .m file of the modal presented file:
// AddProject.m
#import "AddProject.h"
#import "ViewController.h"
#interface AddProject ()
#end
#implementation AddProject
**#synthesize viewContr; // SOLUTION I've mist to synthesize it**
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
**// SOLUTION no new allocation/init of ViewController**
}
- (IBAction)back:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)next:(id)sender
{
[viewContr addToTableView:projectName.text];
[viewContr.projectsTableView reloadData];
[viewContr.projectsTableView setNeedsDisplay];
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidUnload
{
projectName = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
I've tested the code by using breakpoints and I could locate the issue when the AddProject class "viewDidLoad" starts loading. But I don't know why it resets the array.
You navigate from ViewController Screen to Add project Screen. In Add Project Screen, you add a new project and want to delegate back this information to the ViewController Screen where you came from.
Now, you are trying to have an instance of ViewController in your AddProject, which you newly instantiate in viewDidLoad method and when project is added, you provide the information to it by calling - (void)addToTableView:(NSString *)projectName on it.
This doesn't work because - The instance of ViewController you allocated is a new instance and not the one you navigated from. When you do alloc + init you get a new object. Thus, although you get addToTableView: called on your ViewController, it doesn't work because the ViewController instance that is receiving this message is a new instance and not the one you navigated from.
Thus, all you needed to do is - have a ViewController property in your Add Project, which you would set before navigating. And then you can send message to this property - As explained in this transcript.
I would like to say - However this thing works, it provides tight coupling between the two controllers.
Ideally, This cane be done by making ViewController (where you go navigate from) as a delegate of Add Project (where you navigate to). And then Add Project can delegate the added project to it's delegate by something like - (void) addProjectController:(AddProjectViewController *) controller didAddProject:(NSString *) projectName. This provides lose coupling between the two view controllers. To understand delegation concept, refer to Delegates and Datasources. Alternatively, search for other Stack overflow posts.
You should allocate projectsArray in the init method of the view controller, not in in viewDidLoad. projectsArray = [[NSMutableArray alloc] init]; allocated a new empty array tach time the view is loaded.
Okay my issue is fixed through the stackoverflow chat. The tips for the final solution came from user #T-X. I've added the tips in the code in my question. They are marked with "// SOLUTION".
In Add Project View controller's viewDidLoad method, remove the line
viewContr = [[ViewController alloc] init];. Now make the variable
viewContr in your ViewController class a property. Also synthesize
it. And finally in the - (IBAction)addNewProject:(id)sender method,
before presenting the controller, do - addProjectView.viewContr = self;. It will solve your issue.
(quote from chat.stackoverflow | #T-X)

Possible memory leak in UIViewController with UITableView

I have a UIViewController thats presented modally. When I watch the memory allocations Instrument, the memory usage increases when the view is presented, but when it's exited the memory isn't released.
If I keep opening and closing the view, the memory just keeps getting higher.
Instruments doesn't report a memory leak!
What could be causing this? The View Controller code is below (I've skipped the didSelectRow code).
Dealloc is always called.
EDIT - I am using ARC
.h
#import <UIKit/UIKit.h>
#class OutlineTextUILabel;
#interface StoreViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
int starCount;
NSMutableArray *_singleUseArray;
NSMutableArray *_fullUseArray;
}
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl;
- (IBAction)exitBtnPressed:(id)sender;
.m
#import "StoreViewController.h"
#import "NSUserDefaults+MPSecureUserDefaults.h"
#import "PowerUpCell.h"
#import "OutlineTextUILabel.h"
#import "PowerUpSingleton.h"
#import "PowerUp.h"
#define kPrefsNumberOfStars #"numberOfStars"
#interface StoreViewController ()
#end
#implementation StoreViewController
#synthesize tableView = _tableView;
#synthesize starCountLbl;
#pragma mark View Methods
- (void)viewDidLoad
{
[super viewDidLoad];
// Display star count
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
BOOL valid = NO;
starCount = [prefs secureIntegerForKey:kPrefsNumberOfStars valid:&valid];
if (!valid) {
NSLog(#"Stars Tampered With!");
self.starCountLbl.text = #"Err";
} else {
self.starCountLbl.text = [NSString stringWithFormat:#"%d",starCount];
}
// Tableview setup
CGRect frame2 = CGRectMake(0, 0, 320, 40);
UIView *footer = [[UIView alloc] initWithFrame:frame2];
footer.backgroundColor = [UIColor clearColor];
self.tableView.tableFooterView = footer;
self.tableView.opaque = NO;
self.tableView.backgroundView = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
if (![[PowerUpSingleton sharedList] refreshArray]) {
NSLog(#"Error, %s",__FUNCTION__);
} else {
[self performSelectorOnMainThread:#selector(workOutSingleUseToDisplay) withObject:nil waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(workOutFullUseToDisplay) withObject:nil waitUntilDone:YES];
[self.tableView reloadData];
}
}
- (void)workOutSingleUseToDisplay
{
_singleUseArray = [[NSMutableArray alloc] init];
for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) {
if (!pu.fullUnlock) {
[_singleUseArray addObject:pu];
}
}
}
- (void)workOutFullUseToDisplay
{
_fullUseArray = [[NSMutableArray alloc] init];
for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) {
if (pu.prefFullName != nil) {
[_fullUseArray addObject:pu];
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
- (void)viewDidUnload {
[self setTableView:nil];
[self setStarCountLbl:nil];
[super viewDidUnload];
}
#pragma mark TableView Setup Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == 0) {
return #"Single Use";
} else if (section == 1) {
return #"Use forever";
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return [_singleUseArray count];
} else if (section == 1) {
return [_fullUseArray count];
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier;
if (indexPath.section == 0) {
cellIdentifier = #"powerUpCellSingleUse";
} else if (indexPath.section == 1) {
cellIdentifier = #"powerUpCell";
}
PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (indexPath.section == 0) {
PowerUp *tmpPU = [_singleUseArray objectAtIndex:indexPath.row];
cell.descriptionLbl.text = tmpPU.displayName;
int cost = tmpPU.costSingle;
cell.costLbl.text = [NSString stringWithFormat:#"%d",cost];
if (cost > starCount) {
cell.costLbl.textColor = [UIColor redColor];
} else {
cell.costLbl.textColor = [UIColor blueColor];
}
int howMany = tmpPU.numberOwned;
cell.howManyLbl.text = [NSString stringWithFormat:#"%d",howMany];
} else if (indexPath.section == 1) {
PowerUp *tmpPU = [_fullUseArray objectAtIndex:indexPath.row];
cell.descriptionLbl.text = tmpPU.displayName;
int cost = tmpPU.costFull;
cell.costLbl.text = [NSString stringWithFormat:#"%d",cost];
if (cost > starCount) {
cell.costLbl.textColor = [UIColor redColor];
} else {
cell.costLbl.textColor = [UIColor blueColor];
}
if (tmpPU.fullUnlock) {
cell.costLbl.textColor = [UIColor greenColor];
cell.costLbl.text = #"---";
}
}
return cell;
}
#pragma mark -
- (IBAction)exitBtnPressed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (void)dealloc
{
NSLog(#"%s",__FUNCTION__);
self.tableView = nil;
self.starCountLbl = nil;
}
#end
EDIT -------------
Something seems not to be right. I've added an NSLog to the cell alloc, and it's never called, even though the cells are created!
PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSLog(#"new cell");
cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
EDIT July 1st ------
I've added a Navigation controller and now use push instead of modal and this problem is still here.
I've taken heap shots with Instruments by moving back and forward between views a few times and it seems maybe the cells are still hanging around, as this screenshot shows gesture recogniser still around from a previous load of the view.
It is because you used your IBOutlets as weak, instead of using strong.
I actually believe this is a flaw in the XCode environment, as it should warn you of this kind of behavior.
As a best practice, I would suggest letting XCode generate the IBOutlets by dragging the views to the code in the Interface Builder, to avoid such annoying pitfalls.
Looks like you've already found some ways around this, but just in case this helps:
1) Make sure you haven't got Zombies turned on while you're debugging, as this causes objects to hang around after you think they should be dealloc-ed (Edit scheme -> Run -> Diagnostics).
2) You're using ARC and so I assume storyboards or at least prototype UITableView cells in your storyboard/NIB? If so, then the reason your NSLog() below never gets called is because the dequeueReusableCellWithIdentifier call knows to create cells from these prototype cells via the defined cellIdentifier. Pretty handy.
PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSLog(#"new cell");
cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
You have to rely on the UITableView to manage this cache of UITableViewCells, and release them appropriately. So it's possible they are just hanging around because your UITableView isn't being released (though I think you're saying it is).
I'm not sure if I got the anwer, but there's something strange in your code :
your are using weak properties :
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl;
But according to the doc (search "weak"), weak propoerty is quite similar to assign.
In you dealloc, you have
self.tableView = nil;
self.starCountLbl = nil;
I'm pretty sure that the generated setter of these properties doesn't release them at all !
But if you declare your properties like :
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) IBOutlet OutlineTextUILabel *starCountLbl;
the generated setter would be like
(void)setTableView(UITableView *)newTableView {
[tableView release];
if(newTableView != nil)
tableView = [newTableView retain];
}
And your properties would be released.
[EDIT]
In your viewWillAppear method, have you printed out to see how often you move through your else clause. To me it seems that you call your workOutSingleUseToDisplay and workOutFullUseToDisplay methods. Each time you call those, you are allocating _singleUseArray and _fullUseArray. Just because you move in and out of a view, does not mean it calls the dealloc, or that it will auto-release your current arrays. What I think you are seeing is that when you move out of your view, it does not release those two arrays, but does try to reallocate them.
[ORIGINAL]
Well, in your viewDidLoad, you perform an alloc. In your dealloc, I don't see a [footer release]. This may be your leak!!! Nor do I see releasing of your _singleUseArray or _fullUseArray arrays
At least, use the Leaks instrument to monitor memory leaks. The Allocations instrument won't actually show the memory leaks. If you run Analyze, you will see the lines that are potentially causing the leaks.
This is your code:
PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSLog(#"new cell");
cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
You see, cell is not going to be nil... This is stated in the API doc for dequeueReusableCellWithIdentifier::
Return Value
A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
Anyway, if there are leaks, maybe they are very much caused by:
_singleUseArray = [[NSMutableArray alloc] init];
and
_fullUseArray = [[NSMutableArray alloc] init];
When you declared
NSMutableArray *_singleUseArray;
NSMutableArray *_fullUseArray;
I think, by default both were assigned with a __strong qualifier. I'm not really sure but this could be the real cause of the problem. How about declaring this instead?
NSMutableArray * __weak _singleUseArray;
NSMutableArray * __weak _fullUseArray;
Also, before declaring
_singleUseArray = [[NSMutableArray alloc] init];
and
_fullUseArray = [[NSMutableArray alloc] init];
how about assigning it first to nil to remove the previous reference?
_singleUseArray = nil;
_singleUseArray = [[NSMutableArray alloc] init];
and
_fulUseArray = nil;
_fullUseArray = [[NSMutableArray alloc] init];

Failed to call designated initializer on NSManagedObject class

Another newbie question, just when I thought I was beginning to get a very
small handle on ios programming. Ugh! I'm following a tutoria from the
appcodeblog.com where I'm building a simple tab bar application utilizing
core data to enter, display, and search vacation destinations. I've worked
through the tutorial and have a working app, but I notice when I select the
"Show Destinations" tab I get the following error. The app seems to continue
working, but the error is logged to the console. I'm trying to debug the
issue and understand exactly what is happening, but I just don't quite
understand what is wrong. I "think" I have an issue with my
ShowDestinations.xib file where I've incorrectly hooked up my objects within
the xib. Any help is MUCH appreciated. Thanks in advance for your help and
time.
Here's the error, "CoreDataTabBarTutorial[1262:207] Failed to call designated
initializer on NSManagedObject class 'Destination'.
I'm not sure what code to provide so I've started out by showing my header
and implementation files ShowDistinationsViewController.h and
ShowDestinationsViewController.m
ShowDistinationsViewController.h
#import <UIKit/UIKit.h>
#interface SearchDestinationsViewController : UIViewController {
UISearchBar *destinationSearchBar;
UITableView *searchTableView;
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
NSArray *fetchedObjects;
}
#property (nonatomic, retain) IBOutlet UISearchBar *destinationSearchBar;
#property (nonatomic, retain) IBOutlet UITableView *searchTableView;
#property (nonatomic, retain) IBOutlet NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) IBOutlet NSManagedObjectContext *managedObjectContext;
#end
ShowDestinationsViewController.m
#import "ShowDestinationsViewController.h"
#import "Destination.h"
#implementation ShowDestinationsViewController
#synthesize destinationsTableView;
#synthesize destinationsArray;
#synthesize fetchedResultsController;
#synthesize managedObjectContext;
// Not sure where the following code came from so I commented it out!!! It didn't seem to break anything when I commented it out
//- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
//{
// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// if (self) {
// // Custom initialization
// }
// return self;
//}
- (void)dealloc
{
[destinationsArray release];
[destinationsTableView release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// 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];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark -
#pragma Data Fetch from Core Data
- (void) viewWillAppear:(BOOL)animated
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Destination" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil)
{
// Handle the error.
NSLog(#"mutableFetchResults == nil");
}
[self setDestinationsArray:mutableFetchResults];
[request release];
[destinationsTableView reloadData];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [destinationsArray count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
Destination *destination = [[Destination alloc] init];
destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
cell.textLabel.text = destination.name;
[destination release];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
#end
The problem seems to lie in
Destination *destination = [[Destination alloc] init];
destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
[destination release];
The first line is unnecessary: In Objective-C, Destination* is a pointer to the object, not the real object. The Destination object you want is presumably already in the array. So you don't have to create an object to point to, in the line [[Destination alloc] init], which is gone immediately at the next line. What's going on was
[[Destination alloc] init] creates an object a, destination points to a. a is retained by you.
(Destination *)[destinationsArray objectAtIndex:indexPath.row] gives you another object b, which is not retained by you. destination now points to b. No one holds a any longer.
release is sent to the object pointed to by destination, i.e., to b. This is against the retain-release rule; you should release a, not b!
So, instead, just do
Destination *destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
without the release part.
As an advice: always run Analyze (which is available below the Build menu) when you build your project. The analyzer is designed to catch common types of errors, including yours. Correct your code so that all the analyzer warnings go away; you should always regard the analyzer warning as an error on your part.