Pass NSMutableArray object - objective-c

I'm getting lost in pointer land, I believe. I've got this (code syntax might be a little off, I am not looking at the machine with this code on it...but all the pertinent details are correct):
NSMutableArray *tmp = [[NSMutableArray alloc] init];
I them pass that to a routine in another class
- (BOOL)myRoutine: (NSMutableArray *)inArray
{
// Adds items to the array -- if I break at the end of this function, the inArray variable has a count of 10
}
But when the code comes back into the calling routine, [tmp count] is 0.
I must be missing something very simple and yet very fundamental, but for the life of me I can't see it. Can anyone point out what I'm doing wrong?
EDIT: www.stray-bits.com asked if I have retained a reference to it, and I said "maybe...we tried this: NSMutableArray *tmp = [[[NSMutableArray alloc] init] retain]; not sure if that is what you mean, or if I did it right.
EDIT2: Mike McMaster and Andy -- you guys are probably right, then. I don't have the code here (it's on a colleague's machine and they have left for the day), but to fill the array with values we were doing something along the lines of using a decoder(?) object.
The purpose of this function is to open a file from the iPhone, read that file into an array (it's an array of objects that we saved in a previous run of the program). That "decoder" thing has a method that puts data into the array.
Man, I've totally butchered this. I really hope you all can follow, and thanks for the advice. We'll look more closely at it.

You don't need to call retain in this case. [[NSMutableArray alloc] init] creates the object with a retain count of 1, so it won't get released until you specifically release it.
It would be good to see more of the code. I don't think the error is in the very small amount you've posted so far..

I agree with Mike - based on the code you've posted, it looks correct. In addition to posting the code used to call the function and add items to the array, you could try checking the memory addresses of the pointer at the end of the function (when it has all of the objects), and also once it has returned (when it has no objects). I'm not sure why it would be different, but then again the items should stick in the array as well.

You need to show us a bit more of how you're adding objects to the array for us to really help.
I've seen a lot of people write code like this:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0];
array = [foo bar];
People doing this think it "creates and then sets" a mutable array, but that's not at all what it does. Instead, it creates a mutable array, assigns it to the variable named array, and then assigns a different mutable array to that variable.
So be sure you're not confusing the variable for the object to which it is a reference. The object isn't the variable, it's interacted with through the variable.

NSMutableArray retains objects added to it, but have you retained the array itself?

The code you posted should work. You must be doing something funny in the decoder function.
You should not retain that array. It's automatically retained with init. If you retain it, you'll leak memory. If you are just starting with objective c, take time and read "Introduction to Memory Management Programming Guide for Cocoa". It will spare you lots of headache.
Why are you writing so much code to read an array from a file? It's already supported by the framework:
+ arrayWithContentsOfFile:
Returns an array initialized from the contents of a specified file.
The specified file can be a full or
relative pathname; the file that it
names must contain a string
representation of an array, such as
that produced by the
writeToFile:atomically: method.
So you can do this:
NSMuatableArray *myArray = [NSMutableArray arrayWithContentsOfFile:#"path/to/my/file"];
This is a convenience method, so the object will autorelease. Make sure to retain this one if you want to keep it around.

Related

Assign or retain in cocos2d and objective C

Here's my current situation:
I have a NSMutableArray named dictKeyArray which I assign a property with #property(nonatomic,retain)NSMutableArray *dictKeyArray
I synthesize my mutable array in the implementation file.
Later, I have a dictionary name storeDict. I assign all the keys of the dictionary to the dictKeyArray like so:
dictKeyArray = [[storeDict allKeys] mutableCopy];
Now I use this dictionary later in my implementation file. However, when it comes to releasing it, I release it once in my dealloc method. When checking with instruments, a leak shows up! Why is dictKeyArray leaking? Should I be using assign instead of retain?
I'm still not clear on what the difference is exactly...
thank you!
You have to send it an
[[[storeDict allKeys] mutableCopy] autorelease];
Just to make this clear: mutableCopy does the same as alloc meaning you are claiming ownership of the object in question. You have to decrease the retainCount by one.
By the way: You should use the accessor you wrote for it. You are just assigning it to your iVar at the moment. If you want to make your accessors work, you will have to use something like
object.dictKeyArray = ...;
in general. Or here (as mentioned by dreamlax)
self.dictKeyArray = ...;
because you are referring to an object of this specific class the code is in.
Only this way you are ensuring your object is properly retained by your accessor. Otherwise writing the accessor code doesn't make sense at all because it never gets called.
Please note: As Josh said in the comments, your code should be valid (at least from my point of view). What I suggested is a solution that is not as error-prone as yours because you adhere to the rules (could save you from headache in the near future).
You should be using self.dictKeyArray = .... Without the self. you are accessing the instance variable directly, bypassing any memory management benefits of properties, but, remember that you own the result of mutableCopy, and assigning to a property that also takes ownership will result in double-ownership, so use:
self.dictKeyArray = [[[storeDict allKeys] mutableCopy] autorelease];

Re-initialize NSMutableArray as NSMutableArray

I was having a problem with my app throwing an exception when calling removeObjectAtIndex on an NSMutableArray, saying that myLocationsArray was declared immutable. All other manipulation on that array was fine, it was most definitely declared correctly etc etc but somewhere in my app it was getting set as immutable. After investigating for a while trying to find where it was getting set immutable, I decided screw it and just redeclared the variable as such:
myLocationsArray = [[NSMutableArray alloc] initWithArray:[defaults
objectForKey:MYLOCATIONSARRAY_KEY]];
right before the removeObjectAtIndex call.
However I know this has got to be badwrong, I'm calling alloc/init twice on the same variable. However it's the only thing that has worked. Is there any way to remind this variable that it is an NSMutableArray without introducing memory leaks like I am?
NSUserDefaults returns immutable copy of your array. It doesn't matter whether you put NSArray or NSMutableArray in it, it always give you immutable copy back.
So, do this to get a mutable copy that you can work with
myLocationsArray = [[NSMutableArray alloc] initWithArray:[[[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy] autorelease]];
or just this
myLocationsArray = [[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy];
I would suggest to set a breakpoint on the line where your program is throwing an exception (the one containing removeObjectAtIndex) and inspect with the debugger the real type of the array. If you go with you mouse over the array name, a popup menu will display giving you all the information you need about the pointed object.
What I expect is that you find out this way that the object is an NSArray (vs. NSMutableArray) and then trace back to the point where you initialized it in the first place.
It looks like you're working with NSUserDefaults. All objects you get out of NSUserDefaults are always immutable, regardless of what you stored into it. NSUserDefaults doesn't keep a reference to the specific object you set into it, it keeps the data. It's effectively making a copy. When you get something out of NSUserDefaults, it makes a new (immutable) object from the data it has stored and gives that to you.
Unsurprisingly, you can't change what's stored in NSUserDefaults by mutating what you (think you) stored in it. You can only change what's stored by replacing what you previously stored by storing something anew.
The declaration should not matter; your error is a run-time error. It sounds like your myLocationsArray variable has been assigned an immutable array (NSArray) though whether it is being re-assigned somewhere or was always immutable is impossible to say from your code fragment.

When and when to not allocate memory to objects

NSArray *array = [dictionary objectForKey:#"field"];
and
NSArray *array = [[NSArray alloc] initWithArray:[dictionary objectForKey:#"field"]];
I see both kind of approaches very frequently in objective C code.
When tried to understand, I found both of them used in similar situation too, which makes contradiction. I am not clear on when I should use 1st approach and when 2nd one?
Any idea?
Detailed explanation and useful references are moms welcome.
First off, those two examples are doing slightly different things. One is retrieving something from an existing dictionary and one is creating a new array by retrieving something from an existing dictionary (the value of that key is an array).
But, if you're asking the difference between getting objects by alloc vs. convenience methods. ([NSString alloc] init vs [NSString stringWith ...), by convention, you own anything that you call alloc, new copy or mutableCopy on. Anything that you call that is not those, is autoreleased.
See the memory guide here. Specifically, look at the rules.
Getting an autoreleased object means it will go away at some point in the near future. If you don't need to hold onto outside the scope of that function, then you can call autorelease on it or use one of the convenience methods that's not alloc, etc...
For example:
// my object doesn't need that formatted string - create the autoreleased version of it.
- (NSString) description {
return [NSString stringWithFormat:#"%# : %d", _title, _id];
}
// my object stuffed it away in an iVar - I need the retained version of it. release in dealloc
- (void) prepare {
_myVal = [[NSString alloc] initWithFormat:"string I need for %d", _id];
}
In the first example, I created a convenience methods for others to call, my class doesn't need that object beyond the scope of that method so I create the autoreleased version of it and return it. If the caller needs it beyond the scope of his calling method, he can retain it. If not he can use it and let it go away. Very little code.
In the second example, I'm formatting a string and assigning it to an iVar variable that I need to hold onto for the lifetime of my class so I call alloc which will retain it. I own it and releasing it eventually. Now, I could have used the first version here and just called retain on it as well.
You have a fundamental misunderstanding of allocations versus instance methods.
The first example, NSDictionary's -objectForKey method, returns id, not an instance of NSDictionary, therefore it does not allocate or initialize the variable.
The second, however is the classic retain part of the retain-release cycle.
The two methods are fundamentally equal (if we are to assume that array is alloc'd but empty in the first, and nil in the second), and both get ownership of the array object. I would go with the second, as it guarantees a reference, and it's shorter.
What I think you're confusing this with are new and convenience methods. Convenience methods (like NSNumber's +numberWithInt:, NSString's +stringWithFormat:, and NSMutableArray's +array), return an autorelease instance of the class (usually). New takes the place of alloc and init in just one word.

How manages the memory to use copied array and dictionary?

I have removable data in Array (NSMutableArray)...(When data remove in array, array uses other place. so, I retain that.)
also, I used copy command....but I not found how release that.
NSMutableArray *arrRemovable;
NSMutableDictionary *dicData; (alloc, and init)
[_dicData setObject:[arrRemovable copy] forKey:#"DATA1"];
(it's already retain count 2)
I want not to make the arrRemovable variable..
i want to subtract retain count.
[[_dicData objectForKey:#"DATA1"] release]
I using that, but analysis tool warned me.
Message is "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
help me!!
You can do this:
NSMutableArray *copyOfArrRemovable = [arrRemovable copy];
[_dicData setObject:copyOfArrRemovable forKey:#"Data1"];
[copyOfArrRemovable release];
This is assuming I understood the question correctly. If not, please explain.
Hope that helps.

NSMutableArray count always returns zero

I'm sure I'm doing something silly, but this is driving me crazy.
I'm trying to loop through database results, create objects from those results, and add the objects to an NSMutableArray. I've verified via NSLog calls that the data is being correctly read from the database and copied to the object, but the count for the NSMutableArray always returns 0.
Here's the essence of the code:
while ([rs next]) {
Kana *htemp = [Kana alloc];
htemp.content = [rs stringForColumn:#"hiragana"];
[hiragana addObject:htemp];
}
NSLog(#"Hiragana contains %d objects", [hiragana count]);
Kana is derived from NSObject, and hiragana is an instance of NSMutableArray.
I'm sure this is a rookie mistake, and I hope someone can set me straight. TIA! :)
My guess, judging from the code you posted, is that you probably aren't allocating your array properly. When creating objects, you need to initialize them as well. Therefore, this:
Kana *htemp = [Kana alloc];
Should be:
Kata *temp = [[Kana alloc] init];
All objects need to be initialized this way. Thus, if I'm correct and you haven't initialized your array, then your creation needs to go from this:
NSMutableArray *hiragana = [NSMutableArray alloc];
to this:
NSMutableArray *hiragana = [[NSMutableArray alloc] init];
For optimization reasons, you should probably also specify an initial capacity as well if you have any idea how many objects you might hold:
[[NSMutableArray alloc] initWithCapacity:someNumber];
Another common cause (not in your case, as it turns out, but generally) is forgetting to even allocate the array. If you haven't created an array yet, you're sending that count message to nil, so the result will always be 0.
A few things:
What happens if you put an NSLog call inside of the while loop? Verify that loop iterations are actually happening before blaming it on the array.
Where are you creating the array hiragana? If you are doing it incorrectly for some reason and the array is nil, it might cause problems like this.
If you do not have garbage collection on, be sure to do [htemp release] after adding it to the loop. addObject retains and each added item will leak from the loop. Again, this is only relevant if garbage collection is off.
It's most likely either you aren't created the array correctly or rs doesn't contain what you expect it to contain, and so [rs next] isn't getting called ever (if rs is nil, for example, no iterations of this loop would execute and you wouldn't have any sort of error).