2

I have a subclass of UIViewController with an NSMutableArray as a property to use as the data source for a UITableView. I create an instance of the class in my storyboard.

I want to populate the array with the addObject: method but if I try, the array always returns (null).

I read that @synthesize doesn't init the array and I might need to override -init and init the NSMutableArray there but -init never gets called.

How is this supposed to work?

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
TijuanaKez
  • 1,472
  • 3
  • 20
  • 28
  • What is the class that has the `NSMutableArray` property? What is its superclass? Are you creating the class instance in code or in a nib or storyboard? – rob mayoff Feb 20 '12 at 06:57
  • It's a subclass of UIViewController linked to a storyboard view. – TijuanaKez Feb 20 '12 at 07:08

3 Answers3

5

You need to create an instance of NSMutableArray and assign it to the property.

Since the object with the property is a UIViewController created in a storyboard, you can do it in a few different places. You can override initWithCoder:, or awakeFromNib, or viewDidLoad.

If you override initWithCoder:, it is imperative that you call the super method.

If you do it in viewDidLoad, the array won't be created until the view is loaded, which doesn't have to happen right away.

I recommend doing it in awakeFromNib:

@synthesize myArray = _myArray;

- (void)awakeFromNib {
    _myArray = [[NSMutableArray alloc] init];
}

Another option is to just create the array lazily by overriding the getter method of the property:

@synthesize myArray = _myArray;

- (NSMutableArray *)myArray {
    if (!_myArray)
        _myArray = [[NSMutableArray alloc] init];
    return _myArray;
}

If you do this, it is very important that you always access the array using the getter method (self.myArray or [self myArray]) and never by accessing the instance variable (_myArray) directly.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks Rob, does look like -awakeFromNib is the best place as although -initWithCoder is called, if I implement it and do `if ([self = super] init])` none of my protocol methods get called for some reason. init in -awakeFromNib works great. – TijuanaKez Feb 20 '12 at 08:00
  • On a side note, xcode always adds the initWithNibName:bundle: method to all my uiviewcontroller subclasses but the method is never called in any of them. Why is it there and is it safe to remove it? – TijuanaKez Apr 02 '12 at 03:50
  • You can add initialization code to that method if you create your view controller programmatically. Since you're creating your view controller in your storyboard, you can delete the dummy method. It's just part of the standard template Xcode uses for new subclasses of UIViewController. – rob mayoff Apr 02 '12 at 04:37
2

Here's what your code will need:

@interface BlahBlah : UIViewController
@property (...) NSMutableArray *myArray;
@end

At first, *myArray is just a pointer that's equal to nil. You can't use this as an array yet. It needs to be set to a valid NSMutableArray instance. You need to do this in the designated initializer:

@implementation BlahBlah

@synthesize myArray;

// -initWithNibName:bundle: is the designated initializer for UIViewController
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
    self = [super initWithNibName:nibName bundle:nibBundle];
    if (self) {
        myArray = [[NSMutableArray alloc] init];
        // now you can add objects to myArray
    }
    return self;
}

- (void)dealloc
{
    [myArray release];
    [super dealloc];
}

Note that you can't just override -init; you must override the designated initializer for your class. Figure out what subclass you are implementing, find out what its designated initializer is, override that (but call the superclass implementation as is often necessary), then init your properties.

Here's Apple documentation regarding multiple/designated initializers.

bneely
  • 9,083
  • 4
  • 38
  • 46
  • 2
    This is very common bug. Remember to initialize your arrays! – Daniel Feb 20 '12 at 06:56
  • It is a subclass of UIViewController (adopting UITableViewDelegate and UITableViewDataSource protocols). Xcode didn't create any init method in my class implementation. I tried overriding -init manually but it never gets called. Looking at some other UITableView subclasses in my project, some have -initWithNibName:bundle: so I trying copying that method but it never get called either. Where am I supposed to do custom inits in a UIViewController subclass? – TijuanaKez Feb 20 '12 at 07:02
  • From the UIViewController documentation, the designated initializer is `- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle` . I updated this in my answer but I have not tested it. – bneely Feb 20 '12 at 07:06
  • I have put this in my implementation : `@implementation VideosTableViewController @synthesize myVideos; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { myVideos = [[NSMutableArray alloc] init]; } NSLog(@"init Called"); return self; }` But 'init called' never hits the log. – TijuanaKez Feb 20 '12 at 07:12
  • Unfortunately, designated or not, iOS does *not* call `initWithNibName:bundle:` when it is loading the object from a nib. It calls `initWithCoder:`. – rob mayoff Feb 20 '12 at 07:19
  • ...And tried replacing with your code (although the only difference seems to be the 'OrNil' on the var names) but the method is still never called. – TijuanaKez Feb 20 '12 at 07:20
0

@synthesis neither do alloc. So alloc and init your array. Then it should work.

Aaron
  • 1,342
  • 2
  • 16
  • 34