Memory Management - Newbie Q

Discussion in 'Mac Programming' started by hubrisForAll, Jan 20, 2011.

  1. hubrisForAll macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #1
    Hello helpful community.

    Trying to make a very simple application to learn about XML / Parsing, relying on Hillegass and Apple Doc's - just trying to do everything one step at a time.

    Have a local XML file (a downloaded RSS Feed), imported to a cocoa project. Have one window program with a button to load the xml file into an NSData object, then another button to use an NSXMLParser Object to parse the data.

    The program Crashes as soon as I press the button to Parse XML (see bold code) and my initial Question was 'Why'. Then just before I hit post, I think I got it, and appropriately changed the title of my 'Title.' Questions following the code -



    Header:
    Code:
    @interface GoodNoosAppDelegate : NSObject <NSApplicationDelegate, NSXMLParserDelegate> {
        NSWindow *window;
        NSData *xmlData;
        NSString *pathName;
    
    }
    
    @property (assign) IBOutlet NSWindow *window;
    
    - (IBAction)loadXmlToDataObject:(id)sender;
    - (IBAction)parseXml:(id)sender;
    Relevant Body:
    Code:
    - (IBAction)loadXmlToDataObject:(id)sender {
    	
    	NSBundle *thisBundle = [NSBundle bundleForClass:[self class]];
    	
            pathName = [thisBundle pathForResource:@"AroundTheDotNet=xml"	ofType:@"xml"];
    	NSLog(@"'pathName' NSString variable contains: %@", pathName);
    	
    	xmlData = [NSData dataWithContentsOfFile:pathName];
    	NSLog(@"xmlData NSData object successfully loaded with XML File");
    	
    }
    
    - (IBAction)parseXml:(id)sender {
    	
    	[b]NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:xmlData];[/b]
    	NSLog(@"Data Loaded to xmlParser");
    	
    	/* Other stuff.....*/
    
    } 
    1. thisBundle variable is disappearing, and the pathName which relies on the NSBundle thus ends up pointing to nothing, which is perpetuated - right?

    2. OR is it that I am not initializing pathName and xmlData in an init method - in which case, why does the first method not serve that purpose?

    3. If the XML file is local (in my resources) - is there an easier way to load it into NSData without using the bundle and string variables at all?

    I apologize if this is all very obvious - thanks for any and all help / critique.
     
  2. mfram macrumors 65816

    Joined:
    Jan 23, 2010
    Location:
    San Diego, CA USA
    #2
    The first thing that strikes me a little odd is that your overall object is listed as an NSXMLParserDelegate which probably isn't correct. You'd need some kind of Application Controller object to deal with messages from the UI. Probably an NSObject subclass.

    According to the docs, NSXMLParser is a SAX-style XML parser. Is this what you really want? Or do you want more of a DOM-style parser?

    I'm going to assume you really want the SAX-style parser. You will need an NSXMLParser delegate to get messages from the NSXMLParser. That would seem to me to be a different class you'd have to write as a subclass to NSXMLParserDelegate.

    The parser code you have in bold seems reasonable to me. Did you check in the debugger to see if the xmlData variable actually has the data from the file you expect it to? Or is xmlData still zero? If xmlData is zero, I would see how the initWithData method would fail.
     
  3. hubrisForAll thread starter macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #3
    And after nearly finishing Hillegass I still cant' answer this - can I not use the default application delegate as the NSXMLParser delegate as well?

    Not sure - I have read through the event-based (SAX) parser programming guide but by the end decided - one step at a time - I would get basic functionality then hope that the correct one to use would reveal itself :)



    Definately fails out, and through adding logs and attempting to print out each variable - I'm near positive it is related to the reference to the bundle is disappearing.
     
  4. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #4
    Read the memory management guide:
    http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

    You don't own the objects pointed to by:
    pathName
    xmlData

    You don't own the object pointed to by thisBundle, but that's local and doesn't get reused, so it's fine for that to be freed.

    The names of the methods that return these pointers don't have new, alloc, or copy. They are autoreleased. They will go away. You need to retain them to keep them (and release them if/when you finish with them).

    -Lee
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    This line assigns an object to the pathName ivar. Unfortunately, it doesn't claim ownership of the object. So at some time in the near future, that object will be dealloc'ed. Havoc ensues.

    Code:
    	xmlData = [NSData dataWithContentsOfFile:pathName];
    
    Same thing here. You make an object, assign it to an ivar, and don't claim ownership.

    You need to reread the Memory Management Guide and burn the basic rules into your brain. Ownership is fundamental, and if you don't know when to claim ownership and relinquish it, you'll never be able to program effectively with retain/release.

    You can forego learning the rules by switching to Garbage Collected memory management. You still need to read the Guide, though.
    http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html


    From a design standpoint, I don't see why the string and NSData are ivars. They could just as easily be local vars in parseXml. It's not like you're using any parameters to the action method. Local vars doesn't necessarily mean you can avoid reviewing the Memory Management Guide. I'm just wondering why there are variables that have to live across multiple methods, when limiting them to a single method is so much simpler.


    You still have to use the bundle and string, but the variables don't have to be ivars. They can be local to the method that needs them.

    I think you should try eliminating the ivars and using local vars only. Try your best and if you get stuck, post again including your code and a description of what happened.
     
  6. hubrisForAll thread starter macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #6
    First off - thank you so much for helping - it's easy to get discouraged learning something as different as this (I'm a non-programmer, only done basic PHP prior to this) and it means ALOT to be able to get feedback.

    I'm actually aware of it - I've been reading alot of books and guidelines. It just got to the point where I was going through so many tutorials I wasn't sure what I knew and didn't - I thought working on some of my basic Ideas would help me get a better grasp on the concepts I was having trouble with.

    Agree - and as above - I'm mainly splitting this up, with buttongs and NSLog statements - to try and see what I can and can't do, and get feedback along the way. Just training programs - I expect my final program to be much more streamlined and 'stylelish'.

    Here is what I updated:

    Header:
    Code:
    @interface GoodNoosAppDelegate : NSObject <NSApplicationDelegate, NSXMLParserDelegate> {
        NSWindow *window;
    	
        NSXMLParser *xmlParser;
    	
    	// TO CARRY ACROSS METHODS
       [b] NSString *pathName;
        NSBundle *thisBundle;
        NSData *xmlData;[/b]
    
    }

    Relevant Body:
    Code:
    - (IBAction)loadXmlToDataObject:(id)sender {
    	
    	thisBundle = [[b][NSBundle alloc] [/b]bundleForClass:[self class]];
    	[b][thisBundle retain];[/b]
    	
    	pathName = [thisBundle pathForResource:@"AroundTheDotNet=xml"	ofType:@"xml"];
    	[b][pathName retain];[/b]
    	
    	xmlData = [[b][NSData alloc] [/b]dataWithContentsOfFile:pathName];
    	[b][xmlData retain];[/b]
    	
    }
    
    
    - (IBAction)parseXml:(id)sender {
    	
    	xmlParser = [[b][NSXMLParser alloc][/b] initWithData:xmlData];
    	NSLog(@"Data Loaded to xmlParser");
    ....
    
    /* release all previous retains */
    
    

    So (this runs without error) - next Question:

    If I DID NOT alloc and retain thisBundle, but then alloc'ed and retained NSData - would this be sufficient? Moreover - could i kill the 'thisBundle' and 'pathName' ivar's and get by with just the xmlData ivar (retain'ing it) to carry it over to the next method?

    Do I even need to alloc NSXMLParser if I don't use it beyond the second method (I don't think I do, just checking).

    Again - I'm splitting the methods (edited out NSLog statements) just to learn what absolutely must be done and when / where - I know it can all be done in a simpler manner (I"ll update it at that point)

    Again - any and ALL help / Critique appreciated! Thanks!!!
     
  7. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #7
    It looks to me like thisBundle and pathName can be entirely local to that method. No instance variable needed, no retains needed. Just retain the final product (the data) to keep it around.

    <edit>
    Also, a suggestion: rather than loading the data yourself, you can use [thisBundle URLForResource:mad:"AroundTheDotNet=xml" withExtension:mad:"xml"], then pass that url to [[NSXMLParser alloc] initWithURL:url]. NSXMLParser will handle loading the data on its own, and may well be able to do so more efficiently since it knows what it'll be using it for.
    </edit>
     

Share This Page