NSMutableArrays - iphone

I have an NSMutableArray as a member variable for a class.
In the .h file:
#interface bleh {
NSMutableArray *list;
}
#property (readonly, assign) NSMutableArray *list;
#end
In the .m file:
#implementation bleh
#synthesize list;
-(void)init;
{
list = [NSMutableArray arrayWithCapacity:30];
}
#end
Now, I'm not really an objective-C programmer, so maybe I'm missing some of the nuances, but when I do the following:
NSMutableString *listItem = [NSMutableString stringWithString:#"Foobar"];
[list addObject:listItem];
I'm getting strange behavior. Namely, I'm using this to keep a list of files that I eventually want to attach to an email and then open the picker. I'm getting a SIGABRT, and upon debugging, I find out that whenever I operate on list, I'm getting nothing. addObject messages don't increase the size of the NSMutableArray at all.
Am I missing something? Can someone show me a full implementation of setting up an NSMutableArray to be manipulated within a class in Objective C?
Thanks.
PS - Assume that I'm smart enough to put the manipulations of the NSMutableArray inside of a member function for the class containing the member variable.

in the latest release of the SDK arrayWithCapacity is bad practice.
but in your code you creating a array that no one is owner , clam your array properly.
don't forget initialize your array
NSMutableArray *array = [[NSMutableArray alloc] init];
fix the (readonly,assign),

How are you actually creating your array? Is it possible that it's being autoreleased and going away? Remember, if you create it with a convenience method (like array or something) you need to retain it.

You're creating the array with arrayWithCapacity:, which returns an array you don't own, and you're never claiming ownership over it. Use a property accessor to retain the array:
self.list = [NSMutableArray arrayWithCapacity:30];
I would recommend reading the Cocoa memory management docs. Once you know the rules in there, it will be clear what to do in this sort of situation. They're not very hard, but they are very necessary if you're going to be programming Cocoa.

Your list variable has been auto-released and de-allocated, therefore your program crashes when you try to access it.
There are two ways to create objects in Cocoa:
NSMutableArray* array1 = [[NSMutableArray alloc] initWithCapacity:10];
NSMutableArray* array2 = [NSMutableArray arrayWithCapacity:10];
array1 was created using alloc+init, therefore you own it. It will stick around until you release it.
array2 was not created using alloc+init, therefore you do not own it. You're not responsible for releasing it, but it will go away on its own. You must retain array2 if you want it to stick around.

Your list property declaration is keeping you from properly retaining the NSMutableArray. By calling arrayWithCapacity you're effectively putting the array in an autorelease pool, which means it could be deallocated at any time if no object interested in keeping it around. While you are, the way you have things declared doesn't reflect that:
#property (readonly, assign) NSMutableArray *list;
The above declaration simply sets this pointer to be a copy of another pointer - it does no memory management for you. Instead it should read:
#property (readonly, retain) NSMutableArray *list;
... and you should assign the list like so:
self.list = [NSMutableArray arrayWithCapacity:64];
Because you specify the retain attribute for the property, whenever it is assigned a new value the retain message will be sent to that new value, communicating to the memory manager that you don't want this object deallocated. In order to bring this full circle, you'll need to release the object when you containing class is deallocated:
- (void)dealloc
{
[list release];
[super dealloc];
}

Are you initializing your list properly? Ie do you have something like the following in your code?
list = [[NSMutableArray alloc] init];

Problem ehre (assuming you initing your array properly) could be that #"Foobar" assings an NSString not an NSMutableString so its failing because if distinct types you should do
NSMutableString *listItem = [NSMutableString stringWithString:#"Foobar"];
[list addObject:listItem];
or
NSString *listItem =#"FooBar";
[list addObject:listItem];

It doesn't look as though you've actually initialized the NSMutableArray.
In the init event of the object, just say
[self setList:[[NSMutableArray alloc] initWithCapacity:10]]];
(I would just say init, but I don't remember if that works. It doesn't matter what capacity you start with)
Before actually allocating the array, the variable "list" will have a value of nil.

Related

Objective C: Proper way to init an NSArray that is a #property

I have a property in my class, which is an NSArray. I am retaining the property.
My question is, what is the proper way to add objects to that array without leaking and making the retain count too high?
This is what I am using:
.h:
NSArray *foodLocations;
#property (nonatomic, retain) NSArray *foodLocations;
// I make sure to synthesize and release the property in my dealloc.
.m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *tempFood = [[NSArray alloc] initWithArray:[self returnOtherArray]];
self.foodLocations = tempFood;
[tempFood release];
}
Is this the correct way to do it?
Yes this is correct and my preferred way of doing it as it renders the code more readable.
You are essentially allocating a temporary array and then assigning it to your property with a retain attribute, so it is safe to dealloc it as your property now "owns" it. Just remember that you still need to release it in your dealloc method.
You could also initialise the array and assign it to the property in the view controllers init method, depending on whether you need the property to be available to you before the view actually loads (i.e. in case you want to read the value of the property before pushing the view controller etc...)
you will typically want to declare the property copy in this case.
in most cases, immutable collection accessors should be copy, not retain. a lot of people get this wrong, and end up writing a lot of copying manually and sharing objects which should not be shared, thinking they are doing themselves good by cutting a corner.
copying in this form (the collection) is shallow. the objects in the array are not copied, just the array's allocation.
a good implementation of an immutable collection can simply implement copy by retaining self. if the argument is mutable, you want a copy anyhow (in the majority of cases).
your program is then simplified to a declaration of:
// note: copy, not retain. honor this if you implement the accessors.
#property (nonatomic, copy) NSArray * foodLocations;
and then the setter:
self.foodLocations = [self returnOtherArray];
of course, you must still init, dealloc, and handle thread-safety appropriately.
good luck
That looks fine. You don't actually need the tempFood variable, you can just do:
self.foodLocations = [[NSArray alloc] initWithArray:[self returnOtherArray]];
[self.foodLocations release];
or:
self.foodLocations = [[[NSArray alloc] initWithArray:[self returnOtherArray]] autorelease];
Or:
#synthesize foodLocations=_foodLocations;
then in code
_foodLocations = [[NSArray alloc] initWithArray:someOtherArray];
This avoids the autorelease required by
self.foodLocations = [[[NSArray alloc] initWithArray:someOtherArray] autorelease];
Yes, that is correct. Also good to keep in mind is what #synthesize is, in effect, doing for you. A synthesized (& retained) setter is functionally equivalent to the following code:
- (void)setVar:(id)_var {
[_var retain];
[var release];
var = _var;
[var retain];
[_var release];
}
So, basically, every time you call self.var = foo, it releases the previously stored value and retains the new one. You handle the reference counting in your code, and the setter handles its own.

Accessing Class Data Members in ViewController.m (xCode / Objective C)

I have a class myClass and would like to access its properties, a NSArray *currentOptions (specifically to get the size of currentOptions and access the NSStrings which I've put in it.)
I have a method called generate options which assigns an filled array to *currentOptions. Generate options is called before I try to access *currentOptions. An instance of myClass has also been added to the ViewController via the App delegate. However when buttonOnePressed is called, I keep getting this error:
[myClass currentOptions]: unrecognized selector sent to instance 0x9b10490
Here is the parts of my code:
//TClass.h
#interface TClass : NSObject {
NSArray *currentOptions;
}
#property (nonatomic, retain) NSArray *currentOptions;
#end
//viewController
- (IBAction) buttonOnePressed:(id)sender {
NSLog(#"button1 pressed");
NSLog(#"int: %d",[myClass.currentOptions count]);
//myClass here is the instance of TClass
}
One thing that sometimes causes that error is failing to properly retain myClass. (Aside: "myClass" is a really bad name for a pointer because the thing being pointed to is almost certainly not a class but an object, i.e. an instance of a class.) If you don't retain the object that myClass points to, it will be deallocated. Sometimes, a different object happens to be created at that some location, and you end up sending a message meant for the original object to the new one, which is a different type and doesn't understand the message.
To all who have been following, the problem has been resolved by making the following changes:
1) Synthesized current options TClass.m
#implementation TClass
#synthesize currentOptions;
#end
2) I made currentOptions a NSMutableArray instead of a NSArray. This is because I need to reassign values to current options. Somehow it crashes with NSArray and everything goes smoothly with NSMutable array like such
#implementation TutorialClass
if ([currentOptions count] > 0) {
[currentOptions removeAllObjects];
}
[currentOptions addObject:[options objectAtIndex:0]];
[currentOptions addObject:[options objectAtIndex:1]];
[currentOptions addObject:[options objectAtIndex:2]];
[currentOptions addObject:[options objectAtIndex:3]];
3) And of course, I'll also have to do the following in the init method of TClass.m
currentOptions = [[NSMutableArray alloc] init];
Now its time to get some food. Thanks Caleb :D

iOS - resetting NSMutable Array causes crash

I have an NSMutableArray I'm trying to reload after an async call. The first time it loads like this:
self.sessionProcList = [NSMutableArray arrayWithArray:[result records]];
After the user does some interaction, the same line will be reached to reload the NSMutableArray. This causes the crash
Header file has:
#interface...
NSMutableArray *sessionProcList;
... }
#property (nonatomic, retain) NSMutableArray *sessionProcList;
Say you do:
NSMutableArray *a = [NSMutableArray arrayWithObject: [[NSObject alloc] init]];
NSObject *o = [a objectAtIndex: 0];
[a removeAllObjects];
[o description]; // *BOOM*
The above will [generally -- sometimes not but only by coincidence] crash because o has been deallocated by the time the description method is invoked.
If you have a reference to an object in an array, but have not retained said reference, then said object may be deallocated out from under you when you empty the array.
(And nonatomic vs. atomic is irrelevant.)
If I had to guess, I would say that elements in that array are being referenced from somewhere else. Resetting the array causes the items using the references to crash.
I would check your application for other variables, properties, or UI elements using those variables that have not been release before resetting it.
Because arrayWithArray is a convenience method it gets initialised with an autorelease flag.
You haven't mentioned what the crash / error message is but I am guessing your NSMutableArray is getting released before your second iteraction with it starts.
Try and retain the array for however long you need it with:
self.sessionProcList = [NSMutableArray arrayWithArray:[result records]];
[sessionProcList retain];
And then release it when you're done with it:
[sessionProcList release];
I hope it helps.
Rog

Memory management - how best to initialise an instance declared in the header

I've read a few posts on this, but there's still one thing that's not clear for me. I know this might be rather a n00b question, but I've actually got rather far into development without quite grasping this fundamental issue. A symptom of being self taught I guess.
You declare a variable in your header, like so:
#interface SomeClass : NSObject {
NSMutableArray *anArray;
}
#property (nonatomic, retain) NSMutableArray *anArray;
end
And then in your main file you synthesise it and set it to an initial value:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];
And release it when your Class deallocs:
- (void)dealloc {
[anArray release];
[super dealloc];
}
Now, when I run instruments, the line
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
is identified as a memory leak. Is it a memory leak because when you define the variable anArray in the header it allocates memory? (Because I thought it was a null pointer.) Therefore when you want to initialise it, and you call [[NSMutableArray alloc] initWithCapacity:10], you are reallocating the memory, and losing the pointer to the original allocation?
So instead, I use the convenience class method:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [NSMutableArray arrayWithCapacity:10];
}
[return self];
This is no longer identified as a memory leak in instruments. And since it's a convenience method, anArray is autoreleased. However, if I am to assume that the instance declaration in the header allocates memory, which would explain the previous issue, then should I still release anArray? Does setting the initial values in this way retain it perhaps?
I understand the difference between
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];
and
NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];
but what I'm not sure I understand is when you've declared NSMutableArray *anArray in your header, which of the two approaches you should use and why. And whether or not if you use the second approach, you should still release anArray when you call dealloc.
I might add that I've found the following posts/links useful:
Suggest the best way of initialization of array ( or other objects )
What is the cost of using autorelease in Cocoa?
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
What is the difference between class and instance methods?
alloc'ing an object starts it off with a reference count of 1.
Setting a property that has the 'retain' attribute also increases the reference count.
So, that means this is usually bad:
#property (nonatomic, retain) Object * variable;
...
self.variable = [[Object alloc] init];
Because variable now has a reference count of 2.
When setting a object's member variable, just do this:
variable = [[Object alloc] init];
You should also realize that this works
self.anArray = [NSMutableArray arrayWithCapacity:10];
Because "arrayWithCapacity" (and other similar factor methods) autoreleases the object it returns, so after you set the property, it essentially has a reference count of 1.
It's not the instance that allocates the memory. You're right to assume that in Objective-C (at least on all Apple-based operating systems), newly initialized classes have all their ivars set to 0 (or nil or NULL as appropriate).
The problem you're seeing is that you're using the property, not the ivar in your initialization. Since you declared your property as retain, using the property accessor to set it automatically retains it.
So, when you initialize you either have to take ownership and set the ivar directly, or do like you're doing and use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own or, as you did in your second instance, using the convenience constructor so that you never owned the returned instance).
So just remember, if you ever use the property accessors, even within the class itself, you will get the features you set on the property (e.g., nonatomic, retain, etc.). You use the property accessors whenever you do one of the following:
// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:#"anArray"];
You would access your ivar directly like:
anArray = something; // in this case you must take ownership

EXC_BAD_ACCESS when debugging

I'm getting this error when trying to see the contents of a NSMutableArray:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000021
0x94d5a688 in objc_msgSend ()
ViewController.h:
#interface PeopleViewController : UITableViewController {
NSMutableArray *people;
}
#property (nonatomic, retain) NSMutableArray *people;
ViewController.m:
#implementation PeopleViewController
#synthesize people;
In viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// initialize our people array with an autoreleased object
people = [NSMutableArray array];
... Populate the people array with Person objects.
}
When I'm at the point where I'm modifying the contents of a cell in the tableview, I'm unable to access the people array in gdb when typing 'po self.people':
Person *person = [[Person alloc] init];
person = [self.people objectAtIndex: indexPath.row]; // <--- 'po self.people' called
cell.textLabel.text = person.personName;
Any ideas why I can't access it?
The line
people = [NSMutableArray array];
returns an autoreleased array that will be released on the next iteration of the current run loop. You should retain that:
people = [[NSMutableArray array] retain];
and of course release it in your dealloc method.
However: Apple engineers have often mentioned in conferences to avoid autoreleased instances like this whenever possible in the iPhone, for performance reasons. Try using alloc/init instead:
people = [[NSMutableArray alloc] initWithCapacity:1];
with the corresponding release in the dealloc method. In this case you don't even need to retain (init returns an instance with a retain count of 1, which is what you need).
And justin's comment is correct: you should do this instead:
Person *person = [people objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
and this should work.
is indexPath.row > [people count]?
Also, why are you doing this:
Person *person = [[Person alloc] init]
You're allocating memory, and then pointing to completely different memory.
You can avoid having to fuss with retaining properties by using the self notation to call the accessor and setter methods created by the #synthesize directive.
When you set the people property directly in viewDidLoad it sets the property but does nothing for memory management. However, if you set it with self.people you actually call the synthesized setter method that because of the retain setting of the #property directive will automatically retain the assigned array.
As an aside, I would recommend always using -[NSMutableArray initWithCapacity:] instead of a bare init. It is the actual initializer for the class. You can call it with just '1' if you don't know how big it will be. In the past, I have seen odd problem arise from just using bare init.