views:

49

answers:

3

The data source for my table view is a plain NSMutableArray that will be populated as the app runs, but will be empty when the Table View first loads. The class interface looks like this...

@interface ViewController_iPhone : UITableViewController  {

    NSMutableArray *serverList; 
}

@property (retain, readonly) NSMutableArray *serverList;

@end

My questions are...

  1. Currently, I initialize it in the viewDidLoad method like so...

    serverList = [[NSMutableArray alloc] initWithCapacity:1];

    I do it this way because the array needs to be valid in order for my numberOfRowsInSection method to avoid crashing when reading the count of the array (which will be zero) when the view first loads. My current approach of using initWithCapacity just feels a little clunky since I just need an empty, but valid array object that will return a count value of zero when the view loads. How should I be initializing my serverList array?

  2. While playing around, I noticed that when I try and initialize the serverList array this way...

    serverList = [NSMutableArray arrayWithCapacity:1];

    it crashes on that line. Why?

Thanks in advance for all your help!

A: 

Note that capacity does not mean any objects are in the array. Using -initWithCapacity: simply sets aside a chunk of space for the array. Nothing is in the array even with a non-zero capacity.

The following initializers:

self.serverList = [[[NSMutableArray alloc] init] autorelease];
self.serverList = [[[NSMutableArray alloc] initWithCapacity:capacity] autorelease];
self.serverList = [NSMutableArray array];
self.serverList = [NSMutableArray arrayWithCapacity:capacity];

should all work, however.

Make sure you specify self so that the property is accessed. Using serverList by itself will not work.

Make sure you autorelease any alloc-init of a property which you are retaining, otherwise you will have a memory leak.

You do not need an array with a specified capacity. This is just a performance convenience for setting aside contiguous memory, and useful if you have a rough idea how much space your array will need up-front. An array that is created through alloc-init will also be empty, with a count of zero.

Alex Reynolds
thanks, alex! when i use 'serverList = [[NSMutableArray alloc] init];' it seems to work great. why do you say using 'severList' instead of "self.serverList' won't work?
BeachRunnerJoe
Examine the value of `serverList` outside of the method in which you are setting `serverList = [[NSMutableArray alloc] init]`. Do an `NSLog` on it in a separate method.
Alex Reynolds
Because you are `retain`-ing your array, also make sure you `autorelease` an `init`-ed array, or you will have a memory leak.
Alex Reynolds
thanks, alex! i'm still unclear. if i'm not using the "self" object, then i'm not calling the accessor, correct? if so, then i'm not retaining my array either since i'm accessing the ivar directly. is that right? if so, i'm unsure why you say i'm "retain"-ing my array.
BeachRunnerJoe
You defined `serverList` as a retained instance property in the header: `@property (retain, readonly) NSMutableArray *serverList;` I'm not positive why you make it `readonly`, though. You'd need to create your own custom settor, unless you allow `readwrite` access. You may also want to set this property as `nonatomic`, for performance reasons. By default, your `serverList` will be `atomic`, which locks and unlocks access to `serverList`. This overhead slows down access to this property.
Alex Reynolds
You might want to read about declared properties. More info is available here: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
Alex Reynolds
the reason i declare it readonly is because i don't want it to have a setter. the reason i access the ivar directly when creating it is because it doesn't have a setter. anyhow, i've got the code working well now with "serverList = [[NSMutableArray alloc] init]" and "[serverList release]" in the dealloc method. thanks for your help!
BeachRunnerJoe
There's no problem to making it `readonly`, since as the datasource, there's no reason why anyone outside the datasource should be setting this property. Also, `serverList = [[NSMutableArray alloc] init];` will work just fine; in fact, it's the only way to do it if the property is readonly.
David Liu
A: 

Here's a key concept to learn about member variables and properties: Member variables are not the same as properties.

That is, when accessing a member variable in your class:
serverList = [NSArray array]; is not the same as self.serverList = [NSArray array];

serverList by itself means you're accessing it directly.
self.serverList means you're using the getter/setter methods to access it.

Normally this isn't that big of a deal when dealing with basic variable types. However, when your property uses retain or copy, that means your setter method will automatically retain it when you use it, but it won't do such when you access it directly.

That means: serverList = [NSArray array]; will not retain the array. self.serverList = [NSArray array]; will retain the array.

It should be noted that [NSMutableArray arrayWithCapacity:1]; (and 99% of other methods that aren't alloc) will return an object that is autoreleased. If you want to keep it for later use, as you need to in this case, then you must retain it in some form or fashion.

David Liu
i'm not sure you answered any part of his question at all
His second part of the question is about basic memory management issues.
David Liu
+1  A: 

I somehow missed the simplest approach and found that when I simply create the array like so...

serverList = [[NSMutableArray alloc] init];

and release it in the dealloc method, everything works great!

BeachRunnerJoe