Re: Outlets / IBOutlet declarations (was Re: Interface Builder & Wiring Objects)
Re: Outlets / IBOutlet declarations (was Re: Interface Builder & Wiring Objects)
- Subject: Re: Outlets / IBOutlet declarations (was Re: Interface Builder & Wiring Objects)
- From: mmalcolm crawford <email@hidden>
- Date: Wed, 19 Nov 2008 14:04:04 -0800
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
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden