1

What is the correct way of programmatically adding subviews to a view,

A) using @property:

// SampleViewController.h
@interface SampleViewController : UIViewController

@property (nonatomic, weak) UITableView *subTableView;
@end

// SampleViewController.m
@synthesize subTableView = _subTableView;

or B) in loadView:

// SampleViewController.m 
- (void)loadView
{
    UITableView *subTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:subTableView];
}

Again, this is assuming I am not using IB at all. What is the difference between the two (because in practice both seems to work)?

Additional question, if I have both the A) and B) codes in SampleViewController.m, why does XCode allow me to use subTableView as variable name in B, even though I already used that name in the @synthesize part of the code?

Update: I did a bit of digging around and turns out the @synthesize keyword is no longer needed starting from XCode 4.4.

hfz
  • 317
  • 2
  • 15
  • 1
    **Note, however,** that a property alone is not enough. You will still need to add the subview in `loadView`. – Léo Natan Sep 06 '13 at 03:26
  • @LeoNatan by "add the subview" do you mean to `alloc` it, or `addSubview` it, or both? – hfz Sep 06 '13 at 03:44
  • 1
    Both. After initialization, the property will be `nil`. You need to allocate and initialize the view you want, and add it as subview. – Léo Natan Sep 06 '13 at 03:57

4 Answers4

2

Depends on your needs. If you wish to add it once and forget about it, a local variable is enough. It will be retained by the superview, and will not be released.

However, if you wish to access it later, you could use an instance variable or a property to keep a reference to your view.

Whether your property is weak or strong depends on your usage, again. If you add it to a superview and don't remove it again, weak is enough because it is retained by the superview. If, however, you dynamically add it and remove it, for example show it in landscape but remove it in portrait, then you would want the property to have a "strong" modifier, so that the object is retained even when not part of the view hierarchy.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • 2
    Note also that you do not need to use @synthesize and that your ivar is actually named _subTableView which is why it does not conflict with subTableView. Note that if you named your local variable _subTableView you'd just get a warning that it was hiding the ivar. – Ben Flynn Sep 06 '13 at 03:24
  • 1
    If there is a property, there is no need for an instance variable - it will be created automatically. – Léo Natan Sep 06 '13 at 03:25
  • @BenFlynn can you explain more about "you don't need to use @synthesize"? Is that for method A) or B)? – hfz Sep 06 '13 at 03:41
  • 1
    `@synthesize` is no longer required. You also don't need to explicitly declare an instance variable. When declaring a `@property propertyName`, the compiler will generate an instance variable named `_propertyName` for you automatically. – Léo Natan Sep 06 '13 at 03:59
  • @LeoNatan Looks like I got my concept wrong, then. If I don't need to use `@synthesize`, what is that keyword's purpose? – hfz Sep 06 '13 at 04:07
  • 2
    It is largely historic by now. It still has use if you want to name your instance variable differently than the default, but that is usually not recommended for consistency sake. When properties were introduced years ago, calling `@synthesize` was required, but even then, people wondered why. Now this is no longer the case. – Léo Natan Sep 06 '13 at 04:10
  • @LeoNatan Understood. Last question, how do I access the `@property` properly? a) with `self.propertyName` or b) `_propertyName` or are both interchangeable? – hfz Sep 06 '13 at 04:23
  • 1
    A property is just shorthand for a instance variable with a getter and a setter. The getter, is a method that, by default, just returns the ivar. By using self.propertyName or [self propertyName] you are calling a method. By using _propertyName you are directly accessing the ivar. This is critical if you are overriding the getter or setter, and I've seen recommendations for using ivars in init methods (don't really see the point myself). Generally I use the methods because they separate the interface from the implementation. – Ben Flynn Sep 06 '13 at 04:32
  • 1
    My comment is a bit of a simplification. I would give this a read for the full story: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html – Ben Flynn Sep 06 '13 at 04:33
  • 1
    @hfz Yes and no. Read access is interchangeable. Write access gets more tricky. At the outside, it seems like setting a property's instance variable is the same thing, however when using the setter method, several things happen, like atomicity, copy, etc which are property modifiers your have set, as well as KVO willChange/didChange notifications are sent for that property. – Léo Natan Sep 06 '13 at 04:35
2

If you want to access an object in two different classes the @property is used and it is better way because we can also release it, and otherwise a local variable is enough if you want to use it locally

1

First method is fine. But for second one, it's not correct. You need do like this

// SampleViewController.m 
- (void)loadView
{
  CGRect frame = ...
  UIView * view = [[UIView alloc] initWithFrame:frame]; 
  UITableView *subTableView = [[UITableView alloc] initWithFrame:frame];
  // Warn: it's |view|, not |self.view|,
  //   should not use |self.view| in this method except the retain process at bottom,
  //   i.e.: |self.view = view|.
  [view addSubview:subTableView];
  // If not use ARC, you need to release the view here: [subTableView release];

  // Note: |-loadView| is for |self.view| in case the view does not exist.
  //   Generally specking, it'll only be dispatched once.
  self.view = view;
  // If not use ARC, ...: [view release];
}

or you can add subviews in -viewDidLoad: method:

- (void)viewDidLoad
{
  [super viewDidLoad];

  UITableView *subTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
  [self.view addSubview:subTableView];
  // If not use ARC, ...: [subTableView release];
}

If you need to update view data frequently after the view did load, you'd better declare subviews like the first way you mentioned.

Kjuly
  • 34,476
  • 22
  • 104
  • 118
  • The reason I'm using -loadView instead of -viewDidLoad is because [I was told here](http://stackoverflow.com/a/18635083/884836) that way. `-loadView` is for non-XIB, `-viewDidLoad` for XIB-based controllers. Was that incorrect? – hfz Sep 06 '13 at 03:00
  • 1
    @hfz nope, if you don't use Xib, you need to implement `-loadView` by yourself. i.e. initialise the `self.view` (it is managed by xib if you use that one). No matter you use Xib or not, `-viewDidLoad` method will be dispatched, and same to `-viewWillAppear`. – Kjuly Sep 06 '13 at 03:04
1

As you state, either (A) or (B) works. If you need to reference subTableView in other methods besides loadView, then you should use (A).

As to the additional question, you can use subTableView in (B) because you're declaring a local variable with the same name as the synthesized variable.

mjswan
  • 91
  • 4