Reputation: 22252
(this question probably needs a more descriptive title, feel free to improve it)
I have a UIView
subclass with a property:
@property (weak, nonatomic) UILabel *label;
In an initialize
method I have:
[self addSubview: (self.label = [UILabel new])];
I like the terseness of this, but I have questions how it works.
For one, I get a warning:
Assigning retained object to weak property; object will be released after assignment
Warning aside, it actually seems to work. Is that because before the release machinery can run, the addSubview:
re-retains it?
If I understand correctly, the self.label = ...
code is just sugar for a [self setLabel: ...]
. But if I override the property access with my own setLabel:
implementation, its signature will be
- (void) setLabel: (UILabel*) label;
So the return value is void. But it's being fed into the the addSubview:
send and working? So how does that work?
One of the things that frustrates doing a one liner here, is that Objective-C
tend to eschew returning useful information from methods like addSubview:
which is divergent from the Smalltalk
influence which spawned Objective-C in the first place. In Smalltalk, it would be expected/common for the addSubview:
method to return the added object. If this was the case, one could write these expressions as:
self.label = [self addSubview: [UILabel new]];
This would allow the strong/weak semantics to be happy. The addSubview:
would do the strong retain before the it got to the weak label
setter.
I fight this problem with both subviews
and recognizers
, so I thought I'd be clever, and write a category
to do something like that (I inverted the receiver/argument so it would be easily distinguishable from an alternate addSubview: signature).
@interface UIView (UIView_Adding)
- (UIView*) addedToView: (UIView*) superview;
@end
@interface UIGestureRecognizer (UIView_Adding)
- (UIGestureRecognizer*) addedToView: (UIView*) view;
@end
@implementation UIView (UIView_Adding)
- (UIView*) addedToView: (UIView*) superview {
[superview addSubview: self];
return self;
}
@end
@implementation UIGestureRecognizer (UIView_Adding)
- (UIGestureRecognizer*) addedToView: (UIView*) view {
[view addGestureRecognizer: self];
return self;
}
@end
Unfortunately, this just invokes the Law of Conservation of Ugly. I get rid of one kind of warning and get another. With that category included, I can now write:
self.label = [[UILabel new] addedToView: self];
But this generates a warning that self.label
is meant to hold a specific subclass of UIView
, namely a UILabel
and the return type of addedToView:
is `UIView'. I'm not aware of an Objective-C pseudo type which means be the right darn subtype of this known super type to make property types happy. :(
I discovered the instancetype
type. By changing my category method signatures to return these types, everything just works. I'm new enough to instancetype
to not know if I'm abusing something here, but I'm thrilled it worked.
Upvotes: 2
Views: 128
Reputation: 318824
The warning comes from:
self.label = [UILabel new]
since your label
property is weak
.
You are correct that it ends up working because the label is retained by the call to addSubview:
.
But if the label is ever removed from its superview, the label
property will become nil
and the label will be lost. So if there is any chance that the label will be removed but you want to keep a reference to the label (maybe to add it back later), then change your property to be strong
.
You are also correct that self.label =
is really just [self setLabel:]
. The reason that this can be passed to addSubview:
is due to the fact that an expression such as x = y
has a value equal to the assignment.
So:
[self addSubview: (self.label = [UILabel new])];
if equivalent to:
UIView *view = (self.label = [UILabel new]);
[self addSubview:view];
Upvotes: 2