Re: Outlets / IBOutlet declarations (was Re: Interface Builder & Wiring Objects)

  • If you write correct accessors for all outlets, then the retain/release memory management is entirely handled in one method.  the -set retains the new value and releases the old value.
     
    Any confusion regarding memory management for IB outlets seems to stem from failure to write the accessors and reliance on syntactic sugar.  Just remember that
     
    @property (nonatomic, retain) IBOutlet UILabel *label;
     
    results in synthesis of the appropriate accessor methods.  It does nothing more than save you some typing and document your intention.  The -set method that results from the above property declaration retains the new value and releases the old value.  The memory management is therefore all handled in one place just the way it should be and the way you want.
     
    So what's the problem again ?
  • On Nov 18, 2008, at 9:15 AM, Erik Buck wrote:

    > If you write correct accessors for all outlets, then the retain/
    > release memory management is entirely handled in one method.  the -
    > set retains the new value and releases the old value.
    >
    > Any confusion regarding memory management for IB outlets seems to
    > stem from failure to write the accessors and reliance on syntactic
    > sugar.  Just remember that
    >
    > @property (nonatomic, retain) IBOutlet UILabel *label;
    >
    > results in synthesis of the appropriate accessor methods.  It does
    > nothing more than save you some typing and document your intention.
    > The -set method that results from the above property declaration
    > retains the new value and releases the old value.  The memory
    > management is therefore all handled in one place just the way it
    > should be and the way you want.
    >
    > So what's the problem again ?

    OK Erik, I'll bite.  What you describe above is correct as far as it
    goes.  However, when you say all the memory management is handled in
    one place, of course it's two.  The object has to be released.  The
    normal place to release objects is in their owner's dealloc method,
    and this also applies to outlets.

    However, UIViewController has the ability to unload its view outlet in
    response to a memory warning.  Any subclass should also release its
    outlets in response to the memory warning, if the base class releases
    its view, but not otherwise.  So now there are three places to manage
    the memory of these outlets. The problem is that the base class
    doesn't always release its view in response to a memory warning and as
    far as I can tell the subclass has no clean way of telling if the view
    will be released or has been released.  That's the problem.

    --
    Brian Stern
    <brians99...>
  • Brian,

    The way to handle this is to _not_ respond to memory warnings in
    subclasses (at least not for the purposes of view outlet handling -
    there may be other non-view memory you want to free up in response to
    a memory warning). Instead, implement -setView: in your
    UIViewController subclass, and release outlets when the argument is
    nil. For example:

    - (void)setView:(UIView *)aView;
    {
    if (!aView) {
      self.anOutlet = nil;
      self.anotherOutlet = nil;
      self.thirdOutlet = nil;
    }
    [super setView:aView];
    }

    This will correctly clean up all of your outlets whenever the
    UIViewController unloads its view, and not otherwise.

    Hope this helps,
    - Greg

    On Nov 18, 2008, at 10:01 AM, Brian Stern wrote:
    >
    > OK Erik, I'll bite.  What you describe above is correct as far as it
    > goes.  However, when you say all the memory management is handled in
    > one place, of course it's two.  The object has to be released.  The
    > normal place to release objects is in their owner's dealloc method,
    > and this also applies to outlets.
    >
    > However, UIViewController has the ability to unload its view outlet
    > in response to a memory warning.  Any subclass should also release
    > its outlets in response to the memory warning, if the base class
    > releases its view, but not otherwise.  So now there are three places
    > to manage the memory of these outlets. The problem is that the base
    > class doesn't always release its view in response to a memory
    > warning and as far as I can tell the subclass has no clean way of
    > telling if the view will be released or has been released.  That's
    > the problem.
    >
    > --
    > Brian Stern
    > <brians99...>
  • Thanks Greg,

    That does work and has no bad side effects.  I can leave in the retain
    properties and release the outlets in the view controller's dealloc
    method.

    Thanks,

    Brian

    On Nov 18, 2008, at 1:19 PM, Greg Titus wrote:

    > Brian,
    >
    > The way to handle this is to _not_ respond to memory warnings in
    > subclasses (at least not for the purposes of view outlet handling -
    > there may be other non-view memory you want to free up in response
    > to a memory warning). Instead, implement -setView: in your
    > UIViewController subclass, and release outlets when the argument is
    > nil. For example:
    >
    > - (void)setView:(UIView *)aView;
    > {
    > if (!aView) {
    > self.anOutlet = nil;
    > self.anotherOutlet = nil;
    > self.thirdOutlet = nil;
    > }
    > [super setView:aView];
    > }
    >
    > This will correctly clean up all of your outlets whenever the
    > UIViewController unloads its view, and not otherwise.
    >
    > Hope this helps,
    > - Greg
    >
    > On Nov 18, 2008, at 10:01 AM, Brian Stern wrote:
    >>
    >> OK Erik, I'll bite.  What you describe above is correct as far as
    >> it goes.  However, when you say all the memory management is
    >> handled in one place, of course it's two.  The object has to be
    >> released.  The normal place to release objects is in their owner's
    >> dealloc method, and this also applies to outlets.
    >>
    >> However, UIViewController has the ability to unload its view outlet
    >> in response to a memory warning.  Any subclass should also release
    >> its outlets in response to the memory warning, if the base class
    >> releases its view, but not otherwise.  So now there are three
    >> places to manage the memory of these outlets. The problem is that
    >> the base class doesn't always release its view in response to a
    >> memory warning and as far as I can tell the subclass has no clean
    >> way of telling if the view will be released or has been released.
    >> That's the problem.
    >>
    >> --
    >> Brian Stern
    >> <brians99...>
    >>
  • On Nov 18, 2008, at 10:01 AM, Brian Stern wrote:

    > OK Erik, I'll bite.  What you describe above is correct as far as it
    > goes.  However, when you say all the memory management is handled in
    > one place, of course it's two.  The object has to be released.  The
    > normal place to release objects is in their owner's dealloc method,
    > and this also applies to outlets.
    >
    This is standard (best) practice for memory management.  It's not
    clear what the problem is here.
    The only difference with outlets is that, if you don't follow best
    practice, you have to spend some time thinking about whether or not
    you have to release in dealloc (more on this below).

    > However, UIViewController has the ability to unload its view outlet
    > in response to a memory warning.  Any subclass should also release
    > its outlets in response to the memory warning, if the base class
    > releases its view, but not otherwise.  So now there are three places
    > to manage the memory of these outlets. The problem is that the base
    > class doesn't always release its view in response to a memory
    > warning and as far as I can tell the subclass has no clean way of
    > telling if the view will be released or has been released.  That's
    > the problem.
    >
    You're shifting the goalposts; this is not the problem you originally
    described.
    In fact there have been several problems: You didn't understand the
    documentation as written; in testing the implementation of outlets you
    made a fundamental mistake; and -- as noted in my original message in
    this sub-thread -- the "situation" with outlets has heretofore been "a
    mess".

    To reiterate points that Jon and Joar have made:

    You shouldn't have to think about something as mundane as "how do I
    manage memory for outlets?".  There are far more interesting issues
    for a programmer to spend their time on.  This is the reason for
    proposing a new "best practice" that will address all situations
    simply and easily.  *If* you know what you're doing -- and your
    "experiment" has shown that even relatively experienced developers may
    sometimes not "know what they're doing" -- then it is perfectly
    possible to deviate from the suggested pattern, but you then do this
    at your own risk.

    I do, though, have to apologise for introducing a wrinkle (the problem
    of UIViewController's response to memory warnings) and then providing
    an incorrect solution.  I managed to mistake a question from some time
    ago for the answer -- mainly because I never received an answer.  I
    have now and can relay part of it (note to self: <rdar://problem/
    5834347>).  There are several issues:

    The template clearly indicates what should be done:

    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning]; // Releases the view if it
    doesn't have a superview
        // Release anything that's not essential, such as cached data
    }

    In theory, you should simply check whether the view has a superview:

    - (void)didReceiveMemoryWarning {
        if ([_view superview] == nil) {
            // set outlets to nil
        }
        [super didReceiveMemoryWarning];
    }

    but since _view is declared as @package, you can't do that.
    You could invoke 'view':

    - (void)didReceiveMemoryWarning {
        if ([self.view superview] == nil) {
            // set outlets to nil
        }
        [super didReceiveMemoryWarning];
    }

    but this has the disadvantages that (a) in some situations it will
    cause the view to be loaded before it is subsequently unloaded, and
    (b) it isn't future proof.

    This leaves us for now with two solutions:
    (a) Greg's (override setView:) which is more future-proof but is in
    many respects academically unsatisfying.
    (b) For non-top-level-object, specify an assign attribute for the
    property -- and risk dangling pointers.

    It may be that a better approach will present itself time...

    mmalc
  • On Nov 19, 2008, at 3:59 AM, mmalcolm crawford wrote:

    >
    > On Nov 18, 2008, at 10:01 AM, Brian Stern wrote:
    >
    >> OK Erik, I'll bite.  What you describe above is correct as far as
    >> it goes.  However, when you say all the memory management is
    >> handled in one place, of course it's two.  The object has to be
    >> released.  The normal place to release objects is in their owner's
    >> dealloc method, and this also applies to outlets.
    >>
    > This is standard (best) practice for memory management.  It's not
    > clear what the problem is here.
    > The only difference with outlets is that, if you don't follow best
    > practice, you have to spend some time thinking about whether or not
    > you have to release in dealloc (more on this below).

    There are competing issues.  Following this best practice forces me to
    add public properties for all my outlets that my code never uses.
    This violates encapsulation and seems wasteful and error-prone.
    That's the reason I didn't do this earlier.  The one little paragraph
    in the documentation that addresses this says I 'should' do it this
    way but doesn't explain why.  I'll point out that the paragraph that
    explains memory management of outlets on Mac OS X on that page is half
    the length of the iPhone paragraph, and I'd maintain that the iPhone
    paragraph isn't long enough.

    >> However, UIViewController has the ability to unload its view outlet
    >> in response to a memory warning.  Any subclass should also release
    >> its outlets in response to the memory warning, if the base class
    >> releases its view, but not otherwise.  So now there are three
    >> places to manage the memory of these outlets. The problem is that
    >> the base class doesn't always release its view in response to a
    >> memory warning and as far as I can tell the subclass has no clean
    >> way of telling if the view will be released or has been released.
    >> That's the problem.
    >>
    > You're shifting the goalposts; this is not the problem you
    > originally described.

    It's not me who has shifted the goalposts.  The whole playing field
    was moved out from under me when it was decided to retain outlets by
    default, which is different from how this all works on Mac OS X.

    I'll admit that I was confused and probably a few other things a few
    days ago when I discovered that a huge memory leak was caused by
    outlets being retained behind my back.  And then I got a second kick
    in the shorts when I read your post pointing out that outlets need to
    be released in didReceiveMemoryWarning, which hadn't occurred to me
    was a side effect of their being retained.

    > In fact there have been several problems: You didn't understand the
    > documentation as written; in testing the implementation of outlets
    > you made a fundamental mistake; and -- as noted in my original
    > message in this sub-thread -- the "situation" with outlets has
    > heretofore been "a mess".

    You can add to your list of problems 'insufficient documentation and
    education of memory management of outlets.'  If you want to believe
    that I'm just one yokel developer who can't read and understand the
    documentation that's your right.  I'm sure I'm not the only one.

    >
    > To reiterate points that Jon and Joar have made:
    >
    > You shouldn't have to think about something as mundane as "how do I
    > manage memory for outlets?".  There are far more interesting issues
    > for a programmer to spend their time on.

    I couldn't agree with you more.

    > This is the reason for proposing a new "best practice" that will
    > address all situations simply and easily.  *If* you know what you're
    > doing -- and your "experiment" has shown that even relatively
    > experienced developers may sometimes not "know what they're doing"
    > -- then it is perfectly possible to deviate from the suggested
    > pattern, but you then do this at your own risk.
    >
    >
    > I do, though, have to apologise for introducing a wrinkle (the
    > problem of UIViewController's response to memory warnings) and then
    > providing an incorrect solution.  I managed to mistake a question
    > from some time ago for the answer -- mainly because I never received
    > an answer.  I have now and can relay part of it (note to self: <rdar://problem/5834347
    >> ).

    I guess even 'relatively experienced developers may sometimes not
    "know what they're doing"'

    > There are several issues:
    >
    > The template clearly indicates what should be done:

    Obviously the client code can't touch _view or self.view in
    didReceiveMemoryWarning.  That's why I said upthread that there's no
    clean way for the client code to tell if the view will be unloaded or
    has been unloaded.

    > In theory, you should simply check whether the view has a superview:
    > but since _view is declared as @package, you can't do that.
    > You could invoke 'view':
    > but this has the disadvantages that (a) in some situations it will
    > cause the view to be loaded before it is subsequently unloaded, and
    > (b) it isn't future proof.

    self.view will always be non-nil.

    This is why I 'shifted the goalposts.'  There are many issues related
    to memory management of outlets on iPhone.  Suggesting that everyone
    just follow your best practice described at the top obviously isn't
    sufficient.

    >
    > This leaves us for now with two solutions:
    > (a) Greg's (override setView:) which is more future-proof but is in
    > many respects academically unsatisfying.
    > (b) For non-top-level-object, specify an assign attribute for the
    > property -- and risk dangling pointers.

    The override of setView is very similar to the viewDidUnload callback
    that I proposed as a solution for this.  It has its own further
    issues.  UIViewController calls [setView:nil] from its dealloc method
    so the subclass has to be prepared for this.  What I've done is to add
    all the public retain properties for all the outlets.  Additionally
    I've added a

    -(void)releaseOutlets

    method that uses the properties and sets them all to nil.  I call this
    method from my subclass's dealloc method and setView: override.  That
    way I only have one place to write the code that releases all the
    outlets.

    Brian

    --
    Brian Stern
    <brians99...>
  • On Nov 19, 2008, at 7:00 AM, Brian Stern wrote:
    >>
    >> This leaves us for now with two solutions:
    >> (a) Greg's (override setView:) which is more future-proof but is in
    >> many respects academically unsatisfying.
    >> (b) For non-top-level-object, specify an assign attribute for the
    >> property -- and risk dangling pointers.
    >
    > The override of setView is very similar to the viewDidUnload
    > callback that I proposed as a solution for this.  It has its own
    > further issues.  UIViewController calls [setView:nil] from its
    > dealloc method so the subclass has to be prepared for this.  What
    > I've done is to add all the public retain properties for all the
    > outlets.  Additionally I've added a
    >
    > -(void)releaseOutlets
    >
    > method that uses the properties and sets them all to nil.  I call
    > this method from my subclass's dealloc method and setView:
    > override.  That way I only have one place to write the code that
    > releases all the outlets.

    Brian,

    What is your reason for having a separate -releaseOutlets method to do
    this?

    The sample that I gave (calling self.anOutlet = nil; when the argument
    to -setView: is nil) will do whatever releasing is required (if the
    anOutlet property is "retain") or simple zeroing the pointer (if the
    anOutlet property is "assign"). The fact that UIViewController calls -
    setView:nil from its -dealloc is just additional convenience. All of
    your outlets get cleaned up at the same time as the UIViewController
    cleans up the main view. There is no need to write any outlet related
    code in your -dealloc at all, since it is all handled via the
    superclass and the -setView: override.

    In short, the way I think of it, -setView: _IS_ the one centralized
    place that deals with all view-related pointers, including all
    outlets. And UIViewController calls -setView: at all the necessary
    times (setup, memory warning, dealloc), so you don't have to do
    anything else.

    Hope this helps,
    - Greg
  • On Nov 19, 2008, at 10:29 AM, Greg Titus wrote:

    >
    > On Nov 19, 2008, at 7:00 AM, Brian Stern wrote:
    >>>
    >>> This leaves us for now with two solutions:
    >>> (a) Greg's (override setView:) which is more future-proof but is
    >>> in many respects academically unsatisfying.
    >>> (b) For non-top-level-object, specify an assign attribute for the
    >>> property -- and risk dangling pointers.
    >>
    >> The override of setView is very similar to the viewDidUnload
    >> callback that I proposed as a solution for this.  It has its own
    >> further issues.  UIViewController calls [setView:nil] from its
    >> dealloc method so the subclass has to be prepared for this.  What
    >> I've done is to add all the public retain properties for all the
    >> outlets.  Additionally I've added a
    >>
    >> -(void)releaseOutlets
    >>
    >> method that uses the properties and sets them all to nil.  I call
    >> this method from my subclass's dealloc method and setView:
    >> override.  That way I only have one place to write the code that
    >> releases all the outlets.
    >
    >
    > Brian,
    >
    > What is your reason for having a separate -releaseOutlets method to
    > do this?
    >
    > The sample that I gave (calling self.anOutlet = nil; when the
    > argument to -setView: is nil) will do whatever releasing is required
    > (if the anOutlet property is "retain") or simple zeroing the pointer
    > (if the anOutlet property is "assign"). The fact that
    > UIViewController calls -setView:nil from its -dealloc is just
    > additional convenience. All of your outlets get cleaned up at the
    > same time as the UIViewController cleans up the main view. There is
    > no need to write any outlet related code in your -dealloc at all,
    > since it is all handled via the superclass and the -setView: override.

    I think that you're probably right and that it's redundant.  Needless
    to say, mmalc's best practice has the outlets being released in
    dealloc.  That's why I had that code there in the first place.

    FWIW, I don't think that UIViewController is documented as setting its
    view to nil from its dealloc method.  Frankly I don't know why it does
    it. I don't think it needs to.  It could certainly change in the
    future.  In fact another best practice (I think this is from mmalc) is
    to not set properties to nil in dealloc but to simply release them.
    If this changed the retained outlets would leak.

    I don't feel strongly about this, but not having the release code in
    my dealloc method makes me a little nervous.  I do understand that my
    releaseOutlets method gets called twice when the object is being
    dealloced.

    > In short, the way I think of it, -setView: _IS_ the one centralized
    > place that deals with all view-related pointers, including all
    > outlets. And UIViewController calls -setView: at all the necessary
    > times (setup, memory warning, dealloc), so you don't have to do
    > anything else.

    I certainly had never thought of any reason to override this accessor
    before this issue came up.  loadView and viewDidLoad are the normal
    override points for view creation and there are others for view
    appearance and disappearance, as I'm sure you know.  It's just that
    there's no comparable override point for the view being unloaded, even
    though that's a normal part of view controller life on iPhone.

    Brian
  • On Nov 19, 2008, at 10:13 AM, Brian Stern wrote:

    > I think that you're probably right and that it's redundant.
    > Needless to say, mmalc's best practice has the outlets being
    > released in dealloc.  That's why I had that code there in the first
    > place.
    >
    > FWIW, I don't think that UIViewController is documented as setting
    > its view to nil from its dealloc method.  Frankly I don't know why
    > it does it. I don't think it needs to.  It could certainly change in
    > the future.  In fact another best practice (I think this is from
    > mmalc) is to not set properties to nil in dealloc but to simply
    > release them.  If this changed the retained outlets would leak.

    But if your dealloc is like this...

    - (void)dealloc
    {
        self.thisProp = nil;
        self.thatProp = nil;
        ...
    }

    then you'll be calling the accessors and the right thing will happen
    (i.e. release if necessary).  This is what I currently do for all dev
    work (iPhone or Mac OS X).  In fact, I use accessors everywhere and
    never do direct ivar access.

    > I don't feel strongly about this, but not having the release code in
    > my dealloc method makes me a little nervous.  I do understand that
    > my releaseOutlets method gets called twice when the object is being
    > dealloced.

    This has been a very interesting read.  I currently do not have any
    special code in place to handle low-memory stuff.

    >
    >> In short, the way I think of it, -setView: _IS_ the one centralized
    >> place that deals with all view-related pointers, including all
    >> outlets. And UIViewController calls -setView: at all the necessary
    >> times (setup, memory warning, dealloc), so you don't have to do
    >> anything else.
    >
    > I certainly had never thought of any reason to override this
    > accessor before this issue came up.  loadView and viewDidLoad are
    > the normal override points for view creation and there are others
    > for view appearance and disappearance, as I'm sure you know.  It's
    > just that there's no comparable override point for the view being
    > unloaded, even though that's a normal part of view controller life
    > on iPhone.

    From Apple's template code, didReceiveMemoryWarning does have these
    code comments generated:

    [super didReceiveMemoryWarning]; // Releases the view if it doesn't
    have a superview
    // Release anything that's not essential, such as cached data

    So, one should release all outlets that are tied to the view _iff_ it
    doesn't have a superview.  And then of course purge all support
    objects that would not be of any use.

    If a view ends up not being loaded due to error warnings, it could be
    loaded at a later point in time.  Assuming there's enough memory at
    that point, your loadView will once again be called.

    Personally, if any of my instances do get sent the
    didReceiveMemoryWarning message and there is no superview, I'll just
    purge everything.  I don't yet see any need to have only some objects
    live.

    Having said that, I'll probably adopt the following:

    - (void)dealloc
    {
        [self myCleanup];
        [super dealloc];
    }

    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        if ([self.view superview] == nil)
            {
            [self myCleanup];
            }
        else
            {
            // in some cases, I lazily load objects (dictionarys, views,
    etc.)
            // such objects could also be purged here.  If needed again,
    they'll be
            // lazily loaded once more.
            }
    }

    - (void)myCleanup
    {
        self.thisProp = nil;
        ...
    }

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • > But if your dealloc is like this...
    >
    > - (void)dealloc
    > {
    > self.thisProp = nil;
    > self.thatProp = nil;
    > ...
    > }
    >
    > then you'll be calling the accessors and the right thing will happen
    > (i.e. release if necessary).  This is what I currently do for all dev
    > work (iPhone or Mac OS X).  In fact, I use accessors everywhere and
    > never do direct ivar access.

    My understanding (and I'm a noob in this) is that "best practices"
    recommend that you shouldn't start sending additional messages to an
    object from inside its dealloc.

    Those property accesses are just candy-coated calls to setThisProp:, etc
    which violates the 'don't send message to yourself' recommendation.

    Anyone want to clarify if it is/isn't safe to do this?  Certainly most
    of the samples I've seen go out of their way to release instance
    variables rather than nil properties, in their dealloc's

    (This is the one thing I hate the *most* about properties - they really
    feel glued on, at this point, rather than being a language feature that
    the "whole compiler" knows about)
  • On Nov 19, 2008, at 4:41 PM, Ricky Sharp wrote:

    >
    > From Apple's template code, didReceiveMemoryWarning does have these
    > code comments generated:
    >
    > [super didReceiveMemoryWarning]; // Releases the view if it doesn't
    > have a superview
    > // Release anything that's not essential, such as cached data
    >
    > So, one should release all outlets that are tied to the view _iff_
    > it doesn't have a superview.  And then of course purge all support
    > objects that would not be of any use.
    >
    > Having said that, I'll probably adopt the following:
    >
    > - (void)didReceiveMemoryWarning
    > {
    > [super didReceiveMemoryWarning];
    > if ([self.view superview] == nil)
    > {

    The problem with this code is that the view accessor causes the view
    to be loaded if it's not already loaded.  So simply by asking for
    self.view.superview you may be causing your entire view hierarchy to
    be loaded in from the nib.  This doesn't seem to be a good thing to do
    inside a memory warning.  Besides that, the code you showed is wrong.
    You have to have the call to super at the end of the method after you
    call self.view.superview, otherwise you're guaranteeing that the view
    will be loaded every time your method is called.

    This is not just an academic point.  The way that many iPhone apps are
    constructed with navbars means that you've got a root view and then
    one or more other view controllers that get pushed.  The root view
    will participate in all memory warnings for the life of the app and
    may be unloaded many times.  And that's all fine.  It just doesn't
    make any sense to load it in response to a memory warning.

    That's why I said upthread that self.view will never be non-nil.

    I'm starting to think that maybe using the assign properties is the
    better way to handle this.

    --
    Brian Stern
    <brians99...>
  • On Nov 19, 2008, at 7:00 AM, Brian Stern wrote:
    > There are competing issues.  Following this best practice forces me
    > to add public properties for all my outlets that my code never
    > uses.  This violates encapsulation and seems wasteful and error-prone.
    >

    No, it's not.  The nib-loading mechanism uses these methods.
    It is precisely by using these methods that you avoid violation of
    encapsulation (notably on the desktop, where otherwise the instance
    variables may be set directly).
    Further, as others noted, if you want to avoid declaring the
    properties publicly as read/write, you can declare then as read-only
    then re-declare them as read/write in a private extension.
    Moreover it precisely avoids errors by providing a consistent pattern
    that will work across all platforms and that you can use without
    having to think about it.  As we seem to agree, having to think about
    how to declare/use outlets per se is a waste of mental effort.

    > That's the reason I didn't do this earlier.  The one little
    > paragraph in the documentation that addresses this says I 'should'
    > do it this way but doesn't explain why.
    >
    Because it should be obvious from a memory management perspective why
    this is the case.  And the documentation typically tries to avoid
    repeating the basic rules of memory management.

    > I'll point out that the paragraph that explains memory management of
    > outlets on Mac OS X on that page is half the length of the iPhone
    > paragraph, and I'd maintain that the iPhone paragraph isn't long
    > enough.
    >
    I don't see what's missing from the iPhone discussion.  You're told
    that although the nib-loading mechanism doesn't retain the outlets
    directly, it uses KVC to set the outlets and what the ramifications
    are of that...

    >>> However, UIViewController has the ability to unload its view
    >>> outlet in response to a memory warning.  Any subclass should also
    >>> release its outlets in response to the memory warning, if the base
    >>> class releases its view, but not otherwise.  So now there are
    >>> three places to manage the memory of these outlets. The problem is
    >>> that the base class doesn't always release its view in response to
    >>> a memory warning and as far as I can tell the subclass has no
    >>> clean way of telling if the view will be released or has been
    >>> released.  That's the problem.
    >>>
    >> You're shifting the goalposts; this is not the problem you
    >> originally described.
    >
    > It's not me who has shifted the goalposts.  The whole playing field
    > was moved out from under me when it was decided to retain outlets by
    > default, which is different from how this all works on Mac OS X.
    >
    No, it's not.  Again as the documentation points out, exactly what
    happens on Mac OS X depends on the context.  Most outlets are indeed
    not retained, but top-level objects *are* retained (so you do need to
    release them).  What "The Nib Object Life Cycle" doesn't mention --
    but probably should -- is that this is further complicated by what is
    the superclass of File's Owner -- if it's NSDocument,
    NSWindowController, or NSViewController, then you don't have to
    release top-level objects.

    As I wrote originally, this is a mess, and requiring developers to
    think about this every time they make connections is unproductive.
    Which is why having a simple, consistent, approach that can be applied
    pervasively is beneficial.  As ever, you are free to deviate from that
    if you want to expend additional mental effort...

    >> You can add to your list of problems 'insufficient documentation
    >> and education of memory management of outlets.'
    >
    It remains unclear in what respect the document is insufficient.

    >> There are several issues:
    >> The template clearly indicates what should be done:
    > Obviously the client code can't touch _view or self.view in
    > didReceiveMemoryWarning.  That's why I said upthread that there's no
    > clean way for the client code to tell if the view will be unloaded
    > or has been unloaded.
    >
    Yes, there is...

    >> In theory, you should simply check whether the view has a superview:
    >> but since _view is declared as @package, you can't do that.
    >> You could invoke 'view':
    >> but this has the disadvantages that (a) in some situations it will
    >> cause the view to be loaded before it is subsequently unloaded, and
    >> (b) it isn't future proof.
    > self.view will always be non-nil.
    >
    [self.view superview] may, however, be nil, and this is what's
    important.
    If you use this test, then you might temporarily load some unwanted
    objects which in the context of a memory warning is counter-productive
    but in most cases the overhead -- if any -- should be minor compared
    with the data you're expunging.  Nevertheless this is ultimately
    unsatisfactory.

    > This is why I 'shifted the goalposts.'  There are many issues
    > related to memory management of outlets on iPhone.  Suggesting that
    > everyone just follow your best practice described at the top
    > obviously isn't sufficient.
    >
    On the contrary, following the best practice is sufficient, as it
    pertains to managing references to outlets.
    There may be additional considerations on any platform, though.

    The worst that will happen if, on iPhone, you simply use:

    @property (nonatomic, retain) IBOutlet Class *outletName;
    and follow standard memory management rules by releasing in dealloc
    and do nothing else

    is that your didReceiveMemoryWarning method will be less efective than
    would otherwise be the case.  You'll be maintaining views that could
    have been disposed of.  Hence:

    >> This leaves us for now with two solutions:
    >> (a) Greg's (override setView:) which is more future-proof but is in
    >> many respects academically unsatisfying.
    >> (b) For non-top-level-object, specify an assign attribute for the
    >> property -- and risk dangling pointers.

    This, though, is again not wholly satisfactory, and there should be
    architectural support to avoid this...

    And just to be clear, to step back for a moment, best practice does
    not *introduce* a problem with view controllers in this situation.

    First, the situation is largely orthogonal to the use of properties/
    following best practice.  You have to ensure you dispose of resources
    when you receive a memory warning.  How you do that is up to you.  It
    was, though, in part by following best practice that this issue came
    to light...

    To elaborate:
    Because the nib-loading mechanism uses KVC --  in theoryyou have to:
    (a) Remember to release outlets in dealloc;
    (b) Remember to release outlets when you receive a memory warning.

    If you haven't specified properties, it may be less clear that (b) is
    something to consider.
    If you haven't specified properties/accessor methods and forget about
    (b), then you will actually leak objects if you get a memory warning
    and you subsequently re-load your nib.

    Following best practice means that:
    You don't have to think about (a) -- it's a natural consequence of
    retaining properties.
    If you forget about (b), you don't actually ever leak objects -- you
    just don't expunge as many as you might be able to.

    And one might hope that in the fullness of time there would be some
    other feature that would guide you properly in the direction of
    fulfilling (b) without having to think hard about it.

    mmalc
  • On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:

    > My understanding (and I'm a noob in this) is that "best practices"
    > recommend that you shouldn't start sending additional messages to an
    > object from inside its dealloc.

    That is indeed correct. The official guideline is, AFAIK, to not call
    through your properties in init / dealloc.

    > Anyone want to clarify if it is/isn't safe to do this?  Certainly most
    > of the samples I've seen go out of their way to release instance
    > variables rather than nil properties, in their dealloc's

    What makes it unsafe is that you might not control the implementation
    of the property setter method - A subclass could for example override
    it and make it not safe for use from init / dealloc.

    That said, this might be something that the LLVM static analyzer could
    validate for us to the point where it would be OK.

    j o a r
  • On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    > My understanding (and I'm a noob in this) is that "best practices"
    > recommend that you shouldn't start sending additional messages to an
    > object from inside its dealloc.
    >
    Correct.

    > (This is the one thing I hate the *most* about properties - they
    > really
    > feel glued on, at this point, rather than being a language feature
    > that
    > the "whole compiler" knows about)
    >
    It's not clear how this is relevant to the implementation of dealloc?

    mmalc
  • On Nov 19, 2008, at 5:11 PM, j o a r wrote:

    >
    > On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    >
    >> My understanding (and I'm a noob in this) is that "best practices"
    >> recommend that you shouldn't start sending additional messages to an
    >> object from inside its dealloc.
    >
    >
    > That is indeed correct. The official guideline is, AFAIK, to not
    > call through your properties in init / dealloc.
    >

    For whatever reason UIViewController sets its view property to nil
    from its dealloc method.  Greg's override of setView is based on this
    behavior.

    --
    Brian Stern
    <brians99...>
  • On Wed, Nov 19, 2008 at 5:18 PM, mmalcolm crawford <mmalc_lists...> wrote:
    >
    > On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    >>
    >> (This is the one thing I hate the *most* about properties - they really
    >> feel glued on, at this point, rather than being a language feature that
    >> the "whole compiler" knows about)
    >>
    > It's not clear how this is relevant to the implementation of dealloc?

    Because there's essentially no good way to dispose of properties given
    the way that they're implemented.

    If you set them to nil, then you fall afoul of overridden setters and the like.

    If you manually release them, then you explode if you later change the
    property from assign to retain or vice versa.

    There should be a way to take advantage of the built-in property
    mechanism to simply say "do the right thing for this property",
    ideally in a single call for an entire class, or even better yet
    wholly automatically as part of NSObject's -dealloc implementation or
    something. But instead, you get good support for synthesized setters
    and getters but once you step into -dealloc you're back on your own,
    losing a big part of the advantage, at least if you aren't willing to
    hold your nose a bit and simply set them to nil despite the warnings
    against it.

    Mike
  • On Nov 19, 2008, at 2:22 PM, Brian Stern wrote:

    >> That is indeed correct. The official guideline is, AFAIK, to not
    >> call through your properties in init / dealloc.
    >
    > For whatever reason UIViewController sets its view property to nil
    > from its dealloc method.  Greg's override of setView is based on
    > this behavior.

    Please file bug reports whenever you find that our sample code and
    documentation are in conflict!

    <http://developer.apple.com/bugreporter/>

    Thanks,

    j o a r
  • On Nov 19, 2008, at 2:29 PM, Michael Ash wrote:

    > On Wed, Nov 19, 2008 at 5:18 PM, mmalcolm crawford <mmalc_lists...>
    >> wrote:
    >>
    >> On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    >>>
    >>> (This is the one thing I hate the *most* about properties - they
    >>> really
    >>> feel glued on, at this point, rather than being a language feature
    >>> that
    >>> the "whole compiler" knows about)
    >> It's not clear how this is relevant to the implementation of dealloc?
    > Because there's essentially no good way to dispose of properties given
    > the way that they're implemented.
    >
    I'm not sure why this is the case?
    You send a release message to instance variables for which there is a
    corresponding retain or copy property.

    > If you set them to nil, then you fall afoul of overridden setters
    > and the like.
    > If you manually release them, then you explode if you later change the
    > property from assign to retain or vice versa.
    >
    But you have the same problem without properties.
    Except that it's worse; you could change the implementation of an
    accessor method (to assign rather than retain, for example), and you'd
    have no cross-check to make sure you then did the right thing in
    dealloc.

    With properties, you have a clear set of statements that publicly
    declare what are the memory management semantics, and you can cross-
    check them with your dealloc method.

    > There should be a way to take advantage of the built-in property
    > mechanism to simply say "do the right thing for this property",
    > ideally in a single call for an entire class, or even better yet
    > wholly automatically as part of NSObject's -dealloc implementation or
    > something.
    >
    That certainly might be a welcome feature -- I'd encourage you to file
    an enhancement request.

    > But instead, you get good support for synthesized setters
    > and getters but once you step into -dealloc you're back on your own,
    > losing a big part of the advantage, at least if you aren't willing to
    > hold your nose a bit and simply set them to nil despite the warnings
    > against it.
    >
    Again, this doesn't seem like a regression, it's just something that
    could perhaps be taken advantage of.  You've had to put up with worse
    up until now.

    mmalc
  • On Nov 19, 2008, at 1:57 PM, Brian Stern wrote:

    > I'm starting to think that maybe using the assign properties is the
    > better way to handle this.
    >
    That's certainly one approach, and one that was considered.

    The problem is that you then have to think through every outlet
    declaration to make sure that you use the appropriate decorator, and
    you potentially run the risk of accessing dangling pointers --
    something that's typically going be a more significant issue than not
    disposing of an object at an opportune moment...

    As noted previously, the goal with best practice here is precisely to
    relieve you of having to think about such banalities(*).
    If you simply follow best practices, you get a good pattern that won't
    cause a crash and that allows you to concentrate on more important
    aspects of your application.

    Dealing with memory warnings is a slightly special case that's arisen
    fairly early on in the lifetime of a new platform.
    As an issue, it doesn't have any direct bearing on what is in general
    the best way to deal with outlets -- it's largely orthogonal.
    It should be managed better than it is now, and hopefully it will be
    in the future.  In a way that doesn't require great mental effort to
    get right.

    mmalc

    (*) I'm grateful to Henry for having pointed me to an apposite
    quotation from Alfred North Whitehead:

    "It is a profoundly erroneous truism, repeated by all copy-books and
    by eminent people when they are making speeches, that we should
    cultivate the habit of thinking of what we are doing. The precise
    opposite is the case. Civilization advances by extending the number of
    important operations which we can perform without thinking about them.
    Operations of thought are like cavalry charges in a battle--they are
    strictly limited in number, they require fresh horses, and must only
    be made at decisive moments."
  • On Nov 19, 2008, at 4:11 PM, j o a r wrote:

    >
    > On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    >
    >> My understanding (and I'm a noob in this) is that "best practices"
    >> recommend that you shouldn't start sending additional messages to an
    >> object from inside its dealloc.
    >
    >
    > That is indeed correct. The official guideline is, AFAIK, to not
    > call through your properties in init / dealloc.

    I guess I really missed that part of the docs.

    In all my desktop code, I always call my setters (non prop based) in
    dealloc:

    [self setTitle:nil];
    [self setColor:nil];
    etc.

    Just seemed natural to do self.title = nil, etc as I viewed properties
    as just a 1 to 1 replacement of manually setting stuff up.

    Now, when you say "call through your properties in init/dealloc", is
    that explicitly for things set up with @property?  Or, has what I've
    been doing all these years with calls to accessors in init/dealloc
    really "bad"?

    In my case, I do have a decent amount of code being shared between
    platforms.  And, such code doesn't yet use properties.  After reading
    this thread, it's very temping to just rid myself of the properties
    and go back to rolling my own accessors as needed (I do have
    Accessorizer, so that task is not hard at all).

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Nov 19, 2008, at 3:05 PM, Ricky Sharp wrote:

    > Now, when you say "call through your properties in init/dealloc", is
    > that explicitly for things set up with @property?  Or, has what I've
    > been doing all these years with calls to accessors in init/dealloc
    > really "bad"?

    There's no difference between setting through a property, and setting
    through a plain old setter method. That said, it's up to you to decide
    if you want to go with the official recommendation or not. If you
    think that you have enough control over the implementation of your
    accessor methods, you might choose to still use them.

    j o a r
  • On Nov 19, 2008, at 2:29 PM, mmalcolm crawford wrote:
    > You send a release message to instance variables for which there is a
    > corresponding retain or copy property.

    which is why I originally said:
    >>> (This is the one thing I hate the *most* about properties - they
    >>> really feel glued on, at this point, rather than being a language
    feature
    >>> that the "whole compiler" knows about)

    Most of the time you are supposed to think in terms of abstract
    properties (so that KVO will work) but in your dealloc, you need to fall
    back to worrying about the instance variables used to *implement* those
    properties.

    If you accept the default behaviour and name your properties the same as
    your instance variables, then you don't necessarily notice that it's the
    instance variables you clean up.  Its really easy to think that

    - (void) frammitz { [something foobar]; }

    and

    - (void) frammitz { [self.something foobar]; }

    are *identical* and they are not.  The Objective-C designers choice of
    '.' syntax, which just *screams* structure-member-access to anyone with
    C++ experience makes it even more confusing for those of us who have a
    multi-language background.

    The phrase "for which there is a *corresponding* ... property" is the
    one I still need to repeat to myself over and over.  To a certain
    extent, properties are orthogonal to instance variables; you can choose
    to implement properties via equivalently named instance variables but
    its not a given that you have to.  And you can have instance variables
    without property definitions.

    The @property declaration, where you use an alternate name, seems like
    an area for confusion.

    @property (nonatomic,retain) NSObject *xxx;
    ...
    @synthesize xxx=_yyy;
    ...

    not only defines accessors for a property called 'xxx', but also defines
    the memory retention contract for the instance variable '_yyy'.  In the
    past, you need only look in your .m file to determine how you needed to
    clean up your instance variables irrespective of the public interface
    they define in the .h file.  Now, you need to look in both the .h and
    the .m

    As someone else remarked, @synthesize does *most* but not all of the
    job, if its job is to 'provide the implementation of the property'.  The
    reality is that its job is only to 'provide the *accessors* for the
    property' which it does do completely.  And it does seem like having one
    more hack, perhaps

    - (void)dealloc
    {
    @release xxx;
    [super dealloc];
    }

    would make me feel like the feature (properties) was actually complete.

    Jeff Laing <jeffl...>
    ------------------------------------------------------------------------
    ------
    The lessons of history teach us - if they teach us anything - that
    nobody learns the lessons that history teaches us.
  • > There's no difference between setting through a property, and setting
    > through a plain old setter method. That said, it's up to you to decide
    > if you want to go with the official recommendation or not. If you
    > think that you have enough control over the implementation of your
    > accessor methods, you might choose to still use them.

    The question has been related to "best practices", which is why I'm
    pursuing it.

    As to 'control over the implementation of your accessors', if people use
    @synthesize (thus taking whatever the compiler gives them) and/or have
    bindings (or whatever it is) sneaking additional KVO/C stuff in over the
    top of their accessors, how much information do they actually *have*
    about their accessors to make an informed choice?
  • Jeff Laing wrote:

    >> There's no difference between setting through a property, and setting
    >> through a plain old setter method. That said, it's up to you to decide
    >> if you want to go with the official recommendation or not. If you
    >> think that you have enough control over the implementation of your
    >> accessor methods, you might choose to still use them.
    >>
    >>
    >
    > The question has been related to "best practices", which is why I'm
    > pursuing it.
    >
    > As to 'control over the implementation of your accessors', if people use
    > @synthesize (thus taking whatever the compiler gives them) and/or have
    > bindings (or whatever it is) sneaking additional KVO/C stuff in over the
    > top of their accessors, how much information do they actually *have*
    > about their accessors to make an informed choice?
    >
    >
    >
    I think if you're talking about your own instance variables which you
    are declaring and synthesizing, you have enough information to decide if
    it's safe to call your property accessors in dealloc or not. If you're
    inheriting a property from above .. well you shouldn't have to care
    about it as someone else is worrying about it in their dealloc.

    One case in which I use the setter in my dealloc code is is when I have
    a custom setter which does something, like it tears down KVO or turns
    off a timer or otherwise cleans up, I fix the setFoo: method to be the
    one place I do that work, and then I use [ bar setFoo:nil ] in the
    dealloc(). Given I do that, I think I should probably just use
    setBlah:nill for all my dealloc() needs for consistency as I like to
    have one way of doing things if possible.

    The only 'problem' with this I can see is that it triggers KVO if
    someone's observing me, but they shouldn't be, I'm being dealloc'ed and
    if someone's observing me and they've allowed me to be fully released,
    that's their bug.
  • > From: Roland King [mailto:<rols...>]
    > I think if you're talking about your own instance variables which you
    > are declaring and synthesizing, you have enough information to decide
    > if it's safe to call your property accessors in dealloc or not.

    Well, that's sort of my point, really. I don't *know* what happens
    inside the *synthesized* code, the only thing I can do is look to Apple
    who do and what they've said in the past is "don't do it".  Not "its not
    safe". So I don't think I can make my own mind up - Apple has made it up
    for me, without explicitly telling me what the decision was, only
    hinting at it.
  • On Nov 19, 2008, at 4:55 PM, Jeff Laing wrote:

    > Well, that's sort of my point, really. I don't *know* what happens
    > inside the *synthesized* code, the only thing I can do is look to
    > Apple
    > who do and what they've said in the past is "don't do it".  Not "its
    > not
    > safe". So I don't think I can make my own mind up - Apple has made
    > it up
    > for me, without explicitly telling me what the decision was, only
    > hinting at it.

    I think that's being overly defensive. Synthesized accessors will be
    straight forward and minimal. They're not the problem. The problem is
    fancy extra special stuff that we often add to manual accessors.

    j o a r
  • On Wed, Nov 19, 2008 at 5:41 PM, mmalcolm crawford <mmalc_lists...> wrote:
    >
    > On Nov 19, 2008, at 2:29 PM, Michael Ash wrote:
    >
    >> On Wed, Nov 19, 2008 at 5:18 PM, mmalcolm crawford <mmalc_lists...>
    >> wrote:
    >>>
    >>> On Nov 19, 2008, at 1:57 PM, Jeff Laing wrote:
    >>>>
    >>>> (This is the one thing I hate the *most* about properties - they really
    >>>> feel glued on, at this point, rather than being a language feature that
    >>>> the "whole compiler" knows about)
    >>>
    >>> It's not clear how this is relevant to the implementation of dealloc?
    >>
    >> Because there's essentially no good way to dispose of properties given
    >> the way that they're implemented.
    >>
    > I'm not sure why this is the case?
    > You send a release message to instance variables for which there is a
    > corresponding retain or copy property.

    This does not qualify as "good" in my book. A fundamental of good
    programming is "once and only once". You've now described the
    semantics of these properties in two different places in your code
    and, worse, one of those places is implicit rather than explicit.

    >> If you set them to nil, then you fall afoul of overridden setters and the
    >> like.
    >> If you manually release them, then you explode if you later change the
    >> property from assign to retain or vice versa.
    >>
    > But you have the same problem without properties.
    > Except that it's worse; you could change the implementation of an accessor
    > method (to assign rather than retain, for example), and you'd have no
    > cross-check to make sure you then did the right thing in dealloc.
    >
    > With properties, you have a clear set of statements that publicly declare
    > what are the memory management semantics, and you can cross-check them with
    > your dealloc method.

    This is true but I don't really see the point. I never said properties
    were worse than manual accessors, just that this omission really hurts
    their utility.

    >> There should be a way to take advantage of the built-in property
    >> mechanism to simply say "do the right thing for this property",
    >> ideally in a single call for an entire class, or even better yet
    >> wholly automatically as part of NSObject's -dealloc implementation or
    >> something.
    >>
    > That certainly might be a welcome feature -- I'd encourage you to file an
    > enhancement request.

    Duly noted.

    >> But instead, you get good support for synthesized setters
    >> and getters but once you step into -dealloc you're back on your own,
    >> losing a big part of the advantage, at least if you aren't willing to
    >> hold your nose a bit and simply set them to nil despite the warnings
    >> against it.
    >>
    > Again, this doesn't seem like a regression, it's just something that could
    > perhaps be taken advantage of.  You've had to put up with worse up until
    > now.

    I'm honestly not sure why you're using the word "regression".
    Certainly I never said it was one, and I don't believe Mr. Laing meant
    to imply such either.

    Mike
  • On Nov 19, 2008, at 8:40 PM, Michael Ash wrote:

    > On Wed, Nov 19, 2008 at 5:41 PM, mmalcolm crawford <mmalc_lists...>
    >> wrote:
    >>
    >> On Nov 19, 2008, at 2:29 PM, Michael Ash wrote:
    >>> Because there's essentially no good way to dispose of properties
    >>> given
    >>> the way that they're implemented.
    >> I'm not sure why this is the case?
    >> You send a release message to instance variables for which there is a
    >> corresponding retain or copy property.
    > This does not qualify as "good" in my book. A fundamental of good
    > programming is "once and only once". You've now described the
    > semantics of these properties in two different places in your code
    > and, worse, one of those places is implicit rather than explicit.
    >
    I'm not sure what you mean here?
    There is only one place where the semantics are described: In the
    property declaration.
    Whether you release or not in dealloc is a corollary of that
    declaration.

    >> But you have the same problem without properties.
    >> Except that it's worse; you could change the implementation of an
    >> accessor
    >> method (to assign rather than retain, for example), and you'd have no
    >> cross-check to make sure you then did the right thing in dealloc.
    >> With properties, you have a clear set of statements that publicly
    >> declare
    >> what are the memory management semantics, and you can cross-check
    >> them with
    >> your dealloc method.
    > This is true but I don't really see the point. I never said properties
    > were worse than manual accessors, just that this omission really hurts
    > their utility.
    >
    In which case we may be mostly in violet (sic) agreement.
    I obviously see significant benefits to using properties, and agree
    that their utility *might* be enhanced if they could also handle
    dealloc.  I got the impression that others here were suggesting that
    they provide little or no benefit at all...

    mmalc
  • On Wed, Nov 19, 2008 at 11:53 PM, mmalcolm crawford <mmalc_lists...> wrote:
    >
    > On Nov 19, 2008, at 8:40 PM, Michael Ash wrote:
    >
    >> On Wed, Nov 19, 2008 at 5:41 PM, mmalcolm crawford <mmalc_lists...>
    >> wrote:
    >>>
    >>> On Nov 19, 2008, at 2:29 PM, Michael Ash wrote:
    >>>>
    >>>> Because there's essentially no good way to dispose of properties given
    >>>> the way that they're implemented.
    >>>
    >>> I'm not sure why this is the case?
    >>> You send a release message to instance variables for which there is a
    >>> corresponding retain or copy property.
    >>
    >> This does not qualify as "good" in my book. A fundamental of good
    >> programming is "once and only once". You've now described the
    >> semantics of these properties in two different places in your code
    >> and, worse, one of those places is implicit rather than explicit.
    >>
    > I'm not sure what you mean here?
    > There is only one place where the semantics are described: In the property
    > declaration.
    > Whether you release or not in dealloc is a corollary of that declaration.

    Well, I think this is just a question of wording. It's true that it's
    only described in one place, but you have to get it right in two
    places. Whenever you have a situation like that and the compiler can't
    check it (like in this case), then you have a potential to get mixed
    up, especially if you make changes later on. It would be fairly easy
    for the properties mechanism to provide a way to use the @property
    declaration to generate the correct -dealloc behavior. In fact, there
    is one, "self.whateverProperty = nil", it's just that this has other
    unfortunate side effects. An alternate version with no side effects
    would remove this deficiency.

    > In which case we may be mostly in violet (sic) agreement.
    > I obviously see significant benefits to using properties, and agree that
    > their utility *might* be enhanced if they could also handle dealloc.  I got
    > the impression that others here were suggesting that they provide little or
    > no benefit at all...

    Yeah, I didn't get that impression myself. It's just a bit painful to
    see such a construct when a small addition to it would have made a
    significant improvement.

    Mike
  • Context:

    UIViewController provides a method, didReceiveMemoryWarning, which is
    invoked on view controllers when the amount of memory available to the
    application is severely constrained.  The goal of the method is to
    allow view controllers to dispose of resources that are currently not
    needed and that can be recreated later if required.  One such resource
    is the view controller's view itself.  Assuming that it does not have
    a superview, the view is disposed of ([self setView:nil];).

    A "issue" arises in that outlets to elements within the nib file are
    typically declared as follows:

    @property (nonatomic, retain) IBOutlet ElementClass *element;

    Thus even though the main view is disposed of, absent any further
    action the outlets are still retained.  This is not in and of itself a
    problem -- if and when the main view is reloaded, they will simply be
    replaced -- but it does mean that the beneficial effect of the
    didReceiveMemoryWarning is reduced.

    There are, currently, a couple of possible remedies...

    On Nov 19, 2008, at 12:59 AM, mmalcolm crawford wrote:

    > This leaves us for now with two solutions:
    > (a) Greg's (override setView:) which is more future-proof but is in
    > many respects academically unsatisfying.
    >

    - (void)setView:(UIView *)aView;
    {
        if (!aView) {
            // set outlets to nil, e.g.
            self.anOutlet = nil;
        }
        // Invoke super's implementation last
        [super setView:aView];
    }

    Unfortunately, although in principle this is correct, in practice it
    may fall foul of another issue.

    Because UIViewController currently implements its dealloc method using
    the setView: accessor method (rather than simply releasing the
    variable directly...), self.anOutlet = nil will be called in dealloc
    as well as in response to a memory warning... This will lead to a
    crash in dealloc.

    The remedy is to ensure that outlet variables are also set to nil in
    dealloc:

    - (void)dealloc {
        // release outlets and set variables to nil
        [anOutlet release], anOutlet = nil;
        [super release];
    }

    The iPhone engineering team is aware of this issue (read: There is no
    need to file bugs on this).

    mmalc
  • On Nov 21, 2008, at 6:53 PM, mmalcolm crawford wrote:

    >> This leaves us for now with two solutions:
    >> (a) Greg's (override setView:) which is more future-proof but is in
    >> many respects academically unsatisfying.
    >>
    >
    > - (void)setView:(UIView *)aView;
    > {
    > if (!aView) {
    > // set outlets to nil, e.g.
    > self.anOutlet = nil;
    > }
    > // Invoke super's implementation last
    > [super setView:aView];
    > }
    >
    >
    > Unfortunately, although in principle this is correct, in practice it
    > may fall foul of another issue.
    >
    > Because UIViewController currently implements its dealloc method
    > using the setView: accessor method (rather than simply releasing the
    > variable directly...), self.anOutlet = nil will be called in dealloc
    > as well as in response to a memory warning... This will lead to a
    > crash in dealloc.

    But, that's only if dealloc releases objects directly and doesn't use
    accessors or use the workaround shown below.

    I'm now really curious as to why UIViewController uses an accessor in
    dealloc since that's supposed to be bad practice.  Has a bug been
    filed against that too?

    > The remedy is to ensure that outlet variables are also set to nil in
    > dealloc:
    >
    > - (void)dealloc {
    > // release outlets and set variables to nil
    > [anOutlet release], anOutlet = nil;
    > [super release];
    > }

    That is indeed a workaround, but one of the reasons I moved towards
    using accessors instead (i.e. I didn't have to think about all the
    edge cases where clearing out the ivar was absolutely necessary; the
    accessor always does the right thing).

    Now then, two things...

    (1) In my personal code, I'm the only developer and do not integrate
    with any third party code (I only use Apple's SDKs directly).  Having
    said that, I do have full control over my own objects and thus don't
    have any pitfalls in using accessors in inits and/or deallocs.

    (2) But, I can see where in the general case, this is becoming quite
    problematic for folks.  After all, it's always dangerous to base your
    code on implementation details such as these.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Nov 21, 2008, at 5:21 PM, Ricky Sharp wrote:
    > But, that's only if dealloc releases objects directly and doesn't
    > use accessors or use the workaround shown below.
    >
    Yes, although following "best practice" is assumed...  :-)

    > I'm now really curious as to why UIViewController uses an accessor
    > in dealloc since that's supposed to be bad practice.  Has a bug been
    > filed against that too?
    >
    Yes.

    >> The remedy is to ensure that outlet variables are also set to nil
    >> in dealloc:
    >>
    >> - (void)dealloc {
    >> // release outlets and set variables to nil
    >> [anOutlet release], anOutlet = nil;
    >> [super release];
    >> }
    > That is indeed a workaround, but one of the reasons I moved towards
    > using accessors instead (i.e. I didn't have to think about all the
    > edge cases where clearing out the ivar was absolutely necessary; the
    > accessor always does the right thing).
    >
    I would suggest that releasing then setting the variable to nil is a
    better strategy than using the accessor.

    > Now then, two things...
    > (1) In my personal code, I'm the only developer and do not integrate
    > with any third party code (I only use Apple's SDKs directly).
    > Having said that, I do have full control over my own objects and
    > thus don't have any pitfalls in using accessors in inits and/or
    > deallocs.
    >

    That's fine; if you're *sure* you'll never run into a problem such as
    this...

    > (2) But, I can see where in the general case, this is becoming quite
    > problematic for folks.  After all, it's always dangerous to base
    > your code on implementation details such as these.
    >
    ... indeed, this is why best practice is given as such.

    mmalc
  • On 22 Nov 2008, at 00:53, mmalcolm crawford wrote:

    > Context:
    >
    > UIViewController provides a method, didReceiveMemoryWarning, which
    > is invoked on view controllers when the amount of memory available
    > to the application is severely constrained.  The goal of the method
    > is to allow view controllers to dispose of resources that are
    > currently not needed and that can be recreated later if required.
    > One such resource is the view controller's view itself.  Assuming
    > that it does not have a superview, the view is disposed of ([self
    > setView:nil];).
    >
    > A "issue" arises in that outlets to elements within the nib file are
    > typically declared as follows:
    >
    > @property (nonatomic, retain) IBOutlet ElementClass *element;
    >
    > Thus even though the main view is disposed of, absent any further
    > action the outlets are still retained.  This is not in and of itself
    > a problem -- if and when the main view is reloaded, they will simply
    > be replaced -- but it does mean that the beneficial effect of the
    > didReceiveMemoryWarning is reduced.
    >
    > There are, currently, a couple of possible remedies...

    Maybe I'm missing something (I stopped following the previous thread),
    but, presuming that super's -didreceiveMemoryWarning does indeed
    "Release[] the view if it doesn't have a superview", as documented,
    couldn't these contortions be avoided (in a future-proof way) by doing
    something like this in -didReceiveMemoryWarning, and not touching -
    setView:?

    - (void)didReceiveMemoryWarning
    {
    if(self.anOutlet && !self.view.superview) {
      // Will not call loadView if the view is not loaded, because
      // we can assume that if we get past the check for anOutlet
      // the view must already be loaded.

      self.anOutlet = nil;
    }
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't
    have a superview
    }

    Jamie.
  • On Nov 22, 2008, at 7:58 AM, James Montgomerie wrote:

    > if(self.anOutlet && !self.view.superview) {

    On Nov 19, 2008, at 12:59 AM, mmalcolm crawford wrote:

    > You could invoke 'view':
    >
    > - (void)didReceiveMemoryWarning {
    > if ([self.view superview] == nil) {
    > // set outlets to nil
    > }
    > [super didReceiveMemoryWarning];
    > }
    >
    > but this has the disadvantages that (a) in some situations it will
    > cause the view to be loaded before it is subsequently unloaded, and
    > (b) it isn't future proof.
    >

    mmalc
  • On Nov 22, 2008, at 8:16 AM, mmalcolm crawford wrote:

    > [...]
    >
    Sorry, pressed Deliver on the wrong message by mistake.
    Let me check on this one.

    mmalc
  • But I'm assuming that self.anOutlet will be valid if and only if
    self.view is already valid (in which case the call to
    self.view.superview should be safe, no?).

    Under this assumption, if self.anOutlet is nil, the && be short-
    circuited and self.view never called; if it is valid, it's safe to
    call self.view.

    It seems like, for this assumption not to hold, the outlets must not
    be being set up at the same time as the view (which I guess could be
    true if the programmer of the subclass we're talking about is doing
    something strange, but is not what I would expect).

    Jamie.

    On 22 Nov 2008, at 16:16, mmalcolm crawford wrote:

    >
    > On Nov 22, 2008, at 7:58 AM, James Montgomerie wrote:
    >
    >> if(self.anOutlet && !self.view.superview) {
    >
    >
    > On Nov 19, 2008, at 12:59 AM, mmalcolm crawford wrote:
    >
    >> You could invoke 'view':
    >>
    >> - (void)didReceiveMemoryWarning {
    >> if ([self.view superview] == nil) {
    >> // set outlets to nil
    >> }
    >> [super didReceiveMemoryWarning];
    >> }
    >>
    >> but this has the disadvantages that (a) in some situations it will
    >> cause the view to be loaded before it is subsequently unloaded, and
    >> (b) it isn't future proof.
    >>
    >
    > mmalc
    >
  • On 22 Nov 2008, at 16:29, mmalcolm crawford wrote:
    > On Nov 22, 2008, at 8:16 AM, mmalcolm crawford wrote:
    >
    >> [...]
    >>
    > Sorry, pressed Deliver on the wrong message by mistake.
    > Let me check on this one.

    Oops, sorry, looks like we're mailing around each other now...

    Jamie.
  • Am 22.11.2008 um 01:53 schrieb mmalcolm crawford:

    > - (void)dealloc {
    > // release outlets and set variables to nil
    > [anOutlet release], anOutlet = nil;
    > [super release];
    > }

    [super dealloc] ?

    ;)
    atze
  • On Nov 22, 2008, at 8:29 AM, mmalcolm crawford wrote:

    > Let me check on this one.
    >
    It seems that, for various reasons, the setView: approach is still
    preferred.

    mmalc
  • Returning to this months-old thread...  Apologies for the length of
    the mail, I thought it best to summarise what went before.

    I suggested that an appropriate implementation for
    didReceiveMemoryWarning in a UIViewController subclass would be

    On 22 Nov 2008, at 15:58, James Montgomerie wrote:
    > - (void)didReceiveMemoryWarning
    > {
    > if(self.anOutlet && !self.view.superview) {
    > // Will not call loadView if the view is not loaded, because
    > // we can assume that if we get past the check for anOutlet
    > // the view must already be loaded.
    >
    > self.anOutlet = nil;
    > }
    > [super didReceiveMemoryWarning]; // Releases the view if it doesn't
    > have a superview
    > }

    mmalc looked into it, and said:

    On 23 Nov 2008, at 20:10, mmalcolm crawford wrote:
    > It seems that, for various reasons, the setView: approach is still
    > preferred.

    I left the code in my app as-is, because I didn't fancy changing it
    all, and the response didn't indicate (to me, at least) that I was
    doing anything unsafe, but after some testing I'm rethinking that.

    Is the reason that the setView: approach is preferred that, despite
    what the docs and comments in the files say, the superclass'
    didReceiveMemoryWarning might /not/ actually release the view, even if
    it doesn't have a superview?  This would lead to the situation in
    which, when the UIViewController is next used, loadView is not called
    (because the view is not nil), but my resources /are/ nil, because I
    released them in didReceiveMemoryWarning when I saw that the view had
    no superview.

    That's what my playing around seems to suggest, in the simulator,
    calling didReceiveMemoryWarning directly, at least:

    - (void)didReceiveMemoryWarning
    {
        if(self.anOutlet && !self.view.superview) {
            NSLog(@"Released");
        }
        [super didReceiveMemoryWarning];

        // accessing the ivar directly to avoid loadView - very much just
    for testing purposes!
        NSLog(@"View: %p, %@", self->_view, self->_view);
    }

    produces, when I call didReceiveMemoryWarning directly, with the view
    not visible:

    2009-02-19 19:56:00.625 MyApp[76455:20b] Released
    2009-02-19 19:56:00.626 MyApp[76455:20b] View: 0x43b24c0, <UIView:
    0x43b24c0>

    Jamie.

    For those who didn't follow the original thread, the "setView:
    approach" is, by the way:

    On Nov 18, 2008, at 1:19 PM, Greg Titus wrote:
    > The way to handle this is to _not_ respond to memory warnings in
    > subclasses (at least not for the purposes of view outlet handling -
    > there may be other non-view memory you want to free up in response
    > to a memory warning). Instead, implement -setView: in your
    > UIViewController subclass, and release outlets when the argument is
    > nil. For example:
    >
    > - (void)setView:(UIView *)aView;
    > {
    > if (!aView) {
    > self.anOutlet = nil;
    > self.anotherOutlet = nil;
    > self.thirdOutlet = nil;
    > }
    > [super setView:aView];
    > }
    >
    > This will correctly clean up all of your outlets whenever the
    > UIViewController unloads its view, and not otherwise.
  • On Feb 19, 2009, at 12:18 PM, James Montgomerie wrote:

    > Is the reason that the setView: approach is preferred that, despite
    > what the docs and comments in the files say, the superclass'
    > didReceiveMemoryWarning might /not/ actually release the view, even
    > if it doesn't have a superview
    >
    It's what, for various reasons, engineering said is the best solution...

    See also updated: <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmNibObjects.html
    >

    mmalc
  • On Feb 19, 2009, at 2:18 PM, James Montgomerie wrote:

    > Returning to this months-old thread...  Apologies for the length of
    > the mail, I thought it best to summarise what went before.

    I now have shipping iPhone OS apps and during all my tests and
    analyzing things in Instruments (connected to real hardware), I didn't
    see a reason to put the proposed code in.

    Perhaps this is just how I've coded things, but I've seen views get
    unloaded and reloaded perfectly without any leaks.  This included my
    "main" screen view that was currently on the bottom of a nav
    controller stack (not the current view).  When I popped the current
    screen, it was appropriately re-loaded.  All IBOutlets re-wired, etc.

    My dealloc code is doing stuff like:

    self.someIBOutlet = nil;
    self.someOtherIvar = nil;
    ...
    [super dealloc]

    I'm _definitely_ not trying to be argumentative or propose that others
    avoid the "proper" solution.  Just wanted to point out that in my real-
    world situation, I just haven't had the need for it.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Feb 19, 2009, at 1:41 PM, Ricky Sharp wrote:

    > Perhaps this is just how I've coded things, but I've seen views get
    > unloaded and reloaded perfectly without any leaks.
    >
    This isn't the issue.

    The issue is how much memory can you free up in response to a memory
    warning.
    If you don't follow this pattern, then you won't be freeing up as much
    as you could to.

    If your app isn't getting memory warnings, you won't encounter this at
    all.

    mmalc
  • On 19 Feb 2009, at 21:41, Ricky Sharp wrote:
    >
    > On Feb 19, 2009, at 2:18 PM, James Montgomerie wrote:
    >
    >> Returning to this months-old thread...  Apologies for the length of
    >> the mail, I thought it best to summarise what went before.
    >
    >
    > I now have shipping iPhone OS apps and during all my tests and
    > analyzing things in Instruments (connected to real hardware), I
    > didn't see a reason to put the proposed code in.
    >
    > Perhaps this is just how I've coded things, but I've seen views get
    > unloaded and reloaded perfectly without any leaks.  This included my
    > "main" screen view that was currently on the bottom of a nav
    > controller stack (not the current view).  When I popped the current
    > screen, it was appropriately re-loaded.  All IBOutlets re-wired, etc.
    >
    > My dealloc code is doing stuff like:
    >
    > self.someIBOutlet = nil;
    > self.someOtherIvar = nil;
    > ...
    > [super dealloc]
    >
    >
    > I'm _definitely_ not trying to be argumentative or propose that
    > others avoid the "proper" solution.  Just wanted to point out that
    > in my real-world situation, I just haven't had the need for it.

    I feel like I may have opened a can of worms here.

    To be clear, I was not saying that there were any problems at-all with
    default implementations of these methods - the only potential problem
    is if you try to be 'smart' in the wrong way about what you do in
    didReceiveMemroyWarning with respect to outlets or other resources
    loaded manually in loadView.

    There's also no problem if you follow the now-recommended route of
    nilling out outlets in setView - I was just enquiring if my way of
    doing things was actively /wrong/ (which seems to be the case,
    unfortunately for me).

    Jamie.
  • On Feb 19, 2009, at 3:52 PM, mmalc Crawford wrote:

    >
    > On Feb 19, 2009, at 1:41 PM, Ricky Sharp wrote:
    >
    >> Perhaps this is just how I've coded things, but I've seen views get
    >> unloaded and reloaded perfectly without any leaks.
    >>
    > This isn't the issue.
    >
    > The issue is how much memory can you free up in response to a memory
    > warning.
    > If you don't follow this pattern, then you won't be freeing up as
    > much as you could to.
    >
    > If your app isn't getting memory warnings, you won't encounter this
    > at all.

    OK, things have finally clicked :)

    Yes, I have never had memory warnings.  Just "unused objects at this
    moment" unloaded to make room for more.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Feb 19, 2009, at 1:57 PM, James Montgomerie wrote:

    > I feel like I may have opened a can of worms here.
    >
    I think Ricky just closed it -- thanks.

    mmalc
  • On Feb 19, 2009, at 3:57 PM, James Montgomerie wrote:

    > I feel like I may have opened a can of worms here.

    Nope; my fault.  All this time I had misread the whole point of the
    threads.  My app has never received low-memory warnings.  So my
    observations in turn have nothing to do with it.

    I will re-add the proper refactor to my to-do list.  There's a chance
    my particular apps will continue to never receive the warnings.  But,
    I want them to be good citizens should they ever occur.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Feb 19, 2009, at 2:01 PM, mmalc Crawford wrote:

    > On Feb 19, 2009, at 1:57 PM, James Montgomerie wrote:
    >> I feel like I may have opened a can of worms here.
    > I think Ricky just closed it -- thanks.
    >
    And your post afforded a good opportunity to point to the updated
    documentation, so thanks for that too.

    mmalc
  • On Feb 19, 2009, at 12:34 PM, mmalc Crawford wrote:

    > >
    >
    Just to try to forestall what will probably otherwise be a flood of
    feedback; yes, there's a typo:

    [super release];
    should be
    [super dealloc];

    mmalc
  • Am 20.02.2009 um 04:18 schrieb mmalc Crawford:

    >
    > On Feb 19, 2009, at 12:34 PM, mmalc Crawford wrote:
    >
    >> See also updated: <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmNibObjects.html
    >> >
    >>

    The Doc states:
    You should therefore also set outlet variables to nil in dealloc:

    - (void)dealloc {
    // release outlets and set outlet variables to nil
    [anOutlet release], anOutlet = nil;
    [super dealloc];
    }

    why not just use

    [self setAnOutlet = nil];
    or
    self.anOutlet = nil;

    Any danger in this? (except a bad implementation of setAnOutlet:)

    atze
  • On Feb 20, 2009, at 1:05 AM, Alexander Spohr wrote:

    > You should therefore also set outlet variables to nil in dealloc:
    > - (void)dealloc {
    > // release outlets and set outlet variables to nil
    > [anOutlet release], anOutlet = nil;
    > [super dealloc];
    > }
    > why not just use
    > [self setAnOutlet = nil];
    > or
    > self.anOutlet = nil;
    >
    The paragraph above illustrates exactly why you shouldn't do this.

    See also, for example, <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articl
    es/chapter_4_section_4.html#//apple_ref/doc/uid/TP30001163-CH22-SW13
    >

    mmalc
  • On Feb 20, 2009, at 1:05 AM, Alexander Spohr wrote:

    >
    > Am 20.02.2009 um 04:18 schrieb mmalc Crawford:
    >
    >>
    >> On Feb 19, 2009, at 12:34 PM, mmalc Crawford wrote:
    >>
    >>> See also updated: <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmNibObjects.html
    >>> >
    >>>
    >
    > The Doc states:
    > You should therefore also set outlet variables to nil in dealloc:
    >
    > - (void)dealloc {
    > // release outlets and set outlet variables to nil
    > [anOutlet release], anOutlet = nil;
    > [super dealloc];
    > }
    >
    > why not just use
    >
    > [self setAnOutlet = nil];
    > or
    > self.anOutlet = nil;
    >
    > Any danger in this? (except a bad implementation of setAnOutlet:)
    >

    Any of the standard arguments about not calling setters in dealloc and
    init would apply. For example, if you were subclassed, and your
    subclass had an override of one of your setters, and you called it
    from dealloc you'd be calling one of their methods after they'd called
    [super dealloc]. Or, you might later implement side-effects for one of
    your setters not thinking about the called-from-dealloc-case and
    introduce a subtle bug into your own code.

    Jon Hess

    >
    > atze
  • Le 25 févr. 09 à 00:44, Jonathan Hess a écrit :

    >
    > On Feb 20, 2009, at 1:05 AM, Alexander Spohr wrote:
    >
    >>
    >> Am 20.02.2009 um 04:18 schrieb mmalc Crawford:
    >>
    >>>
    >>> On Feb 19, 2009, at 12:34 PM, mmalc Crawford wrote:
    >>>
    >>>> See also updated: <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmNibObjects.html
    >>>> >
    >>>>
    >>
    >> The Doc states:
    >> You should therefore also set outlet variables to nil in dealloc:
    >>
    >> - (void)dealloc {
    >> // release outlets and set outlet variables to nil
    >> [anOutlet release], anOutlet = nil;
    >> [super dealloc];
    >> }
    >>
    >> why not just use
    >>
    >> [self setAnOutlet = nil];
    >> or
    >> self.anOutlet = nil;
    >>
    >> Any danger in this? (except a bad implementation of setAnOutlet:)
    >>
    >
    > Any of the standard arguments about not calling setters in dealloc
    > and init would apply. For example, if you were subclassed, and your
    > subclass had an override of one of your setters, and you called it
    > from dealloc you'd be calling one of their methods after they'd
    > called [super dealloc]. Or, you might later implement side-effects
    > for one of your setters not thinking about the called-from-dealloc-
    > case and introduce a subtle bug into your own code.

    One question about this.

    If I understand correctly the 64bit runtime, for property we don't
    have to declare ivar.
    Then, if I want to init a property and clear it in dealloc I must do :

    self.myProperty = anObject;  // in init

    and

    self.myProperty = nil;  // in dealloc

    Does it mean I must always declare ivars even for 64bit only
    architecture ?

    Frédéric
previous month november 2008 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today
MindNode
MindNode offered a free license !