Southland Sooner
Southland Sooner

Reputation: 13

NSMutableArrays are null

I have an NSViewController subclass with:

@property (retain) NSMutableArray* entities;
@property (retain) NSMutableArray* tiles;

In my -init method, both arrays are created with +new, and are given one object each. After that, I call NSLog(@"%@, %@", entities, tiles);, and it gives me just as expected:

2012-12-30 15:07:04.160 Project Land III[2177:303] (
"<RBEntity: 0x100508170>"
), (
"<RBTile: 0x100508470>"
)

I can click a button on the view, though, which calls the same log function, and it spit out this:

2012-12-30 15:07:06.071 Project Land III[2177:303] (null), (null)

I've been stuck on this problem in some form or another for days. Why in the world are the arrays null?

I'm more than happy to post more code, just let me know!

Interface:

#import <Cocoa/Cocoa.h>
#import "RBEntity.h"
#import "RBTile.h"

@interface RBMainViewController : NSViewController {
    NSMutableArray* _entities;
    NSMutableArray* _tiles;
}

@property (retain) NSMutableArray* entities;
@property (retain) NSMutableArray* tiles;

- (IBAction)log:(id)sender;

@end

My -init method:

- (id)init {
    self = [super init];
    self.entities = [NSMutableArray new];
    self.tiles = [NSMutableArray new];
    [self.entities addObject:[RBEntity entityWithLocation:NSMakePoint(4, 5) type:FACEEATER]];
    [self.tiles addObject:[RBTile tileWithLocation:NSMakePoint(10, 2) type:GRASS]];
    NSLog(@"%@, %@", self.entities, self.tiles);
    return self;
}

Upvotes: 1

Views: 152

Answers (3)

Peter Hosey
Peter Hosey

Reputation: 96373

In my -init method, both arrays are created with +new, and are given one object each. After that, I call NSLog(@"%@, %@", entities, tiles);, and it gives me just as expected:

2012-12-30 15:07:04.160 Project Land III[2177:303] (
    "<RBEntity: 0x100508170>"
), (
    "<RBTile: 0x100508470>"
)

I can click a button on the view, though, which calls the same log function, and it spit out this:

2012-12-30 15:07:06.071 Project Land III[2177:303] (null), (null)

This is a very common novice mistake.

You have two RBMainViewController objects. One of them, you presumably created in code in one of your other .m files, by saying something like [[RBMainViewController alloc] init]. The other, you created in a nib, probably by dragging it into the nib.

(Note: The nib that you created that VC in is not the VC's nib. That would be circular, to have the VC's nib containing the VC that is loading the nib. The VC that doesn't have its arrays resides in nib A, and each VC will load nib B.)

The VC that you created in a nib is the one whose view appears on the screen. Because that object never received an init message (it was initialized with some other initWith… message instead), you never created its arrays. The view controller you created with init, which does have its arrays, is not visible on the screen (otherwise you would have clicked on its button, rather than the other's, and you'd have seen the arrays in the output).

The solution involves two changes.

The first is to change your implementation of init to be an implementation of initWithNibName:bundle: instead. Like so:

- (instancetype) initWithNibName:(NSString *)nibName
    bundle:(NSBundle *)bundle
{
    self = [super initWithNibName:nibName bundle:bundle];
    if (self != nil) {
        <#...#>
    }
    return self;
}

If you want to continue using init to create your VC in other code, fine, but your implementation of -[RBMainViewController init] should simply send initWithNibName:bundle: to self and return the result.

- (instancetype) init {
    return [self initWithNibName:<#nibName#> bundle:<#bundle#>];
}

You also need to delete one of the two view controllers. We'd need to see the code and the nib to know which. If you delete the one you created in code, you may want to create an outlet in that class and connect it in the nib. If you delete the one in the nib, any outlet connections you established in that nib to that VC, you'll need to re-create in code.

Upvotes: 1

Joe
Joe

Reputation: 2987

I believe you will need to use self.entites and self.tiles when working with those objects in your class.

EDIT (after interface added)

You will need to have something in your interface like:

@interface RBMainViewController : NSViewController{
    NSMutableArray* _entities;
    NSMutableArray* _tiles;
}

@property (retain) NSMutableArray* entities;
@property (retain) NSMutableArray* tiles;

- (IBAction)log:(id)sender;

@end

Then you will need to add this to the implementation:

@synthesize entities = _entities;
@synthesize tiles = _tiles;

Upvotes: 0

jweyrich
jweyrich

Reputation: 32260

Write a custom getter/setter for one of these properties, and put a breakpoint there to see who's resetting the value. If that doesn't catch the problem, and your property is still being reset, you're probably accessing them from an instance of your NSViewController that hasn't been initialised by your init, thus those properties were always uninitialised.

Upvotes: 0

Related Questions