not getting entire xml data into tableview - iphone

im parsing xml data into table view,here iam retrieving question and answer tags,problem occured in question tag only one question was displayed remaining questions not displayed ,this is the web service http://www.cmellc.com/DesktopModules/CaseChallenge/quiz.xml
this is the code i wrote
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentTag = elementName;
if ([currentTag isEqualToString:#"question"])
{
model = [[ModelDataView alloc]init];
}
if([currentTag isEqualToString:#"answer"])
{
model_2=[[Model2 alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"question"])
{
[dataControllerArray addObject:model];
}
if ([elementName isEqualToString:#"answer"])
{
[dataControllerArray2 addObject:model_2];
model_2 = nil;
}
currentTag = nil;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentTag isEqualToString:#"result"])
{
model_2.result = string;
}
if ([currentTag isEqualToString:#"notes"])
{
model_2.notes = string;
}
if ([currentTag isEqualToString:#"question"])
{
model.question = string;
}
}
this is code for tableview methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
static NSString *cellIdentifier2 = #"cell2";
UITableViewCell *cell;
if(tableView.tag==2)
{
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
Model2 *obj=[modelController.dataControllerArray2 objectAtIndex:indexPath.row];
NSString *res=obj.result;
cell.textLabel.text=res;
return cell;
}
else if(tableView.tag==1)
{
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier2];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier2] autorelease];
}
ModelDataView *obj=[modelController.dataControllerArray objectAtIndex:indexPath.row];
NSString *res=obj.question;
UIWebView *_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 190)];
[self.view addSubview:_webView];
CGRect frame = _webView.frame;
frame.size.height = 190;
_webView.frame = frame;
[_webView loadHTMLString:res baseURL:[NSURL URLWithString:res]];
return cell;
}
}
thank you for helping me

Related

iphone- UITableView displaying only one value

My UITableView is not displaying all values after parsing XML. It returns only one row. After parsing xml, why is only one row being shown and not the whole ArrayList? I have checked my xml php page. It is showing all values fine.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[xmlcont xmlbanktransfer] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSUInteger const kMagnitudeImageTag = 0;
static NSUInteger const kLocationLabelTag = 4;
static NSUInteger const klocationtag = 6;
static NSUInteger const kacctag = 8;
static NSUInteger const knameLabelTag = 9;
UIImageView *magnitudeImage = nil;
UILabel*locationLabel = nil;
UILabel*locationtag=nil;
UILabel*acc=nil;
UILabel*nameLabel= nil;
//NSMutableArray *cont = [xmlcont tweets];
// Tweet *current       = [cont objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
/*//common settings
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.frame = CGRectMake(0.0f, 0.0f, 44.0f, 44.0f);
cell.imageView.clipsToBounds = YES;
*/
}
for(UIView *view in cell.contentView.subviews)
{
if ([view isKindOfClass:[UIView class]])
{
[view removeFromSuperview];
}
}
NSMutableArray *cont = [xmlcont xmlbanktransfer];
trans *current= [cont objectAtIndex:indexPath.row];
NSLog(#"sd %#",current.date);
NSLog(#"sd %#",current.name);
locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,10, 250, 20)];
locationLabel.tag = kLocationLabelTag;
locationLabel.font = [UIFont boldSystemFontOfSize:15];
[cell.contentView addSubview:locationLabel];
locationLabel.text=current.name;
locationLabel.textColor=[UIColor colorWithRed:0.050 green:0.278 blue:0.392 alpha:1];
//locationLabel.backgroundColor=[UIColor lightGrayColor];
locationLabel.backgroundColor=[UIColor colorWithRed:225 green:225 blue:225 alpha:0] ;
nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(180,10, 250, 20)];
nameLabel.tag = knameLabelTag;
nameLabel.font = [UIFont systemFontOfSize:13];
[cell.contentView addSubview:nameLabel];
nameLabel.text=current.date ;
nameLabel.textColor=[UIColor colorWithRed:0.050 green:0.278 blue:0.392 alpha:1];
//locationLabel.backgroundColor=[UIColor lightGrayColor];
nameLabel.backgroundColor=[UIColor colorWithRed:225 green:225 blue:225 alpha:0] ;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
code for parsing xml:
xmlbanktransfer=[[NSMutableArray alloc] init];
NSURL *url=[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
parser=[[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser parse];
NSLog(#"testing12 %# ",url);
// NSLog(#"tweets %# ", xmlbanktransfer);
NSLog(#"Total values = %d",[xmlbanktransfer count]);
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"item"])
{
currentTweet = [trans alloc];
banktransNodeContent =[[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"date"])
{
currentTweet.date = [currentTweet.date stringByReplacingOccurrencesOfString:#"&" withString:#" "];
currentTweet.date = banktransNodeContent;
}
if([elementName isEqualToString:#"name"])
{
currentTweet.name =banktransNodeContent;
}
if ([elementName isEqualToString:#"type"])
{
currentTweet.type=banktransNodeContent;
}
if ([elementName isEqualToString:#"gross"])
{
currentTweet.gross=banktransNodeContent;
}
if ([elementName isEqualToString:#"fee"])
{
currentTweet.fee=banktransNodeContent;
}
if ([elementName isEqualToString:#"net"])
{
currentTweet.net=banktransNodeContent;
NSLog(#"current tweet %#",currentTweet.net);
}
if ([elementName isEqualToString:#"category"])
{
currentTweet.category=banktransNodeContent;
NSLog(#"current tweet %#",currentTweet.category);
}
if ([elementName isEqualToString:#"account"])
{
currentTweet.account=banktransNodeContent;
NSLog(#"current tweet %#",currentTweet.account);
}
if([elementName isEqualToString:#"item"])
{
[xmlbanktransfer addObject:currentTweet];
currentTweet = nil;
banktransNodeContent = nil;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"currentNodeContent are %#",banktransNodeContent);
banktransNodeContent = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"currentNodeContentstring test are %#",banktransNodeContent);
}
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
banktransNodeContent = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[xmlcont xmlbanktransfer] count];
}
this method tells the tableview how many cells to populate
Log the value and see how much it is returning [it must be 1 returning now,since the table shows one row].Correct the return value and you have all values displayed
Things to check
Returning value mentioned above
Make sure the array xmlbanktransfer is a property which is of strong type

i'm trying to display xml data in custom cell but it's working, and it display only one data [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
- (void)viewDidLoad
{
NSURL *url=[[NSURL alloc]initWithString:#"http://avweatherforecasts.com/services/iphoneservice.php"];
DaTa=[[NSData alloc]initWithContentsOfURL:url];
NSLog(#"data----%#",DaTa);
Xmlpaeser=[[NSXMLParser alloc]initWithData:DaTa];
Xmlpaeser.delegate=self;
[Xmlpaeser parse];
[Xmlpaeser release];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
array=[[NSMutableArray alloc]init];
strcontent=nil;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"start"])
{
Dict=[[NSMutableDictionary alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
strcontent=[NSString stringWithFormat:#"%#",string];
NSLog(#"Strcontent----%#",strcontent);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"city"]){
[Dict setObject:strcontent forKey:elementName];
}
if ([elementName isEqualToString:#"min"]){
[Dict setObject:strcontent forKey:elementName];
}
if ([elementName isEqualToString:#"max"]){
[Dict setObject:strcontent forKey:elementName];
}
if ( [elementName isEqualToString:#"forecast"]
) {
[Dict setObject:strcontent forKey:elementName];
NSLog(#"dict--%#",[Dict description]);
}
else if([elementName isEqualToString:#"start"]){
[array addObject:Dict];
[Dict release];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(#"array--%#",[array description]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier=#"cell";
custome_cell *Cell=(custome_cell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (Cell==nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"custome_cell" owner:self options:nil];
Cell = [nib objectAtIndex:indexPath.row];
}
Cell.city.text=[[array objectAtIndex:indexPath.row] objectForKey:#"city"];
Cell.min.text=[[array objectAtIndex:indexPath.row] objectForKey:#"min"];
Cell.max.text=[[array objectAtIndex:indexPath.row] objectForKey:#"max"];
Cell.forecast.text =[[array objectAtIndex:indexPath.row] objectForKey:#"forecast"];
return Cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 80;
}
Try this...May be it will help you...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [CommentsGetArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
//[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
commentsTextView = [[UITextView alloc]initWithFrame:CGRectMake(90, 4, 190, 50)];
commentsTextView.delegate = self;
commentsTextView.tag = 101;
commentsTextView.textAlignment = UITextAlignmentLeft;
[commentsTextView setScrollEnabled:YES];
commentsTextView.backgroundColor =[UIColor clearColor];
commentsTextView.font = [UIFont fontWithName:#"Arial" size:12.0];
commentsTextView.textColor =[UIColor blackColor];
commentsTextView.editable = NO;
[cell addSubview:commentsTextView];
}
commentsTextView = (UITextView *)[cell viewWithTag:101];
commentsTextView.text = [[CommentsGetArray objectAtIndex:indexPath.row]objectForKey:#"comment"];
return cell;
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 70;
}
you can use as a label instead of uitextView and you can create more text views or label to show your data but remember change their frames.

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

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

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.

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!