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 itThis is standard (best) practice for memory management. It's not
> 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.
>
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 outletYou're shifting the goalposts; this is not the problem you originally
> 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.
>
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 littleBecause it should be obvious from a memory management perspective why
> paragraph in the documentation that addresses this says I 'should'
> do it this way but doesn't explain 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 ofI don't see what's missing from the iPhone discussion. You're told
> 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.
>
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 viewNo, it's not. Again as the documentation points out, exactly what
>>> 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.
>
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 documentationIt remains unclear in what respect the document is insufficient.
>> and education of memory management of outlets.'
>>> There are several issues:Yes, there is...
>> 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:[self.view superview] may, however, be nil, and this is what's
>> 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.
>
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 issuesOn the contrary, following the best practice is sufficient, as it
> related to memory management of outlets on iPhone. Suggesting that
> everyone just follow your best practice described at the top
> obviously isn't sufficient.
>
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"Correct.
> recommend that you shouldn't start sending additional messages to an
> object from inside its dealloc.
>> (This is the one thing I hate the *most* about properties - theyIt's not clear how this is relevant to the implementation of dealloc?
> really
> feel glued on, at this point, rather than being a language feature
> that
> the "whole compiler" knows about)
>
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...>I'm not sure why this is the case?
>> 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.
>
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 settersBut you have the same problem without properties.
> and the like.
> If you manually release them, then you explode if you later change the
> property from assign to retain or vice versa.
>
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 propertyThat certainly might be a welcome feature -- I'd encourage you to file
> 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.
>
an enhancement request.> But instead, you get good support for synthesized settersAgain, this doesn't seem like a regression, it's just something that
> 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.
>
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 theThat's certainly one approach, and one that was considered.
> better way to handle this.
>
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 - theyfeature
>>> really feel glued on, at this point, rather than being a language>>> 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 settingI think if you're talking about your own instance variables which you
>> 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?
>
>
>
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...>I'm not sure what you mean here?
>> 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.
>
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.In which case we may be mostly in violet (sic) agreement.
>> 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.
>
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'tYes, although following "best practice" is assumed... :-)
> use accessors or use the workaround shown below.
>> I'm now really curious as to why UIViewController uses an accessorYes.
> 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 nilI would suggest that releasing then setting the variable to nil is a
>> 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).
>
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... indeed, this is why best practice is given as such.
> problematic for folks. After all, it's always dangerous to base
> your code on implementation details such as these.
>
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, despiteIt's what, for various reasons, engineering said is the best solution...
> 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
>
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 getThis isn't the issue.
> unloaded and reloaded perfectly without any leaks.
>
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:And your post afforded a good opportunity to point to the updated
>> I feel like I may have opened a can of worms here.
> I think Ricky just closed it -- thanks.
>
documentation, so thanks for that too.
mmalc -
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>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:The paragraph above illustrates exactly why you shouldn't do this.
> - (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;
>
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 -
Le 25 févr. 09 à 18:37, mmalc Crawford a écrit :
> >>
> mmalc
OK, thank you
Frédéric


