In code that I'm inheriting, I've seen the following:
@property (readonly) IBOutlet UIImageView * bgImage;
When I would expect a retain memory model like:
@property (readonly, retain) IBOutlet UIImageView * bgImage;
I'm confused why the first property definition works without causing problems.
Also, there is a release
in dealloc as you might expect like:
-(void)dealloc
{
[_bgImage release];
[super dealloc];
}
I'd appreciate if anyone can come up with an explanation for this. I've spoken with the original developer and he was trying to write more concise code which is why he left out the retain
in the memory model (seemed unnecessary).
I wonder if the IBOutlet is basically treated like an ivar IBOutlet statement because it is readonly (no setter to use, thus the default assign memory model makes no difference).
If the IBOutlet is never expected to change, would using a readonly property with no memory model actually be a preferable way to define properties?
The nib loader on iOS creates the objects in the nib and then autoreleases them. When it establishes connections to outlets, it uses setValue:forKey:
, which will call the setter method for that key. If no setter is defined, such as when the IBOutlet
is a readonly
property, the object is retained anyways before being assigned.
(This is a paraphrase of Managing Nib Objects in iOS in the Resource Programming Guide.)
So in actual fact, whether the outlet is declared as retain
or assign
, the object at the other end is owned by the object with the outlet. Either it is retained by the setter method, or it is retained by setValue:forKey:
when a setter is not found. Since there is no other possible owner in the second case, you may consider the object with the outlet to be the owner. Therefore the object in the nib should be released in dealloc
.
I agree with you that this memory condition should be made explicit by changing the property attributes to include retain
.* Whether or not it's readonly
doesn't seem to make a difference (however, see below). Conceptually, yes, the object is read-only, so whether to mark it explicitly as such depends on whether you consider that suitably documented by the fact that it's an IBOutlet
.
UPDATE:
Paul.s's comment below prompted me to do a quick test. I created a UIView
subclass that logs its alloc
, retain
, release
, and autorelease
calls, stuck an instance of it into a nib, and gave the app delegate a IBOutlet
via a property.
Tallying up the reference counting activity by hand, the instance came out with a net 0 count when the property was (readwrite, assign)
. It was net +1 when the property was declared the recommended way, (readwrite, retain)
, and also when it was (readonly, assign)
. All of this is pretty much as expected -- when it is (readwrite, assign)
, the assigning setter is used to make the connection, and no retain is made. When it is readonly
, the connection mechanism falls back on doing its own retain.
Most interestingly, when I tried to crash the app by changing the background color of this view with the property declared (readwrite, assign)
(i.e., when it had presumably been deallocated), I saw one last call to retain
pop up.
I think what this comes down to is: follow Apple's recommendation -- they know what's going on behind the scenes, and (barring bugs) aren't going to steer you wrong.
(Another thing to take away is that, as always, worrying about absolute reference counts is not going to be terribly useful -- the count went all the way up to 6 at one point, over the course of two dozen calls to retain
and release
-- you just need to worry about retains and releases that you cause directly.)
*Of course, this changes under ARC. The info I paraphrased is in the "Legacy Patterns" section of its chapter. Under ARC, the recommendation is for IBOutlets
to be weak
unless they are top-level, in which case they should be strong
. Doing it that way means you are relying on the view hierarchy (views retaining their subviews) to maintain itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With