Behaviour of awakeFromNib

Discussion in 'Mac Programming' started by DannySmurf, Jul 12, 2005.

  1. DannySmurf macrumors 6502a

    Joined:
    Jul 7, 2005
    #1
    Okay, I've got a problem here that I just don't understand. Maybe this is just the assumed behaviour, because I can't find thing one about it in any of Apple's documentation or on the web, but I'm baffled as to why this would happen, or why anyone would choose this as the default behaviour.

    I have two data objects in my app's controller class, folderList and dataStore. They're initialized and deserialized from disk in the class' init function. I'm also implementing awakeFromNib in this class.

    Now, as soon as control exits awakeFromNib, both folderList and dataStore become invalid. They're not nil, but it's like they haven't been initialized at all. DURING execution of awakeFromNib, they're valid and populated. But as soon as control passes out of awakeFromNib, they're empty and uninitialized.

    Can anyone shed some light on this? It seems to me that using awakeFromNib shouldn't be blasting the other data in my class, but like I said, maybe I'm not understanding how all the different initializations that go on in an app work.
     
  2. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #2
    That sounds like an autorelease problem. When an object is autoreleased, it can be used in the current method, but will be released when control returns to the application object (when the autorelease pool is emptied). Most methods return autoreleased objects, so you will need to retain them if you want to use them outside the scope of the current method.

    For more information about autorelease, see Memory Management: Object Ownership and Disposal.
     
  3. DannySmurf thread starter macrumors 6502a

    Joined:
    Jul 7, 2005
    #3
    Maybe I should elaborate on that. I don't think this is an autorelease problem, because none of my objects are being autoreleased, and I'm not getting anything back from another function.

    If I exclude the awakeFromNib (in other words, everything is done in the init function), things work fine. But as soon as I add awakeFromNib to my code -- even if I don't add any implementation (awakeFromNib is EMPTY) -- my objects are invalid once awakeFromNib returns.

    I need to use awakeFromNib for some initialization on something in the NIB file, so the obvious solution (just don't use awakeFromNib) isn't an option.
     
  4. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #4
    That's very strange. Can you post the code from your init method, as it might provide some clues?
     
  5. DannySmurf thread starter macrumors 6502a

    Joined:
    Jul 7, 2005
    #5
    Here's the init function. folderList, dataStore and folderImage are all released in the class' release function.

    Code:
    - (id)init
    {
    	folderList = [[FolderList alloc] init];
    	[folderList deserialize];
    	
    	dataStore = [[DataStore alloc] init];
    	[dataStore deserialize];
    	
    	folderImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"folder" ofType:@"png"]];
    	
    	return self;
    }
    
    If I add this bit of code right above the init function, once initialization is finished and my code gets control, folderList and dataStore are empty (and consequently, the app that was working just fine using only init crashes in several places).

    Code:
    - (void)awakeFromNib
    {
    }
    
     
  6. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #6
    After some experimenting, I think I found the problem. You say you are releasing your variables in the controller's release method, however you should put this in the class's dealloc method, not release. Sending a release message decrements the reference count of an object, and once this count reaches 0, release sends a dealloc message to the object. Therefore, if an object has been retained multiple times, sending a release method shouldn't cause the object's variables to be released. However, if you release your variables in the release method as you are doing, they will be unusable after the controller receives just one release message, even though its reference count might still be greater than 0.

    The reason the application will only crash when you include awakeFromNib is that your controller is retained and released when the method is included. To fix your problem, just rename the release method to dealloc.

    On another note, although unrelated to the problem, you're not calling [super init] from your init method. It's a good habit to write init methods in the following format, which could prevent a couple of potential problems in the future:

    Code:
    - init
    {
        if (self = [super init]) {
            /* class-specific initialization goes here */
        }
        return self;
    }
     
  7. caveman_uk Guest

    caveman_uk

    Joined:
    Feb 17, 2003
    Location:
    Hitchin, Herts, UK
    #7
    I was reading in Wil Shipley's blog that you shouldn't reassign self.
    He says you should use a form along the lines of
    This raised quite a few comments in the blog about whether this was right. To be honest I've always used the self=[super init] form and it's never bitten me. Wil is a rather opinionated so-and-so but he has an awful lot more experience than me so I'm prepared to believe him.

    He also has interesting comments on the retain-release stuff you do in KVC setters. He says to check if the objects are equal before you do a retain-release as retain and release are 'expensive' and you want to do as little of that as you can.
     
  8. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #8
    Interesting read. His arguments appear to state that "self=" isn't necessary because only in extremely rare cases (which most programmers will never encounter) will [super init] return a different value. While this is true, using "self=" should never have any problems, and it is the method used in Apple documentation.

    The way I see it, both methods will work and it's mostly a matter of personal preference which one you use, although "self=[super init]" may be slightly more future compatible.
     
  9. whooleytoo macrumors 603

    whooleytoo

    Joined:
    Aug 2, 2002
    Location:
    Cork, Ireland.
    #9
    Another side to the "self = [super init]" debate:

    here
     
  10. caveman_uk Guest

    caveman_uk

    Joined:
    Feb 17, 2003
    Location:
    Hitchin, Herts, UK
    #10
    He seems pretty certain...

    From one of Wil's comments in his blog
     
  11. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #11
    It seems like he's arguing that "[super init]" is as good as "self=[super init]", while others claim it may not be. Without providing any examples of why leaving out "self=" is actually better (the performance loss (if any) of using "self=" is trivial and it's no less readable to anyone with much experience), I don't see any reason to change.

    Claiming A is better than B because A is almost as good as B doesn't make much sense to me. :rolleyes:
     
  12. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #12
    One small reason I like "if (![super init]) { return nil; }" better is because it keeps all your initialization code buttud up as far left as possible instead of indented. And the setter check for equivalency first instead of just retaining the new value seems a tad silly as well. I really don't think it's that expensive to send a retain: message, all it's doing is incrementing an integer, right? Like the init thing, it's not wrong, but it doesn't seem necessarily superior. Wil certainly knows more about Objective-C than I do, but I also think he just likes to hear himself talk and he's a bit too aggressive in setting himself up as The Ultimate Programming Authority for my taste. :rolleyes:
     
  13. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #13
    And it's got to call [super dealloc] at the end. Doesn't he also need to retain those objects when he creates them in the init method?
     
  14. HexMonkey Administrator

    HexMonkey

    Staff Member

    Joined:
    Feb 5, 2004
    Location:
    New Zealand
    #14
    True, although you could combine the two with "if (!(self=[super init])) { return nil; }"

    No, they are created with [[<Class> alloc] init], so don't need to be retained.
     
  15. caveman_uk Guest

    caveman_uk

    Joined:
    Feb 17, 2003
    Location:
    Hitchin, Herts, UK
    #15
    I think you might just be right there ;)
     
  16. DannySmurf thread starter macrumors 6502a

    Joined:
    Jul 7, 2005
    #16
    Aha! It seems you're right. Changing it to dealloc fixed the problem. Looks like I've been going about my designs all wrong. The tutorials I've been reading have led me to believe that release is the "destructor" for a class and all the teardown code should go in there (I didn't even know there was such a thing as dealloc), and I've been writing all of my code on that assumption. I'll have to make some changes, apparently.
     
  17. caveman_uk Guest

    caveman_uk

    Joined:
    Feb 17, 2003
    Location:
    Hitchin, Herts, UK
    #17
    Check out this link for more info on this.
     
  18. whooleytoo macrumors 603

    whooleytoo

    Joined:
    Aug 2, 2002
    Location:
    Cork, Ireland.
    #18
    True, but don't confuse "certainty" with correctness. ;)
     
  19. DannySmurf thread starter macrumors 6502a

    Joined:
    Jul 7, 2005
    #19
    Good article. Thanks for the link. :)
     

Share This Page