Problem with NSMutableArray & object retrieval from it, populated from NSXMLParser - iphone

I'm a newbie to Objective-C & iPhone development, hence please bear with me.
I'm working on an app which loads with UITableView and upon selecting one particular cell called "Address Book" it should load with another UITableView containing all the addresses retrieved from a web request. Using NSXMLParser's delegate methods I'm storing those addresses into a NSMutableArray defined in the loaded view's header file. But the problem occurs when those addresses are being displayed onto the UITableView cells (yes, I have got the count for the number of cells). Can anybody please help me with this problem? I'm stuck on this for days now !!! Following is my code:
- (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];
}
AddressEntry *entry = (AddressEntry *)[self.addresses objectAtIndex:indexPath.row];
#try
{
NSLog(#"%#", entry.firstName);
}
#catch (NSException * e)
{
#try
{
NSLog(#"%#", entry.lastName);
}
#catch (NSException * e)
{
NSLog(#"%#", entry.lastName);
}
}
[entry release];
return cell;
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.currentElement = nil;
self.f_Name = nil;
self.l_Name = nil;
self.phone = nil;
self.email = nil;
count = 0;
self.addresses = [[NSMutableArray alloc] init];
}
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"Entries"])
{
count = [[attributeDict valueForKey:#"total"] intValue];
}
else if([elementName isEqualToString:#"FirstName"])
{
[self.f_Name release];
self.f_Name = [[NSString alloc] init];
}
else if([elementName isEqualToString:#"LastName"])
{
[self.l_Name release];
self.l_Name = [[NSString alloc] init];
}
else if([elementName isEqualToString:#"PhoneNumber"])
{
[self.phone release];
self.phone = [[NSString alloc] init];
}
else if([elementName isEqualToString:#"EmailAddress"])
{
[self.email release];
self.email = [[NSString alloc] init];
}
else if([elementName isEqualToString:#"Record"])
{
[self.addrEntry release];
self.addrEntry = [[[AddressEntry alloc] init] retain];
}
[self.currentElement release];
self.currentElement = nil;
self.currentElement = [elementName copy];
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
//
if([elementName isEqualToString:#"FirstName"])
self.addrEntry.firstName = self.f_Name;
else if ([elementName isEqualToString:#"LastName"])
self.addrEntry.lastName = self.l_Name;
else if([elementName isEqualToString:#"PhoneNumber"])
self.addrEntry.mobile = self.phone;
else if([elementName isEqualToString:#"EmailAddress"])
self.addrEntry.emailAddress = self.email;
else if([elementName isEqualToString:#"Record"])
{
[self.addresses addObject:self.addrEntry];
[self.addrEntry release];
self.addrEntry = nil;
/*AddressEntry *e = (AddressEntry *)[self.addresses lastObject];
NSLog(#"%# %# %# %#", e.firstName, e.lastName, e.mobile, e.emailAddress);
[e release];*/
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if([currentElement isEqualToString:#"FirstName"])
self.f_Name = string;
else if([currentElement isEqualToString:#"LastName"])
self.l_Name = string;
else if([currentElement isEqualToString:#"PhoneNumber"])
self.phone = string;
else if([currentElement isEqualToString:#"EmailAddress"])
{
self.email = string;
}
}

The most likely reason for the app crashing is this line in cellForRowAtIndexPath:
[entry release];
You did not alloc/init the variable entry there so don't call release on it. Remove that line.
In that method, entry is only holding a reference to an already-allocated object in the address array. It doesn't "own" the object so it shouldn't release it.
This page in the Apple docs shows a nice flow diagram and some simple rules for memory management that might help in understanding this better.
As a separate issue unrelated to the crashing, the use of #try/#catch in your example seems unnecessary. See this answer for an explanation.
Finally, also unrelated to the crashing, in foundCharacters, you should really be appending the string parameter to your data instead of just assigning because it's possible for that method to be called multiple times within the same value. See Handling XML Elements and Attributes for an example.

Related

How to parse Multiple .xml files in iphone?

Its my first experience with xmls so please don't be harsh on me if my question is low quality. I am working on app where I retrieve all data using xml files from server.
Before posting question here I read some Tutorials about xml file parsing. But when I start to implement it in my app, then I am totally confused at the point when I am trying to parse the second Xml file and so on.
Now I want to explain my app flow using some screenshot so that it become easy for everyone that what I want.
First view
Second view
Third view
Now when I click in any row in first view it goes to second view and show all related data in second view and when I click any item in Second view it goes to the third view and show detail information about that particular item.
After explanation of app flow, now I show my code what I tried so far. Following a tutorial I just simply drag classes (XMLParser, Book) to my app for testing purpose.
Code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *url = [[NSURL alloc] initWithString:#"Firstviewxmlurl"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
}
Now my XMLParser.m code is
- (XMLParser *) initXMLParser {
[super init];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Collections"]) {
//Initialize the array.
appDelegate.books = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Collection"]) {
//Initialize the book.
aBook = [[Book alloc] init];
//Extract the attribute here.
aBook.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading id value :%i", aBook.bookID);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Collections"])
return;
if([elementName isEqualToString:#"Collection"]) {
[appDelegate.books addObject:aBook];
[aBook release];
aBook = nil;
}
else
[aBook setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
Finaly my First view Class code is
#pragma mark Table Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//return [tblViewData count];
return [appDelegate.books count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
cell.selectionStyle=UITableViewCellSelectionStyleGray;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
//cell.textLabel.text=[[tblViewData objectAtIndex:indexPath.row]
// stringByReplacingOccurrencesOfString:#"*" withString:#""];
Book *aBook = [appDelegate.books objectAtIndex:indexPath.row];
cell.textLabel.text = aBook.title;
if([deviceType isEqualToString:#"ipad"])
{
[cell.textLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:20]];
}
//cell.textLabel.font=[UIFont fontWithName:#"Walkway_SemiBold" size:16];
return cell;
}
After implementation of all the above Code i got the result in form of this Now at this point i confuse how can i pass my Second view xml file url and then third view url.my xml files are in this format
First xml file
<?xml version="1.0" encoding="UTF-8"?>
<Collections> <Collection id="5"><title>Allegroo</title> </Collection>
Second xml file
<?xml version="1.0" encoding="UTF-8"?>
<Collection id="5"><colletciontitle>Allegroo</colletciontitle> <weaveimage>"imageurl" </weaveimage><Rug id="48"><Rugthumb>"firstimageurl"</Rugthumb><RugSku>AL-637</RugSku></Rug><Rug id="51"><Rugthumb>"Secondimageurl"</Rugthumb><RugSku>AL-641</RugSku></Rug>
Third xml file
<?xml version="1.0" encoding="UTF-8"?>
<Rug id="47"><Rugmainimage>"imageurl"</Rugmainimage> <Rugtitle>AL-636 Aged Indigo / Vintage Beige</Rugtitle> <Rugdiscription>Hand Knotted
Distressed Wool Pile / Cotton Foundation </Rugdiscription><Availablesizes><Size>10x14</Size><Size>12x15</Size><Size>2x3</Size><Size>3x5</Size><Size>4x6</Size><Size>6x9</Size><Size>8x10</Size><Size>9x12</Size><Runner>2'6"x10'</Runner><Runner>2'6"x12'</Runner><Runner>2'6"x8'</Runner></Availablesizes></Rug>
Any help in the form of link or sample code releated to my issue and suggestion will be highly appriated.Thanks
Hmm, So Defining long story short, you want to parse multiple urls in sequence ?
So here is the answer,
Use a variable / identifier in appDelegate that tells you which xml is being parsed.
Use your xml parser as delegate class object
Use delegate method to swich to next xml parsing
- (void)parserDidEndDocument:(NSXMLParser *)parser {
// Change URL accordingly
NSURL *url = [[NSURL alloc] initWithString:#"Firstviewxmlurl"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];
BOOL success = [xmlParser parse];
}
I have added some of the code to make you understand. Try to implement according to your condition. Below code is just to refer as i havn't made all the changes u need to understand first and then implement accordingly
//int i = 0; // in AppDelegate
Code:
//Or ViewDidLoad Method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int = 1; //For First View
NSURL *url = [[NSURL alloc] initWithString:#"Firstviewxmlurl"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
}
XMLParser.m
- (XMLParser *) initXMLParser
{
[super init];
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Collections"])
{
//Initialize the array.
if (int == 1)
appDelegate.books = [[NSMutableArray alloc] init];
else if (int == 2)
appDelegate.secondArray = [[NSMutableArray alloc] init];
else if (int == 3)
appDelegate.thirdArray = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Collection"])
{
if (i == 1)
{
//Initialize the book.
aBook = [[Book alloc] init];
//Extract the attribute here.
aBook.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading id value :%i", aBook.bookID);
}
else if (i == 2)
{
//Second Xml
}
else if (i == 3)
{
//Third Xml
}
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
//Check condition
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
//Check COndition
if([elementName isEqualToString:#"Collections"])
return;
if([elementName isEqualToString:#"Collection"]) {
[appDelegate.books addObject:aBook];
[aBook release];
aBook = nil;
}
else
[aBook setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}

Merge sections by object property in UITableView

i'm having some troubles trying to parse an XML file and display the results grouped by an object property on a UITableView.
This is the XML file, I can change the structure if it's necessary:
<Anuncios>
<Anuncio id="1">
<localeName>name1</localeName>
<address>address1</address>
<type>A</type>
</Anuncio>
<Anuncio id="2">
<localeName>name2</localeName>
<address>address2</address>
<type>B</type>
</Anuncio>
<Anuncio id="3">
<localeName>name3</localeName>
<address>address3</address>
<type>A</type>
</Anuncio>
</Anuncios>
At this moment I have a NSMutableArray called servs containing objects (Anuncio).
This is the structure:
#interface Anuncio : NSObject
NSInteger iden;
NSString *localeName;
NSString *address;
NSString *type;
So, I would like to sort the UITableView by anuncio.type with the correct titleForHeaderInSection.
I downloaded the TableViewSuite from the Apple documentation, and the second example, SimpleSectionedTableView seems to do what I want, but instead of getting the data from an XML file, it gets the data calling [NSTimeZone knownTimeZoneNames], so the structure is quite different, I tried to adapt the sample code to my project, but i'm starting to realize that seems to be not the best way to do it. So I'm a bit stuck.
I was thinking on having arrays inside the main array containing the objects editing the XML, like that:
servs: {
arrayA : { anuncio1, anuncio3 }
arrayB : { anuncio2 }
}
but then I don't know how to complete the tableView methods.
Here it is the XMLParse code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Anuncios"]) {
//Initialize the array.
appDelegate.servs = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Anuncio"]) {
//Initialize the anuncio.
serv = [[Anuncio alloc] init];
//Extract the attribute here.
serv.iden = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading id value: %i", serv.iden);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Anuncios"])
return;
if([elementName isEqualToString:#"Anuncio"]) {
[appDelegate.servs addObject:serv];
[serv release];
serv = nil;
}
else
[serv setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
Part of my current UITableViewController:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [appDelegate.servs count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
anuncios = (NSMutableArray *)[appDelegate.servs sortedArrayUsingSelector:#selector(compare:)];
Anuncio *anuncio = [anuncios objectAtIndex:section];
return [anuncio type];
}
- (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];
}
anuncios = (NSMutableArray *)[appDelegate.servs sortedArrayUsingSelector:#selector(compare:)];
Anuncio *anuncio = [anuncios objectAtIndex:indexPath.section];
cell.textLabel.text = anuncio.localeName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
At the moment I get:
A
--------
name1
--------
A
--------
name3
--------
B
--------
name2
and I would like to get:
A
--------
name1
name3
--------
B
--------
name2
I don't know if the model that I'm proposing is the right one or i'm missing something. I thought it was something very common but I didn't find anything about this.
Thanks for helping
In your Anuncio class, add a compare: method like this:
- (NSComparisonResult) compare:(Anuncio *)otherObject {
return [self.type compare:otherObject.type];
}
Then sort your NSMutableArray with this:
anuncios = (NSMutableArray *)[anuncios sortedArrayUsingSelector:#selector(compare:)];

Data Types and XMLParser

I syncing the data on my website to my app, I'm using NSXMLParser to do this. The problem is I have all the fields on my database defined as Strings. The sync process works fine when everything is a string, but this is causing me heartache further down the line when I try and use this data for other purposes.
Can anyone help me with defining my fields with the correct data types for the sync process, code below:
.m
// Array for WORKOUT.
NSMutableString *currentID, *currentUserID, *currentWalkID, *currentDate, *currentDistance, *currentRepeats, *currentType, *currentIntensity,
*currentComments, *currentTime, *currentWeight, *currentHeight;
I know its something to do with this NSMutableString, obviously everything is defined as a string.
.h
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
// Check for the WORKOUT details in the XML feed.
if ([elementName isEqualToString:#"workout"])
{
// clear out our workout item caches...
item = [[NSMutableDictionary alloc] init];
currentID = [[NSMutableString alloc] init];
currentUserID = [[NSMutableString alloc] init];
currentWalkID = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentDistance = [[NSMutableString alloc] init];
currentRepeats = [[NSMutableString alloc] init];
currentType = [[NSMutableString alloc] init];
currentIntensity = [[NSMutableString alloc] init];
currentComments = [[NSMutableString alloc] init];
currentTime = [[NSMutableString alloc] init];
currentWeight = [[NSMutableString alloc] init];
currentHeight = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"workout"])
{
Workout *newWorkout = [NSEntityDescription insertNewObjectForEntityForName:#"Workout" inManagedObjectContext: self.managedObjectContext];
// save values to an item, then store that item into the array...
[item setObject:currentID forKey:#"workout_id"];
[item setObject:currentUserID forKey:#"workout_user_id"];
[item setObject:currentWalkID forKey:#"workout_walk_id"];
[item setObject:currentDate forKey:#"workout_date"];
[item setObject:currentDistance forKey:#"workout_distance"];
[item setObject:currentRepeats forKey:#"workout_repeats"];
[item setObject:currentType forKey:#"workout_type"];
[item setObject:currentIntensity forKey:#"workout_intensity"];
[item setObject:currentComments forKey:#"workout_comments"];
[item setObject:currentTime forKey:#"workout_time"];
[item setObject:currentWeight forKey:#"workout_weight"];
[item setObject:currentHeight forKey:#"workout_height"];
newWorkout.workout_id = currentID;
newWorkout.workout_user_id = currentUserID;
newWorkout.workout_walk_id = currentWalkID;
newWorkout.workout_date = currentDate;
newWorkout.workout_distance = currentDistance;
newWorkout.workout_repeats = currentRepeats;
newWorkout.workout_type = currentType;
newWorkout.workout_intensity = currentIntensity;
newWorkout.workout_comments = currentComments;
newWorkout.workout_time = currentTime;
newWorkout.workout_weight = currentWeight;
newWorkout.workout_height = currentHeight;
[self.workoutArray addObject:newWorkout];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// save the characters for the current item...
if ([currentElement isEqualToString:#"workout_id"]) {
[currentID appendString:string];
} else if ([currentElement isEqualToString:#"workout_user_id"]) {
[currentUserID appendString:string];
} else if ([currentElement isEqualToString:#"workout_walk_id"]) {
[currentWalkID appendString:string];
} else if ([currentElement isEqualToString:#"workout_date"]) {
[currentDate appendString:string];
} else if ([currentElement isEqualToString:#"workout_distance"]) {
[currentDistance appendString:string];
} else if ([currentElement isEqualToString:#"workout_repeats"]) {
[currentRepeats appendString:string];
} else if ([currentElement isEqualToString:#"workout_type"]) {
[currentType appendString:string];
} else if ([currentElement isEqualToString:#"workout_intensity"]) {
[currentIntensity appendString:string];
} else if ([currentElement isEqualToString:#"workout_comments"]) {
[currentComments appendString:string];
} else if ([currentElement isEqualToString:#"workout_time"]) {
[currentTime appendString:string];
} else if ([currentElement isEqualToString:#"workout_weight"]) {
[currentWeight appendString:string];
} else if ([currentElement isEqualToString:#"workout_height"]) {
[currentHeight appendString:string];
}
XML can only carry strings. Wheather it's an element's inner text or the value of an attribute, in the end everything is just a string.
If you want to send anything other via XML, the sending side has to encode it in a string and the receiving side has to decode the string. For that to work both sides have to agree on a format, for Example for an Integer value one possibility is to encode 125 as "125", or a float 2.5 as "2.5". If you are using this format for your numbers you can decode them by using the integerValue and floatValue of NSString.
float test = [#"2.5" floatValue];
There are some standard formats defined in xsd, that you could use, but that will not help you decode them, if you don't have a parser that does this for you. (NSXMLParser is no such)
If you are using other Formats look at NSScanner. For dates and time you would like to look at NSDateFormatter.
If you need help converting please post the format you are using, that is an excerpt of your XML.

Need help with memory leaks in RSS Reader

I'm trying to write a simple RSS reader for the iPhone, and it appeared to be working fine, until I started working with Instruments, and discovered my App is leaking massive amounts of memory.
I'm using the NSXMLParser class to parse an RSS feed. My memory leaks appear to be originating from the overridden delegate methods:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
and
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
I'm also suspicious of the code that populates the cells from my parsed data, I've included the code from those methods and a few other key ones, any insights would be greatly appreciated.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([self.currentElement isEqualToString:#"title"]) {
[self.currentTitle appendString:string];
} else if ([self.currentElement isEqualToString:#"link"]) {
[self.currentURL appendString:string];
} else if ([self.currentElement isEqualToString:#"description"]) {
[self.currentSummary appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
//asdf
NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentURL forKey:#"URL"];
[item setObject:currentSummary forKey:#"summary"];
[self.currentTitle release];
[self.currentURL release];
[self.currentSummary release];
[self.stories addObject:item];
[item release];
}
}
// 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.
// Set up the cell
int index = [indexPath indexAtPosition: [indexPath length] - 1];
CGRect contentRect = CGRectMake(8.0, 4.0, 260, 20);
UILabel *textLabel = [[UILabel alloc] initWithFrame:contentRect];
if (self.currentLevel == 0) {
textLabel.text = [self.categories objectAtIndex: index];
} else {
textLabel.text = [[self.stories objectAtIndex: index] objectForKey:#"title"];
}
textLabel.textColor = [UIColor blackColor];
textLabel.font = [UIFont boldSystemFontOfSize:14];
[[cell contentView] addSubview: textLabel];
//[cell setText:[[stories objectAtIndex: storyIndex] objectForKey: #"title"]];
[textLabel autorelease];
return cell;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"item"]) {
self.currentTitle = [[NSMutableString alloc] init];
self.currentURL = [[NSMutableString alloc] init];
self.currentSummary = [[NSMutableString alloc] init];
}
if (currentElement != nil) {
[self.currentElement release];
}
self.currentElement = [elementName copy];
}
- (void)dealloc {
[currentElement release];
[currentTitle release];
[currentURL release];
[currentSummary release];
[currentDate release];
[stories release];
[rssParser release];
[storyTable release];
[super dealloc];
}
// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here -- for example, create and push another view controller.
// AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
int index = [indexPath indexAtPosition: [indexPath length] - 1];
if (currentLevel == 1) {
StoryViewController *storyViewController = [[StoryViewController alloc] initWithURL:[[stories objectAtIndex: index] objectForKey:#"URL"] nibName:#"StoryViewController" bundle:nil];
[self.navigationController pushViewController:storyViewController animated:YES];
[storyViewController release];
} else {
RootViewController *rvController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
rvController.currentLevel = currentLevel + 1;
rvController.rssIndex = index;
[self.navigationController pushViewController:rvController animated:YES];
[rvController release];
}
}
I figured out my problem, all of my memory leaks stemmed from this statement:
self.stories = [[NSMutableArray alloc] init];
This causes the retain count of stories to be incremented by 2, since the setter calls retain on the newly allocated array.
I replaced the above statement with this one and it solved my problem:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.stories = array;
[array release];
Another way of fixing your code is this replacing
self.stories = [NSMutableArray alloc] init];
with
self.stories = [NSMutableArray arrayWithCapacity:10];
The arrayWithCapacity method is autoreleased so you don't need to manually call release. (This is true for other classes i.e. setWithCapacity, stringWithFormat etc)
Thanks, Sam
PS Not helping your question but these lines look a little unusual :
[self.currentTitle release];
You should probably be doing this :
self.currentTitle = nil;
That will release currentTitle the same as your code does but it will also set it to nil which means you can't use it again by mistake!

Display NSMutableArray in Table View Cell

How could I display arrayData in table view cell?
I want to set the table view cell's label text to the 'ename' from the data parsed.
Please help me out.
Thanks in Advance.
- (void)viewDidLoad {
NSString *url = [NSString stringWithFormat:#"http://mmabigshow.com/app/get_result_from_query.php?q=select * from event"];
NSString *escapedUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[self performSelector:#selector(startParsing:) withObject:escapedUrl afterDelay:1.0];
NSLog(#"View Did Load End");*/
[super viewDidLoad];
}
- (void) startParsing: (NSString *) query{
NSLog(#"Start Of Parsing");
NSURL *xmlURL = [[NSURL alloc] initWithString:query];
NSData *myData = [NSData dataWithContentsOfURL:xmlURL];
NSString *myStr = [[NSString alloc] initWithData:myData encoding:NSWindowsCP1252StringEncoding];
myStr = [myStr stringByReplacingOccurrencesOfString:#"encoding=\"windows-1252\"" withString:#""];
NSData* aData = [myStr dataUsingEncoding:NSUTF8StringEncoding];
rssParser = [[NSXMLParser alloc] initWithData:aData];
[rssParser setDelegate:self];
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
NSLog(#"End of Parsing");
}
#pragma mark xml parser
#pragma mark -
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"table"]){
item = [[NSMutableDictionary alloc] init];
currentEid = [[NSMutableString alloc] init];
currentEname = [[NSMutableString alloc] init];
currentEurl = [[NSMutableString alloc] init];
dataArray = [[NSMutableArray alloc] init];
}
//NSLog(#"didStartElement");
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"table"]) {
// save values to an item, then store that item into the array...
[item setObject:currentEname forKey:#"ename"];
//NSLog(#"%#",item);
[dataArray addObject:[[item copy] autorelease]];
//NSLog(#"%#",dataArray);
}
[currentEid release];
[currentEname release];
[currentEurl release];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"eid"]) {
[currentEid appendString:string];
} else if ([currentElement isEqualToString:#"ename"]) {
[currentEname appendString:string];
//NSLog(#"%#",currentEname);
} else if ([currentElement isEqualToString:#"eurl"]) {
[currentEurl appendString:string];
}
// NSLog(#"foundCharacters");
}
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
NSLog(#"numberOfRowsInSection");
return [self.dataArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"cellForRowAtIndexPath");
static NSString *CustomCellIdentifier = #"CustomCellIdentifier ";
TableDetailsCell *cell = (TableDetailsCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TableDetailsCell" owner:self options:nil];
for (id oneObject in nib)
if ([oneObject isKindOfClass:[TableDetailsCell class]])
cell = (TableDetailsCell *)oneObject;
}
NSUInteger row = [indexPath row];
NSDictionary *rowData = [self.dataArray objectAtIndex:row];
//NSLog(#"%#",rowData);
cell.text = [rowData objectForKey:#"ename"];
cell.labelName.text= [rowData objectForKey:#"ename"];
return cell;
}
I believe your problem is that you are not referring to dataArray as self.dataArray all the time which may cause your program not to work properly.
Then assign via
cell.textLabel.text = [self.dataArray objectAtIndex:indexRow.row];
Hope this helped
Seb Kade
"I'm here to help"
Apple's SeismicXML app does exactly this. Look at the rootViewController's cellForRowAtIndexPath.
Also, make sure your tableview has it's delegate and datasource set correctly in Interface Builder (if you chose to use it).