views:

263

answers:

5

So, my second question based off of this application I'm teaching myself Objective C with. I have a Data Source class, which for now looks mostly like:

- (id) init
{
    if (self = [super init]){
    listNames =  [[NSArray alloc] initWithObjects: @"Grocery", @"Wedding", @"History class",@"CS Class",@"Robotics",@"Nuclear Sciences",
                  @"Video",@"Library",@"Funeral", nil];
    NSLog(@"%@",listNames);
    }
    return self;
}

The .h looks as follows:

@interface MainViewDataSource : NSObject {
    NSArray *listNames;
}

@property (nonatomic, retain) NSArray *listNames;
-(NSArray *)getListNames;

So that's where I make it. Now the problem is that when I try to get the array listNames, it returns nothing.

The following piece:

NSArray* data = [listData listNames];

Is supposed to put the information in listNames in data, but... isn't. Since I'm rather used to JAva, I'm betting this is an Objective C quirk that I don't know how to fix. Which is why I'd be here asking for help. What's the proper way to pass around NSArrays like this?

A: 

You have to create the implementation of the getter. The compiler can do that for you by using @synthesize:

@synthesize listNames;

You don't have to declare the getter method.

Philippe Leybaert
Done. Still not working however. Also have a synthesize for the listData class in the main Delegate. @synthesize listData;That should call the "init" function of listData, correct? Since the init is where I insert the data into listNames.
Althane
init is not called by the synthesized getter/setter functions. The code that allocs the class should call the init function. Did you see the NSLog output from init when you ran it?
progrmr
No, but I've had problems with NSLog being eaten by the console before, so I wasn't sure. So, instead of synthesize for listData, I should say "listData = [MainViewDataSource init]"?
Althane
+1  A: 

Are you running -init on MainViewDataSource?

MainViewDataSource *_source = [[MainViewDataSource alloc] init];
NSLog(@"listNames --> %@", [_source listNames]);
...
[_source release];

Also make sure your -dealloc method includes a release for listNames or you will have a memory leak.

Alex Reynolds
As I added to the answer below, I was not. I'll implement this code and see if it works.
Althane
You might think of `MainViewDataSource *_sourceObjC = [[MainViewDataSource alloc] init]` being equivalent to `MainViewDataSource _sourceJava = new MainViewDataSource()` in Java. Except that your `_sourceObjC` needs to released at some point in the future.
Alex Reynolds
Okay, that worked. That's one problem down, now to see what other problems occur in my code after this starts working. Thanks.
Althane
You must also remember to release any member variables that themselves have been `alloc-init` -ed.
Alex Reynolds
Pardon me for being an idiot, but what do you mean by that?
Althane
In the `-dealloc` method for `MainViewDataSource`, you need to specify `[listNames release]` because you are doing an `alloc-init` of `listNames` in the `MainViewDataSource` initialization. Unlike Java, you have to manage your memory usage for the iPhone -- anything you `alloc-init` needs a `release` somewhere down the line, or you will have a memory leak.
Alex Reynolds
Take a look at this video for more info: http://maniacdev.com/2009/07/video-tutorials-on-objective-c-memory-management/
Alex Reynolds
Ah. Okay, I knew to do that from the book telling me to do it, but the book I had wasn't very good (the "For dummies" one. New book is being ordered)
Althane
A: 

Without seeing all of your code, I think you may need to retain the array in your other class. The idea is if you are using another classes object, you need to retain it so that it doesn't get released with MainViewDataSource object. Try...

NSArray* data = [[listData listNames] retain];

And see if you can then get your array to print.

Staros
A: 

I would guess you have not initialized listData. You can alloc and init it like this:

MainViewDataSource* listData = [[MainViewDataSource alloc] init];

then you can get a pointer to the array like this:

NSArray* data = [listData listNames]; 

OR

NSArray* data = listData.listNames;
progrmr
A: 

Accessors... Accessors... Accessors...

Use them ALWAYS. No exceptions (until you learn them).

when you do:

listNames =  [[NSArray alloc] initWithObjects: @"Grocery", @"Wedding", @"History class",@"CS Class",@"Robotics",@"Nuclear Sciences", @"Video",@"Library",@"Funeral", nil];

you really want to be doing:

[self setListNames:[NSArray arrayWithObjects: @"Grocery", @"Wedding", @"History class",@"CS Class",@"Robotics",@"Nuclear Sciences", @"Video",@"Library",@"Funeral", nil];

or

self.listNames = [NSArray arrayWithObjects: @"Grocery", @"Wedding", @"History class",@"CS Class",@"Robotics",@"Nuclear Sciences", @"Video",@"Library",@"Funeral", nil];

by setting the listNames directly (no reference to self), you are not using the accessors and will open a whole world of hurt as you try to get you leaks cleared up. To get to your list, simply use the accessor:

[self listNames];

or

self.listNames;

Again, avoid the temptation to NOT use the accessors.

Steven Noyes
Apple recommend that you do not use the accessor methods from within your `init` and `dealloc` methods. See here: http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html
dreamlax
Specific excerpt: *The only places you shouldn’t use accessor methods to set an instance variable are in `init` methods and `dealloc`*
dreamlax
As I said, until you learn when not to use them. There may (but almost never is. Like ever) be possible interactions with KVO/KVC. Even there, however, it is not an issue if you design your observers right in being able to handle the nil case since that is always a possibility.So, this Guideline is simply that, a guideline. You will get much simpler code if you actually DO use the accessors within both the init code and the dealloc code. Of course, that depends on doing the correct defensive programming on any observers.
Steven Noyes
It's not always an issue with KVC. For example, an issue with using accessor methods in `dealloc` is that if the accessor method has been overridden by a subclass, it may not return the object that you want to release, it may return a completely different object, which it is allowed to do. There are two major reasons that come to mind: (1) a subclass can alter the behaviour of an accessor, and (2) accessors can have side effects, or may feasibly or naively expect a fully initialised instance.
dreamlax
Not even then. On your 1) case, if the subclass modifies the behavior of the accessor by over-riding it, it is the responsibility of the subclass to allocate, set and deallocate things correctly. If you do a [self setName:nil] and that calls the subclass that still ends up freeing Name. If it calls [super setName:nil] is up to the implementation of the subclass. Case 2) indicates really poor design. Both have design weaknesses and in 100's of thousands of lines of Cocoa, I will take the simpler code, no memory leaks (like ever) and the 1 in 10,000 chance of issues compared to ugly code.
Steven Noyes
I guess what I am saying is, that after almost 15 years of doing OpenStep/Cocoa, I found the shift in pattern (for no discernible reason) of init and dealloc methods a bit off on Apple's part. There are a very very few cases (and not any that you mentioned so far) where it does matter. But in the general case, ALWAYS USING accessors generates far less code and far fewer issues in the long run.
Steven Noyes