简体   繁体   中英

Parsing XML code on iphone SDK

I have written some code, most of which has been taken from tutorials etc, but not sure it's 100%. The code works, but I think its a bit long winded, and has memory leaks.

So three questions:

  1. Can it be cleaned up? Easier to understand?
  2. How can I fix the 'if (indexPath.section == 1) theRow += 6;' bit to be dynamic?
  3. Memory leaks, how can i sort if they need to be sorted? (newbie...)

Any feedback/pointers/help more than welcome...

#import "InfoViewController.h"
@implementation InfoViewController

- (void)parseXMLFileAtURL:(NSString *)URL {
    sections = [[NSMutableArray alloc] init];
    secItems = [[NSMutableArray alloc] init];

    //you must then convert the path to a proper NSURL or it won't work
    NSURL *xmlURL = [NSURL URLWithString:URL];
    rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];

    // Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
    [rssParser setDelegate:self];

    // Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
    [rssParser setShouldProcessNamespaces:NO];
    [rssParser setShouldReportNamespacePrefixes:NO];
    [rssParser setShouldResolveExternalEntities:NO];

    [rssParser parse];
}

- (void)parserDidStartDocument:(NSXMLParser *)parser {
    [activityIndicator startAnimating];
    [activityIndicator addSubview];
}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    NSString * errorString = [NSString stringWithFormat:@"Unable to download story feed from web site (Error code %i )", [parseError code]];
    NSLog(@"error parsing XML: %@", errorString);

    UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:@"Error loading content" message:errorString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{

    currentElement = [elementName copy];

    if ([elementName isEqualToString:@"title"]) {
        // clear out our story item caches...
        currentTitle = [[NSMutableString alloc] init];
    }

    if ([elementName isEqualToString:@"section"]) {
        //item = [[NSMutableDictionary alloc] init];
        item = [[NSMutableDictionary alloc] init];
        currentSection = [[NSMutableString alloc] init];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{

    if ([elementName isEqualToString:@"section"]) {

        // save values to an item, then store that item into the array...
        [item setObject:currentSection forKey:@"name"];
        [item setObject:[NSNumber numberWithInt:itemsCount] forKey:@"itemsCount"];

        [sections addObject:[item copy]];

        itemsCount = 0;
    }

    if ([elementName isEqualToString:@"title"]) {

        // save values to an item, then store that item into the array...
        [item setObject:currentTitle forKey:@"title"];

        itemsCount = itemsCount + 1;
        [secItems addObject:[item copy]];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    // save the characters for the current item...
    if ([currentElement isEqualToString:@"name"]) {
        [currentSection appendString:string];
    } else if ([currentElement isEqualToString:@"title"]) {
        [currentTitle appendString:string];
    } else if ([currentElement isEqualToString:@"description"]) {
        [currentSummary appendString:string];
    } else if ([currentElement isEqualToString:@"pubDate"]) {
        [currentDate appendString:string];
    }
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {

    [activityIndicator stopAnimating];
    [activityIndicator removeFromSuperview];

    [dataTable reloadData];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];


    if ([sections count] == 0) {
        NSString * path = @"http://www.website.com/_dev/info.xml";
        [self parseXMLFileAtURL:path];
    }

}

#pragma mark -
#pragma mark Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return [sections count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [[sections objectAtIndex: section] objectForKey: @"name"];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[[sections objectAtIndex: section] objectForKey: @"itemsCount"] intValue];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];

    if(cell == nil){
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }

    int theRow = indexPath.row;

    /*
     if(indexPath.section >= 1){
     int oldSection = indexPath.section - 1;
     int prevRows = prevRows + [[[sections objectAtIndex: oldSection] objectForKey: @"itemsCount"] intValue];
     theRow += prevRows;
     }
     */

    if (indexPath.section == 1) theRow += 6;
    if (indexPath.section == 2) theRow += 8;
    if (indexPath.section == 3) theRow += 14;
    if (indexPath.section == 4) theRow += 19;
    if (indexPath.section == 5) theRow += 22;
    if (indexPath.section == 6) theRow += 23;

    cell.textLabel.text = [[secItems objectAtIndex:theRow] objectForKey: @"title"];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    return cell;
}

#pragma mark -
#pragma mark Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
}


#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Relinquish ownership any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
    // For example: self.myOutlet = nil;
}


- (void)dealloc {
    [super dealloc];
    [currentElement release];
    [rssParser release];
    [sections release];
    [item release];
    [currentTitle release];
}


@end

The XML feed which I am trying to break down for sections and items:

<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
    <section>
        <name>Section 1</name>
        <item>
            <title>Title 1</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
        <item>
            <title>Title 2</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[<Detail text]]></detail>
        </item>
        <item>
            <title>Title 3</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
        <item>
            <title>Title 4</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
        <item>
            <title>Title 5</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
        <item>
            <title>Title 6</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
    </section>
    <section>
        <name>Section 2</name>
        <item>
            <title>Title 1</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
        <item>
            <title>Title 2</title>
            <pubDate>1 Sept 2010</pubDate>
            <detail><![CDATA[Detail text]]></detail>
        </item>
    </section>
</data>

errorAlert leaks because you don't release it.

There are bad leaks with currentElement , currentTitle , item and currentSection because they all get overwritten in -parserDidStartElement:... without being released. You should create retain properties for them and access them only the properties (except in dealloc). That will make the problem go away.

  1. Unfortunately the NSXMLParser that Apple provides requires at least 3 functions to work, as you have done. If you want to clean it up, think about using a third party script, such as TouchXML or KissXML .

  2. As for making this dynamic, you can put those numbers into an array and read them out, or change the way you're setting the data. Usually with different sections you use different arrays for the data. This takes your theRow variable out of the equation. Here is a tutorial that uses multiple arrays without a dictionary.

  3. @JeremyP has some good suggestions. If you're still struggling, do a Build and Analyze. This will show you "potentially leaked objects" so you can go through and make sure everything's released properly.


There are 2 ways to go about parsing XML Trees-which is what you are doing.

The first is using the NSXML parser you already have. First create a dictionary containing all of the higher level element names, ie section and item, then add two extra strings to your parser, currentNodeName and parentNodeName. As you iterate through, change the currentNodeContent to the elementName. If the elementName is in your dictionary, set parentNodeName to the elementName, otherwise leave it as is.

Using TouchXML requires a bit less code. It works with xpaths, so you can specify /section/name/item/title in a path format. You will probably need to add another for loop that counts because you drill down another level.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM