Travis Griggs
Travis Griggs

Reputation: 22252

Idiomatic method to initialize weak properties?

I have a UIView subclass that manipulates a number of layers. Because the view's layer's sublayers retains the layers strongly, my thought was to make my own references to them weak:

@interface AnchorCell : UICollectionReusableView
@property (weak, nonatomic) CAShapeLayer *link;
@property (weak, nonatomic) CATextLayer *offset;
@end

I can do a certain amount of setup at init time. But I end up writing code like this:

- (void) initLink {
    CAShapeLayer *shape = _link = [CAShapeLayer layer]; // have to put it in local variable to make it last long enough to get to the sublayer
    _link.strokeColor = [[UIColor orangeColor] colorWithAlphaComponent: 1].CGColor;
    _link.fillColor = nil;
    _link.lineWidth = 3;
    _link.masksToBounds = NO;
....
    [self.layer addSublayer: _link];
}

I'm curious if there's a better idiomatic way to do this. What I like about the above, is that it highlights as much as possible, that I'm setting up the link variable, instead of some local shape variable, which I then set to link at the end. What I don't like about it is that you have to add a local variable for no apparent reason that Xcode warns about.

I could move the addSublayer: to the top of the method:

- (void) initLink {
    CAShapeLayer *shape = [CAShapeLayer layer];
    [self.layer addSublayer: shape];
    _link = shape;
    _link.strokeColor = [[UIColor orangeColor] colorWithAlphaComponent: 1].CGColor;
    _link.fillColor = nil;
    _link.lineWidth = 3;
    _link.masksToBounds = NO;
}

But this hides things (to me) as well. It doesn't make it clear that link has been added as a sublayer. Also, sometimes, there are patterns where you have to do a certain amount of setup to the object before you register it elsewhere.

Is there a more elegant way of doing this? Or at least a more idiomatic way given the restrictions of ARC managed ObjectiveC?

Upvotes: 3

Views: 411

Answers (3)

Rob
Rob

Reputation: 437532

I don't think you should be making properties strong just to avoid the use of a local variable (and if the property is weak, you need the local variable).

I think the memory semantics (e.g. weak vs strong vs copy) should reflect the functional object ownership graph, not sleight of hand just to avoid the use of a local variable. I would leave the property as weak, and then do the obvious:

CAShapeLayer *shape = [CAShapeLayer layer]; // have to put it in local variable to make it last long enough to get to the sublayer
shape.strokeColor = [[UIColor orangeColor] colorWithAlphaComponent: 1].CGColor;
shape.fillColor = nil;
shape.lineWidth = 3;
shape.masksToBounds = NO;
....
[self.layer addSublayer: shape];

self.link = shape;

Personally, I don't I think the line that assigned both shape and _link in a single line of code improved its readability, so I prefer to separate those, but do whatever you want.

Furthermore, I generally advise the use of the setter, because I never know if I might have a custom setter or change the memory semantics at some future date (and the code here shouldn't be concerned about that). More than once I've had to refactor code that used the ivar directly because some implementation detail changed at a later date, but I have yet to regret using the accessor methods.

Upvotes: 4

Joshua Kaden
Joshua Kaden

Reputation: 1230

You might want to try code block evaluation, as described in this blog post.

Like:

    _link = ({
        CAShapeLayer *shape = [CAShapeLayer layer];
        shape.strokeColor = [[UIColor orangeColor] colorWithAlphaComponent: 1].CGColor;
        shape.fillColor = nil;
        shape.lineWidth = 3;
        shape.masksToBounds = NO;
        [self.layer addSublayer: shape];
        shape;
    )};

Upvotes: 1

nsdebug
nsdebug

Reputation: 364

I don't think you need the temporary variable shape at all - simply do:

_link = [CAShapeLayer layer];

or

self.link = [CAShapeLayer layer];

Mind you, if you never need _link again once it becomes a sublayer then you can do it all with the local variable and skip the @property.

Hope this helps.

Upvotes: 0

Related Questions