Why is my NSMutableArray "half dead" in cellForRowAtIndexPath? - iphone

In rootViewController.h i have a property NSMutableArray mainList:
#interface RootViewController : UITableViewController {
NSMutableArray *mainList;
}
#property (nonatomic, retain) NSMutableArray *mainList;
#property (nonatomic, retain) IBOutlet DetailsViewController *detailsController;
In the m file, when loading, the following works just fine:
- (void)viewDidLoad
{
self.mainList = [[NSArray alloc] initWithObjects:#"Country1", #"Contry2", nil];
}
Populating the array by a class also works fine (on first load):
innehall *myInnehall= [[innehall alloc] init];
[myInnehall fillMain];
self.mainList = myInnehall.theMainList;
[myInnehall release];
(the debugger shows data to be correct in the array)
When scrolling, the app crasches at setting the label of the cell:
- (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];
}
cell.textLabel.text = [self.mainList objectAtIndex: [indexPath row]];
return cell;
}
In the debugger, the Array is only populated 1-9 instead of up to 19. 10-19 containing strange objects. What can be eating my Array??

First of all, your NSMutableArray property must be initialized properly, like:
self.mainList = [[NSMutableArray alloc] init];
(Not as a NSArray)
Then, you are making your property mainList point to myInnehall.theMainList and you are releasing it afterwards, that is what is causing the crash.
Try just add the myInnehall items to your mainList
[self.mainList addObjectsFromArray:myInnehall.theMainList];

Try to change
self.mainList = myInnehall.theMainList;
to
self.mainList = [myInnehall.theMainList copy];
Can you also put NSLog([self.mainList description]) in your cellForRowAtIndexPath and post the result?
PS: You have NSMutableArray in property declaration and you initialize it as NSArray?

Related

Populating UITableView with NSArray in iOS 7

A lot of the methods have deprecated in iOS 7 in order to set font, textLabel, and color for UITableView cells. I'm also just having a difficult time populating the view with these values. Here's a snippet of my code:
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSArray* jobs = [json objectForKey:#"results"];
for(NSDictionary *jobsInfo in jobs) {
JobInfo *jobby = [[JobInfo alloc] init];
jobby.city = jobsInfo[#"city"];
jobby.company = jobsInfo[#"company"];
jobby.url = jobsInfo[#"url"];
jobby.title = jobsInfo[#"jobtitle"];
jobby.snippet = jobsInfo[#"snippet"];
jobby.state = jobsInfo[#"state"];
jobby.time = jobsInfo[#"date"];
jobsArray = [jobsInfo objectForKey:#"results"];
}
}
I am looping through an array of dictionaries from a GET request and parsed. I am now attempting to fill my UITableView with the following code:
-
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [jobsArray count];
}
- (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];
}
NSDictionary *jobsDic = [jobsArray objectAtIndex:indexPath.row];
[cell.textLabel setText:[jobsDic objectForKey:#"jobtitle"]];
return cell;
}
Also, I have declared this is in my .h file:
NSArray *jobsDic;
Any ideas on what I'm doing wrong? Is this an iOS 7 problem?
It seems that you reinitialize jobsarray at the end of the forin loop.
You didn't mean ?
NSArray* jobs = [json objectForKey:#"results"];
NSMutableArray *jobsTemp = [[NSMutableArray alloc] initWithCapacity:jobs.count];
for(NSDictionary *jobsInfo in jobs) {
JobInfo *jobby = [[JobInfo alloc] init];
jobby.city = jobsInfo[#"city"];
jobby.company = jobsInfo[#"company"];
jobby.url = jobsInfo[#"url"];
jobby.title = jobsInfo[#"jobtitle"];
jobby.snippet = jobsInfo[#"snippet"];
jobby.state = jobsInfo[#"state"];
jobby.time = jobsInfo[#"date"];
[jobsTemp addObject:jobby];
}
self.jobsArray = jobsTemp; //set #property (nonatomic, copy) NSArray *jobsArray; in the .h
[self.tableView reloadData]; //optional only if the data is loaded after the view
In the cell for row method :
- (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];
}
JobInfo *job = self.jobsArray[indexPath.row];
cell.textLabel.text = job.title;
return cell;
}
And don't forget :
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.jobsArray.count;
}
Update - an user suggested an edit :
It's true that count isn't a NSArray property. But as Objective-C lets us use a shortcut notation for calling method with a dot, this code works. You have to know limitation of this : if you use a NSArray subclass with a count property with a custom getter this could have side effects #property (nonatomic, strong, getter=myCustomCount) NSUInteger count. As I think code readability is to me one of most important things I prefer to use dot notation. I think Apple SHOULD implement count as readonly property so I use it this way (but it's my point of view). So for those which don't agree with me just read return [self.jobsArray count]; in the tableView:numberOfRowsInSection: method.
Change the declaration of jobsArray from NSArray to NSMutableArray.
Add an initialization at the beginning point of fetchedData method like follows.
if(!jobsArray) {
jobsArray = [NSMutableArray array];
}
else {
[jobsArray removeAllObjects];
}
Remove the following line.
jobsArray = [jobsInfo objectForKey:#"results"];
Instead of that, add the initialized object to the array at the end of for loop.
[jobsArray addObject:jobby];
Add a [tableView reloadData]; at the end of your *-(void)fetchedData:(NSData )responseData; method implementation.
Initially when you are loading the view, tableView will get populated. After you received the data, tableView will not be known that it is received.
Everything else seems good. Hope rest will work fine.

Uitableview displaying objects

I want to display the values of a NSMutableArray in a UITableView. In the NSMutableArray are values of objects. But the UITableView doesn't display anything. If I use a normal NSArray with static values it works well.
So this is my code:
This is my object
#interface Getraenke_Object : NSObject {
NSString *name;
}
my NSMutableArray
NSMutableArray *getraenkeArray;
here is where I get the values into the array:
for(int i = 0; i < [getraenkeArray count]; i++)
{
[_produktName addObject:[[getraenkeArray objectAtIndex:i]getName]];
NSLog(#"test: %#",_produktName);
}
and that is how I try to display it in the UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ProduktCell";
ProduktTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ProduktTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
int row = [indexPath row];
cell.produktName.text = _produktName [row];
return cell;
}
Just make your getraenkeArray as member and:
cell.produktName.text = [[getraenkeArray objectAtIndex:indexPath.row] getName];
it seems like you never allocated the NSMutableArray. you are missing:
_produktName = [NSMutableArray array];
and that's why the addObject is being sent to nil..

avoid auto release of NSMutable array..... iphone app

I have a NSmutablearray
after i read datas from it, i cant read the same data(index) again
Error:
"EXC_BAD_ACCESS"
in interface
NSMutableArray *ticketList;
#property (nonatomic, retain) NSMutableArray *ticketList;
assigning value
self.ticketList = [NSMutableArray arrayWithArray:[results objectForKey:#"tickets"]];
reading value
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ticketCell";
ticketCell *cell = (ticketCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[self.cellNib instantiateWithOwner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
else {
// Nothing to do here. Because in either way we change the values of the cell later.
}
cell.useDarkBackground = (indexPath.row % 2 == 0);
// Configure the data for the cell.
int rowID = indexPath.row;
NSDictionary *currentTicket = [ticketList objectAtIndex:(int)(indexPath.row)];
NSString *tikid = [currentTicket objectForKey:#"number"];
cell.ticketID = [currentTicket objectForKey:#"number"];
cell.ticketStatus = [currentTicket objectForKey:#"status"];
cell.ticketOpenDate = [currentTicket objectForKey:#"oDate"];
cell.ticketEndDate = [currentTicket objectForKey:#"eDate"];
cell.ticketCategory = [currentTicket objectForKey:#"category"];
cell.ticketPriority = [currentTicket objectForKey:#"priority"];
cell.ticketInfo = [currentTicket objectForKey:#"info"];
return cell;
}
You have to alloc array properly:
ticketList = [[NSMutableArray alloc] initWithArray:[results objectForKey:#"tickets"]];
And also maybe try to alloc currentTicket:
NSDictionary *currentTicket = [[NSDictionary alloc] initWithDictionary:[ticketList objectAtIndex:indexPath.row]];
Sounds like somewhere you're doing something like this:
[currentTicket release];
If so, don't. The currentTicket pointer doesn't belong to you.
use this
ticketList = [[NSMutableArray alloc]initWithArray:[results objectForKey:#"tickets"]];
instead of
self.ticketList = [NSMutableArray arrayWithArray:[results objectForKey:#"tickets"]];
use this
NSDictionary *currentTicket = [ticketList objectAtIndex:indexPath.row];
instead of
NSDictionary *currentTicket = [ticketList objectAtIndex:(int)(indexPath.row)];

Cannot access NSMutableDictionary outside method where values are passed into it

I am trying to set up Index and sections for my uitableview, which I have managed to do.. However now that I am trying to pass my NSDictionary values over to my uitableviewcell my app is crashing when I try to access the NSDictionary outside of the method that I passed the values to it from.
I am thinking that maybe I am not passing the values in correctly or something along the line of that, but I simply cannot figure out why its going this...
Heres my code...
.h
#interface VehicleResultViewController : UITableViewController <NSXMLParserDelegate> {
//......
//Indexed tableview stuff
NSArray *sortedArray;
NSMutableDictionary *arraysByLetter;
NSMutableArray *sectionLetters;
}
//.....
//Indexed tableview stuff
#property (nonatomic, retain) IBOutlet NSArray *sortedArray;
#property (nonatomic, retain) IBOutlet NSMutableDictionary *arraysByLetter;
#property (nonatomic, retain) IBOutlet NSMutableArray *sectionLetters;
//....
.m
//...
//This is where I try to access the NSDictionary to pass it to my uitableviewcells
- (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];
}
cell.accessoryType = UITableViewCellAccessoryNone; //make sure their are no tickes in the tableview
cell.selectionStyle = UITableViewCellSelectionStyleNone; // no blue selection
// Configure the cell...
NSString *value = [self.arraysByLetter objectForKey:[[self.arraysByLetter allKeys] objectAtIndex:indexPath.row]];
cell.textLabel.text = key;
NSLog(#"%#",arraysByLetter);
return cell;
}
//This is where I set NSDictionary
//method to sort array and split for use with uitableview Index
- (IBAction)startSortingTheArray:(NSMutableArray *)arrayData
{
//Sort incoming array alphabetically
sortedArray = [arrayData sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
//NSLog(#"%#",sortedArray);
// Dictionary will hold our sub-arrays
arraysByLetter = [NSMutableDictionary dictionary];
sectionLetters = [[NSMutableArray alloc] init];
// Iterate over all the values in our sorted array
for (NSString *value in sortedArray) {
// Get the first letter and its associated array from the dictionary.
// If the dictionary does not exist create one and associate it with the letter.
NSString *firstLetter = [value substringWithRange:NSMakeRange(0, 1)];
NSMutableArray *arrayForLetter = [arraysByLetter objectForKey:firstLetter];
if (arrayForLetter == nil) {
arrayForLetter = [NSMutableArray array];
[arraysByLetter setObject:arrayForLetter forKey:firstLetter];
[sectionLetters addObject:firstLetter]; // This will be used to set index and section titles
}
// Add the value to the array for this letter
[arrayForLetter addObject:value];
}
// arraysByLetter will contain the result you expect
NSLog(#"Dictionary: %#", arraysByLetter); //This prints what is currently in the NSDictionary
//Reloads data in table
[self.tableView reloadData];
}
.output of checking the Dictionary with NSLog in the last method above
Dictionary: {
H = (
Honda,
Honda,
Honda,
Honda,
Honda,
Honda,
Honda
);
M = (
Mazda,
Mazda,
Mitsubishi,
Mitsubishi,
Mitsubishi,
Mitsubishi,
Mitsubishi,
Mitsubishi
);
N = (
Nissan,
Nissan,
Nissan,
Nissan,
Nissan,
Nissan,
Nissan
);
T = (
Toyota,
Toyota,
Toyota
);
}
I have debugged the two points (where i set the NSdictionary in the method) and (where I access the NSDictionary in cellforrowatindexpath) and it is defiantly set before I even try to use it.
Any help would be greatly appreciated..
//Indexed tableview stuff
#property (nonatomic, retain) NSArray *sortedArray;
#property (nonatomic, retain) NSMutableDictionary *arraysByLetter;
#property (nonatomic, retain) NSMutableArray *sectionLetters;
REMOVE IBOutlet from properties declarations. It's only for Interface Builder controls.
Also correct dictonary allocation - self.arraysByLetter = [NSMutableDictionary dictionary];
The NSMutableDictionary that is allocated is autoreleased, therefore when it is called in the other method the NSAutoreleasePool has been drained, and the NSMutableDictionary has been released. If you want to retain the object using the property you have to do it like this:
self.arraysByLetter = [NSMutableDictionary dictionary];
self will set the dictionary using the setter which is declared as retain, so it will be available when you try to use it later on.
As a note any method that does not start with new or alloc or contains copy must return an autoreleased object, which is your case.

Memory leaks in UITableView with NSMutableArray - How to stop them?

I'm pretty new to objective-c development and I'm to the point I'm beginning to test my application for leaks and patching up anything else I may have done wrong originally. I followed the examples from a book I bought and expanded on those ideas. The Leaks instrument is telling me in my tableView cellForRowAtIndexPath method I have a leak and I'm not sure on how to fix it.
Here is the related .h contents:
#interface NewsListViewController : UITableViewController<UIActionSheetDelegate> {
NSMutableArray *newsList, *account, *playerList;}
And here is the related .m contents:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)ip {
static NSString *CellIdentifier = #"NewsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
[cell autorelease];
}
NSManagedObject *uNews = [newsList objectAtIndex:[ip row]];
NSManagedObjectContext *playerDBContext = [[AppController sharedAppController] managedObjectContext];
NSFetchRequest *playerDBRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *playerDBEntity = [NSEntityDescription entityForName:#"Players"
inManagedObjectContext:playerDBContext];
[playerDBRequest setEntity:playerDBEntity];
NSPredicate *playerDBPredicate = [NSPredicate predicateWithFormat:#"playerID=%#", [uNews valueForKey:#"playerID"]];
[playerDBRequest setPredicate:playerDBPredicate];
NSError *playerDBError;
NSArray *playerDBList = [playerDBContext executeFetchRequest:playerDBRequest error:&playerDBError];
[playerDBRequest release];
playerList = [playerDBList mutableCopy];
NSString *playerInformation;
if (![playerDBList count] == 0) {
NSManagedObject *playerInfo = [playerList objectAtIndex:0];
playerInformation = [NSString stringWithFormat:#"%#, %# (%#-%#)", [playerInfo valueForKey:#"playerLastName"],
[playerInfo valueForKey:#"playerFirstName"],
[playerInfo valueForKey:#"team"],
[playerInfo valueForKey:#"position"]];
} else {
//NSInteger playerID = (NSInteger *)[uNews valueForKey:#"playerID"];
[self addPlayer:(NSInteger *)[uNews valueForKey:#"playerID"]];
NSLog(#"%#", [uNews valueForKey:#"playerID"]);
playerInformation = [uNews valueForKey:#"playerInfo"];
}
cell.textLabel.text = playerInformation;
cell.detailTextLabel.text = [uNews valueForKey:#"news"];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;}
It's throwing the error on the playerList = [playerDBList mutableCopy]; line - Help with how to fix and an explanation would be greatly appreciated. It's probably from reallocating without releasing but when I've tried using [playerList release]; at the end of the cellForRowAtIndexPath my app crashes.
Properties would make this 'just work'.
.h:
...
#property (nonatomic, retain) NSMutableArray *playerList;
...
.m:
#implementation MyClass
#synthesize playerList;
... then in your cellForIndexPath method ...
self.playerList = [[playerDBList mutableCopy] autorelease];
...
- (void)dealloc {
[playerList release];
[super dealloc];
}
A property declared 'retain' will automatically handle memory management when the property is assigned, releasing the old value if it exists before retaining the new one.
The release you tried crashed because the first time through playerlist hasn't ever been assigned and you release a nil. But the second time through it has something and you leak it. Whenever I reuse a retaining pointer like that, I do
if( playerList )
[playerList release];
playerList = [playerDBList mutableCopy];
just to be safe.