Problem accessing NSArray ivar from IBAction

Discussion in 'iOS Programming' started by tw002, Apr 24, 2010.

  1. tw002 macrumors newbie

    Joined:
    Feb 23, 2009
    #1
    Hi,

    I'd consider myself a beginner at iPhone development, but I do have a reasonably solid understanding of how the basics work.

    I'm working on an app which reads through an XML file on the internet - the file I'm using for testing purposes: http://www.w3schools.com/xml/note.xml

    As it processes the contents of each element, it stores each piece of information (to, from, heading, body) in a separate ivar, and when it reaches the end of the <note> element, it creates an array from these 4 variables, and adds the array to an NSMutableArray (allNotes) which is an instance variable of the class.

    -(void) listNoteDetails is called to log the details of the notes:

    Code:
    -(void) listNoteDetails
    {	
    	for(NSArray *note in allNotes)	
    	{
    		NSString *to = [note objectAtIndex:0];
    		NSString *from = [note objectAtIndex:1];
    		NSString *heading = [note objectAtIndex:2];
    		NSString *body = [note objectAtIndex:3];	
                 // Code to log details here
    	}
    
    
    
    }
    

    The problem I'm having is if I call listNoteDetails from the method

    Code:
    - (void)parserDidEndDocument:(NSXMLParser *)parser
    {
    [self listNoteDetails]
    }

    which is implemented in the same class, it all works fine. However I want to be able to log these details by pressing a button. As soon as I change listNoteDetails to an IBAction and connect it to a button, it causes the app to crash when the button is pressed - this occurs whenever something in the listNoteDetails method tries to access the allNotes ivar. Can anyone explain why this happens?

    I hope my description of the problem makes some sense.

    Thanks.
     
  2. Luke Redpath macrumors 6502a

    Joined:
    Nov 9, 2007
    Location:
    Colchester, UK
    #2
    Sounds like a memory issue but hard to say without the rest of the code; can you post your whole class (imp and header).
     
  3. tw002 thread starter macrumors newbie

    Joined:
    Feb 23, 2009
    #3
    Thanks for your help.


    Code:
    #import <Foundation/Foundation.h>
    
    
    @interface XMLController : NSObject < NSXMLParserDelegate >
    
    {
    
    	NSMutableString *currentElement;
    	
    	NSMutableString *currentTo;
    	NSMutableString *currentFrom;
    	NSMutableString *currentHeading;
    	NSMutableString *currentBody;
    	
    	NSMutableArray *allNotes;
    	
    	
    }
    
    -(void) listNoteDetails;
    
    
    @end



    Code:
    #import "XMLController.h"
    
    
    @implementation XMLController
    
    
    
    -(id)init		// Create parser, set feed, create allNotes array
    
    {
    
    	NSURL *feedURL = [NSURL URLWithString:@"http://www.w3schools.com/xml/note.xml"];
    	
    	NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:feedURL];
    	
    	allNotes = [NSMutableArray array];
    	
    	[parser setDelegate:self];
    	
    	[parser parse];
    		
    	return self;
    	
    }
    
    
    - (void)parserDidStartDocument:(NSXMLParser *)parser
    {
    	NSLog(@"Started");
    	
    }
    
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
    													namespaceURI:(NSString *)namespaceURI 
    													qualifiedName:(NSString *)qualifiedName 
    													attributes:(NSDictionary *)attributeDict {
    
    	
    	currentElement = [NSMutableString stringWithFormat:@"%@", elementName];
    	
    	
    }
    
    
    // Set ivars to found details, ignoring whitespace
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
    	
    	
    	NSString *trimmedString = [string stringByTrimmingCharactersInSet:
    							   [NSCharacterSet whitespaceAndNewlineCharacterSet]];
    	
    	NSMutableString *finalTrimmed = [NSMutableString stringWithFormat:@"%@", trimmedString];
    	
    	NSString *nullString = @"";
    	
    	if ([trimmedString isEqualToString:nullString]) {
    		
    	} else {
    	
    		if([currentElement isEqualToString:@"to"])
    		{currentTo = finalTrimmed;}
    		
    		if([currentElement isEqualToString:@"from"])
    		{currentFrom = finalTrimmed;}
    	
    		if([currentElement isEqualToString:@"heading"])
    		{currentHeading = finalTrimmed;}
    		
    		if([currentElement isEqualToString:@"body"])
    		{currentBody = finalTrimmed;}
    		
    	
    	}
    	
    	
    }
    
    
    // If end on note reached, create array of finished note, add to allNotes array
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
    												namespaceURI:(NSString *)namespaceURI 
    												qualifiedName:(NSString *)qName {
    		
    	
    	NSString *lastTag = @"note";
    	
    	if ([elementName isEqualToString:lastTag]) {
    	
    		NSArray *note = [[NSArray alloc] initWithObjects:	
    					 currentTo,
    					 currentFrom,
    					 currentHeading,
    					 currentBody,
    					 nil];
    			
    	[allNotes addObject:note];
    		
    	[note release];
    		
    	}
    	
    }
    
    
    
    
    
    // When finished processing, list all details
    
    
    - (void)parserDidEndDocument:(NSXMLParser *)parser
    {
    	[self listNoteDetails];
    	
    }
    
    
    
    
    -(void) listNoteDetails
    {
    
    	
    	for(NSArray *note in allNotes)
    		
    	{
    		NSString *to = [note objectAtIndex:0];
    		NSString *from = [note objectAtIndex:1];
    		NSString *heading = [note objectAtIndex:2];
    		NSString *body = [note objectAtIndex:3];
    	
    		NSString *details = [NSString stringWithFormat:@"To: %@ From: %@ Subject: %@ Note: %@", to, from, heading, body];
    	
    		NSLog(@"%@", details);
    	
    	}
    	
    }
    
    
    
    
    @end
     
  4. Luke Redpath macrumors 6502a

    Joined:
    Nov 9, 2007
    Location:
    Colchester, UK
    #4
    Here's your problem, in your init method:

    Code:
    allNotes = [NSMutableArray array];
    
    [NSMutableArray array] returns an autoreleased object, which means it will be released at some point in the future (when the autorelease pool is drained). By the time you click your button and it tries to access the allNotes variable, it would have been released which is why you get a crash.

    You should allocate a non-autoreleased object:

    Code:
    allNotes = [[NSMutableArray alloc] init];
    
    If you aren't sure about which methods return autoreleased objects, and when to retain/release objects, I recommend reading Apple's Memory Management Guide.
     
  5. tw002 thread starter macrumors newbie

    Joined:
    Feb 23, 2009
    #5
    Thanks very much - I understand where I went wrong.
     

Share This Page