Possible memory leak in UIViewController with UITableView - objective-c

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];

Related

Segue doesn't work because of searchbar

I have made a table where depending on which cell you click on you will be sent into a new scene (detailviewcontroller). For example if you click on the cell with the text Thailand you will be sent to ThailandDetailViewController (scene). Everything works until you use the searchbar (look under - (void)tableView).
-When some countries get outfiltered (because of the searchfunction) the reaming countries will go higher in the table and acquire a lower count. Which leads to that they will lead to the wrong detailviewcontroller (scene).
A friend of mine said to me that I should use objectAtIndex within my array, didnt really catch what he meant with that.. And make a switch on the cell.textLabel.text (didnt really follow him)
Here is my .m file:
- (void)viewDidLoad
{
[super viewDidLoad];
self.mySearchBar.delegate = self;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
totalStrings = [[NSMutableArray alloc] initWithObjects:#"America",#"Austria",#"Canada",#"France",#"Germany",#"Greece",#"Malaysia",#"Mexico",#"Netherlands",#"Poland",#"Russia",#"Singapore",#"Thailand",#"Ukraine", nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: [self performSegueWithIdentifier:#"Segue0" sender:self];
break;
case 1: [self performSegueWithIdentifier:#"Segue1" sender:self];
break;
//and so on
default: break;
}
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if(searchText.length == 0){
isFiltered = NO;
}
else
{
isFiltered = YES;
filteredStrings = [[NSMutableArray alloc]init];
for (NSString *str in totalStrings){
NSRange stringRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(stringRange.location !=NSNotFound) {
[filteredStrings addObject:str];
}
}
}
[self.myTableView reloadData];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Cellidentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Cellidentifier];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Cellidentifier];
}
if (!isFiltered) {
cell.textLabel.text = [totalStrings objectAtIndex:indexPath.row];
}
else //if it's filtered
{
cell.textLabel.text = [filteredStrings objectAtIndex:indexPath.row];
}
return cell;
}
Big thank you in beforehand!!
Well, you can have a custom class to store the area and the segue index like this:
#interface SegueVO : NSObject
#property (nonatomic, strong) NSString *area;
#property int segueIndex;
-(id)initWithArea:(NSString *)area andSegueIndex:(int)index;
#end
#implementation SegueVO
-(id)initWithArea:(NSString *)area andSegueIndex:(int)index
{
self = [super init];
if (self)
{
self.area = area;
self.segueIndex = index;
}
return self;
}
#end
You will then store your ares in the totalStrings array like this:
[[NSMutableArray alloc] initWithObjects:[[SegueVO alloc] initWithArea:#"America" andIndex:0],....
Of course you can create a factory method to cut down on initialisation code.
Now you can work out what segue to activate like this:
NSArray *arrayToUse = totalStrings;
if (isFiltered)
arrayToUse = filteredStrings;
[self performSegueWithIdentifier:[#"Segue"
stringByAppendingString:[NSString stringWithFormat:#"%i",
[arrayToUse[indexPath.row].segueIndex]] sender:self];
Hope this helps.
You could easily solve this problem by storing a custom object in your table's data model instead of an NSString. That object would contain the label to display plus the name of the segue to activate once selected.
It's another question why you'd want a totally different view controller for different data. I suppose these are different kinds of data that need different ways to deal with them.

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

UITableView with UISegmented control and checkmark accessory

I am a newbie in the world of Objective-C and iOS coding so I'll appreciate a little bit of help.
Here is my issue. I've got a UITableView with a UISegmentedControl. This one has 6 different segments which modify the content of the table by modifying an NSMutableArray. I managed to do that so I'm already pretty proud of me but still the newbie curse is back today. I want to implement checkmarks in order to select cells and pass the cells' data to another UITableView. The first issue is that I've got my checkmarks but I click on a different segment the data are updated but the checkmarks from the previous segment remain. How to address this problem.
Second what is the best way to pass data from all of the segment of this UITableView to another tableview by selecting the cells?
Here is my UITableViewController.h
#class MesExercicesViewController;
#protocol MesExercicesViewControllerDelegate <NSObject>
- (void) mesExercicesViewControllerDidCancel:
(MesExercicesViewController *) controller;
- (void) mesExercicesViewControllerDidSave:
(MesExercicesViewController *)controller;
#end
#interface MesExercicesViewController : UITableViewController {
NSMutableArray *exercicesList;
UISegmentedControl *segment;
}
- (IBAction)segmentChange;
#property (nonatomic, retain) IBOutlet UISegmentedControl *segment;
#property (nonatomic, weak) id <MesExercicesViewControllerDelegate> delegate;
- (IBAction)cancel:(id)sender;
- (IBAction)done:(id)sender;
#end
And here is the code of the UISegmentedControl in the UITableViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
exercicesList = [NSMutableArray arrayWithObjects:
#"A",#"A1",#"A2",#"A3",#"A4",#"A5",#"A6",#"A7", nil];
}
- (IBAction)segmentChange {
if (segment.selectedSegmentIndex == 0) {
exercicesList = [NSMutableArray arrayWithObjects:#"A",#"A1",#"A2",#"A3",#"A4",#"A5",#"A6", nil];
[[self tableView]reloadData];
} else if (segment.selectedSegmentIndex == 1) {
exercicesList = [NSMutableArray arrayWithObjects:#"C",#"C1",#"C2",#"C3",#"C4", nil];
[[self tableView] reloadData];
} else if (segment.selectedSegmentIndex == 2) {
exercicesList = [NSMutableArray arrayWithObjects:#"E",#"E1",#"E2",#"E3",#"E4",#"E5",#"E6",#"E7",#"F",#"F1", nil];
[[self tableView] reloadData];
} else if (segment.selectedSegmentIndex == 3) {
exercicesList = [NSMutableArray arrayWithObjects:#"I",#"I1",#"I2",#"I3",#"I4",#"I5",#"I6",#"I7",#"I8",#"J", nil];
[[self tableView] reloadData];
} else if (segment.selectedSegmentIndex == 4) {
exercicesList = [NSMutableArray arrayWithObjects:#"L",#"M",#"M1",#"N",#"N1", nil];
[[self tableView] reloadData];
} else if (segment.selectedSegmentIndex == 5) {
exercicesList = [NSMutableArray arrayWithObjects:#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z", nil];
[[self tableView] reloadData];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//reflect selection in data model
}else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
//reflect the deselection in data model
}
Thank you very much for your help in advance
Firstly, in your cellForRowAtIndexPath set
cell.accessoryType = UITableViewCellAccessoryNone;
this way, whenever you reloadData on your tableView it will get rid of the check marks. You could do this when a new segment is tapped.
2nd Edit
Secondly, set up 2 complimentary methods - in didSelectRow and didDeselectRow. Create a NSMutableArray in your viewDidLoad and then add to it. So your didSelectRow would have:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[mutableArray addObject:[exercisesList objectAtIndex:indexPath.row]];
}
and your didDeselectRow would have
(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[mutableArray removeObject:[exercisesList objectAtIndex:indexPath.row]];
}
and as far as the checkmarks go, have you implemented this?
[[self tableView] setEditing:YES animated:YES];
Then you would use this mutableArray to populate the new table.
1st EDIT
Here is some extra detail for how to set the delegate
So lets call the VC that has your original table - OriginalTableViewController
and the VC with the new table that you want to populate from the mutableArray - NextTableViewController (best to stay away from the word 'new' at the start of any names...)
NextTableViewController.h - right after the # import < UIKit/UIKit.h>
#class NextTableViewController;
#protocol NextTableViewControllerDelegate
-(NSMutableArray*)sendThroughTheMutableArray;
#end
declare your delegate
#property (nonatomic, assign) id delegate;
NextTableViewController.m
#synthesize delegate;
Then, you may be familiar with making calls to self like - [self getThatArray]; its the same with a delegate, but you just make the call to delegate instead of self
This is assuming you've declared the myTablePopulatingArray in your h file:
if(delegate != nil)
{
myTablePopulatingArray = [[NSMutableArray Alloc] initWithArray: [delegate sendThroughTheMutableArray]];
}
Basically to this point we have just set up the delegate. We are saying, this is what I need. Who's up for it? Who will volunteer for this job. We put the if(delegate != nil) as a safety - but you are the one who will make sure there is a delegate, so you probably don't really need it
Now for the delegate itself - you only need 2 things:
in your OriginalTableViewController.h file
#import "NextTableViewController.h"
#class DetailViewController;
OriginalTableViewController
in your OriginalTableViewController.m file you must put the method that you declared earlier
-(NSMutableArray*)sendThroughTheMutableArray
{
return mutableArray;
}
so this mutableArray is now ready to populate your tableView.

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.

Why is my tab bar controller crashing?

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;
}