Cannot access NSMutableDictionary outside method where values are passed into it - iphone

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.

Related

Having problems with Array

SO here's my setup. I have an object called radiostations where I have several strings like callsign, frequency declared and an NSMutableArray called amStationInfo. On my viewcontroller, I access an SQLite database which populates the an array like so...
radiostations.h
#interface radiostations : NSObject {
NSString *format;
NSString *city;
}
#property (nonatomic, retain) NSString *format;
#property (nonatomic, retain) NSString *city;
ViewController.m
radiostations *amStationClass = [[radiostations alloc] init];
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *cityField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 10)];
NSString *formatField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)];
[amStationInfo addObject:amStationClass];
[amStationClass setCity:cityField];
[amStationClass setFormat:formatField];
}
[tabView reloadData];
sqlite3_finalize(statement);
and then I populate a UITableView
NSString *cityValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] city];
NSString *formatValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] format];
cityLabel.text = cityValue;
formatLabel.text = formatValue;
Initially I was dealing with a few Arrays and this worked just fine. I then changed it so that I was only dealing with one array using a class object and now it's not working. I know the SQLite query and what not works so Im not having any problems with that. It seems as though the array does not get populated.
You are changing the properties of the same radiostations object and adding it over and over again to the array. You need to create a new radiostations object for each row from your sqlite database and add this:
while (...) {
// fetch data as before
radiostations *record = [[radiostations alloc] init];
[record setCity: cityField];
[record setFormat: formatField];
[amStationInfo addObject: record];
[record release];
}
If you are using ARC you need to remove the line [record release];, otherwise it is necessary to avoid leaking those objects.
where did you allocate/init your mutablearray?
something like:
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
you need to allocate it once, before to add objects in it

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

iphone listview with several cells

I have been able to make the listview show a single field of data using parts of the code like below.
NSMutableArray *array;
..
..
array = [[NSMutableArray alloc] init];
[array addObject:#"John Doe"];
However I want to keep several fields, like:
Name
ID
Date of Birth
I assume the NSMutableArrary is a NSString but I need something like a struct in C that holds the fields I need.
The ID would be "Hidden" but I need to access it when the user clicks on the line. How I access the ID and the other fields? How do I set this up so the list has the information?
Does anyone have any example code that might explain how to do this?
EDIT #1: Thanks for the comments, but I am too new to iPhone and really need to find example code on how to do this. While the comments make it sound like can do this, I dont know where to start. Can someone post example code for the idea of 3 fields?
EDIT #2: I have tried everything so far, is the the correct way to do this or should I use the ideas below?
Userrec.m
#import "UserRec.h"
#implementation Userrec
#synthesize Name, ID;
-(id)initWithName:(NSString *)n ID:(NSString *)d {
self.Name = n;
self.ID = d;
return self;
}
#end
UserRec.h
#import <UIKit/UIKit.h>
#interface Userrec : NSObject {
NSString *Name;
NSString *ID;
}
#property (nonatomic, retain) NSString *Name;
#property (nonatomic, retain) NSString *ID;
-(id)initWithName:(NSString *)n ID:(NSString *)d;
#end
UserList.m
#synthesize userrecs;
…
- (void)viewDidLoad {
[super viewDidLoad];
NSString *Name = #"Name";
NSString *ID = #"IID";
Userrec *userrec = [[Userrec alloc] initWithName:Name ID:ID ];
[userrecs addObject:userrec];
NSLog(#"Count %d",[userrecs count]);
[userrec release];
NSLog(#"Count %d",[userrecs count]);
}
After I addobject and check the count its = 0. So I assume something is wrong?
NSMutableDictionary is the best way to go. You can do something as follows:
NSMutableDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"John Doe", #"Name", [NSNumber numberWithInt:5], #"ID", nil];
You can keep adding as many fields as you like with that same template, even NSArray objects. I'd look up the documentation if you have any more trouble. Remember, you can only store pointers to objects in an NSDictionary. Things like
NSMutableDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"John Doe", #"Name", 5, #"ID", nil];
won't work. Good Luck!
Take a look at an NSMutableDictionary it seems like the exact thing you would want to use
Edit:
Here's some sample code
NSMutableArray *myData = [[NSMutableArray alloc] init];
NSMutableDictionary *myRow = [[NSMutableDictionary alloc] init];
[myRow setObject:#"John Doe" forKey:#"Name"];
[myRow setObject:#"4738" forKey:#"ID"];
[myRow setObject:#"1/23/45" forKey:#"DOB"];
[myData addObject:myRow];
[myRow release];
//Repeat from dictioanry alloc through release for each row you need to add
To display this in a UITableView, you need to have a UITableViewController class. In there override the cellForRowAtIndexPath: function. here is a simple implementation of that function
-(UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = [indexPath row];
static NSString *kCellID = #"cellID";
UITableViewCell *cell = nil;
cell = [tableView dequeueReuseableCellWithIdentifier:kCellID];
if ( cell == nil )
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
}
NSMutableDictionary curRow = [myData objectAtIndex:row];
cell.textLabel.text = [curRow objectForKey:#"Name"];
return cell;
}

Why is my NSMutableArray "half dead" in cellForRowAtIndexPath?

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?

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.