Sam
Sam

Reputation: 621

Why objects are not created using property directly?

I am working on the UIPopover and in one of the example I found that the Popover object is created but then the object is assigned to the property of the Viewcontroller.

UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:content];
self.popoverController = aPopover;

What is the merit in such assignment and any reason for not assigning object to the property directly?

Upvotes: 1

Views: 97

Answers (3)

Rob
Rob

Reputation: 437702

I agree with the others that, in this case, the assigning of the popover controller to a local variable before later assigning it to a class property is largely a stylistic matter. But this is only the case because you are keeping a strong reference to that popover controller. There are other situations where you have weak properties, in which this local variable pattern is critical.


For example, let's assume that we have a bunch of controls that we're going to add to our view controller's view programmatically:

@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, strong) UILabel *usernameLabel;
@property (nonatomic, strong) UILabel *emailLabel;
// and more labels

When you want to add these to your view controller's view, you could do something like:

- (void)addSubviewsAtPoint:(CGPoint)location
{
    self.containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
    [self.view addSubview:self.containerView];

    self.usernameLabel = [[UILabel alloc] initWithFrame:[self frameForUsernameLabel]];
    [self.containerView addSubview:self.usernameLabel];
    self.usernameLabel.text = self.username;

    self.emailLabel = [[UILabel alloc] initWithFrame:[self frameForEmailLabel]];
    [self.containerView addSubview:self.emailLabel];
    self.emailLabel.text = self.email;

    // etc.
}

But it also means that when you remove the subviews, you not only have to remove the container view from your view hierarchy, but you also have to remember to nil all of the properties for all of those subviews:

- (void)removeSubviews
{
    [self.containerView removeFromSuperview];
    self.containerView = nil;
    self.emailLabel = nil;
    self.usernameLabel = nil;
    // etc.
}

This introduces a maintenance issue, that every time you add a new control via addSubviewsAtPoint, that you also have to remember to add it to removeSubviews, too, or else you might be hanging on to the control well after you've removed it from the screen.

To simplify your life, you might make all of these properties weak (with the intuition being that it's the view that owns these subviews, not the view controller):

@property (nonatomic, weak) UIView *containerView;
@property (nonatomic, weak) UILabel *usernameLabel;
@property (nonatomic, weak) UILabel *emailLabel;
// etc.

But now, using ARC, your addSubviewsAtPoint no longer works, because when you assign an object to a weak property, if there are no other strong references, it will be immediately become nil:

self.containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
[self.view addSubview:self.containerView];    // FAIL!!! self.containerView will be nil!

So, instead, we employ that local variable pattern of your question to ensure that the controls are not prematurely deallocated while we're adding them to our view:

- (void)addSubviewsAtPoint:(CGPoint)location
{
    UIView *containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
    [self.view addSubview:containerView];
    self.containerView = containerView;

    UILabel *usernameLabel = [[UILabel alloc] initWithFrame:[self frameForUsernameLabel]];
    [containerView addSubview:usernameLabel];
    usernameLabel.text = self.username;
    self.usernameLabel = usernameLabel;

    UILabel *emailLabel = [[UILabel alloc] initWithFrame:[self frameForEmailLabel]];
    [containerView addSubview:emailLabel];
    emailLabel.text = self.email;
    self.emailLabel = emailLabel;

    // etc.
}

And, as a result, because we're using weak properties, our removal of those subviews is now much simpler, as we don't have to nil all of those properties when we remove the containerView from our view controller's view:

- (void)removeSubviews
{
    [self.containerView removeFromSuperview];

    // because all of those `containerView` subviews were `weak`,
    // we don't have to manually `nil` them
}

Upvotes: 1

matt
matt

Reputation: 535353

There is no "merit" in it. Saying

self.popoverController = 
    [[UIPopoverController alloc]  initWithContentViewController:content];

would be absolutely equivalent.

On the other hand there is nothing wrong with using a temporary variable (aPopover) as shown in your example. It's just a name (a pointer); there is no significant waste of space or time. Moreover, saying self.popoverController repeatedly (either to set or to get its value) is to be avoided, because this is a method call - you are passing through the setter method or getter method (which may be synthesized, may have side effects, and does in fact take some extra time). Thus, when there is much configuration to be done (for example), it is best to do it as shown in your example:

UIPopoverController* aPopover = 
    [[UIPopoverController alloc]  initWithContentViewController:content];
// lots of configuration here, using the local automatic variable aPopover...
// ...and then, only when it is all done, call the setter:
self.popoverController = aPopover;

Upvotes: 5

Leeloo Levin
Leeloo Levin

Reputation: 443

The only reason for that is that you probably read this in a tutorial somewhere. And the author did it for readability for beginners. You could absolutely use:

self.popoverController = [[UIPopoverController alloc] initWithContentViewController:content];

All depending on how familiar you are with programming in general and how readable you want your code to be.

Upvotes: 1

Related Questions