NSMutableArray becomes empty? - objective-c

I have noticed, that even that my code is working great, something strange happen.
Assume I have two arrays: passwordEntered and tempPasswordEntered.
After I get a new passwordEntered, I do this each time:
tempPasswordEntered=[passwordEntered mutableCopy];
Then I clean :
[passwordEntered removeAllObjects];
then, next time I again do this (for the new passwordEntered):
tempPasswordEntered=[passwordEntered mutableCopy];
So tempPasswordEntered has only the last passwordEntered, and not both of them.
if first time it had 4 places in array,the second time it still has 4 places,
so my question is, does the copy REPLACE the array ? its not added to the last place of it as when you addObject ?
Another thing: should I use retain instead?

The line tempPasswordEntered=[passwordEntered mutableCopy]; is a variable assignment, and like other assignments it changes the value of the variable completely. In this case, tempPasswordEntered now points to a copy of passwordEntered, which has only a single object in it. So yes, it does replace the array, like any other assignment would.
If you wanted to add the objects in passwordEntered to tempPasswordEntered, try [tempPasswordEntered addObjectsFromArray:passwordEntered].
It sounds like what you want is mutableCopy, not retain, but I don't really know what your requirements are exactly so I can't say much more. You should probably be using ARC anyways :)

NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];
The latter two are simple leaks. The first is one way of making a copy, but retVal = [fetchResults copy]; is a better way to make a copy.
But, of course, you don't need a copy at all. That isn't the problem. You go on to say that the only thing that doesn't crash is an empty result set.
That indicates one of two things; either your result set is corrupt (unlikely) or you are accessing the result set incorrectly (likely).

Related

Incompatible pointer type assigning "NSMutableArray" to "NSArray"?

This is my code
NSMutableArray * reversedNamesArray; //theres about a thousand variable already stored in here, this is just for documentation
reversedNamesArray = [reversedNamesArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
//Incompatible pointer type assigning "NSMutableArray" to "NSArray"
The message above is what i get, i know what it means, but i don't want to take an extra step of copying an NSMutableArray of a thousand variable to a NSArray is there any cast i can use to fix the warning, it doesn't affect my code, but i just want a fix for it. and can you explain why they are not compatible, they NSMutableArray and NSArray should use the same amount of bytes so i don't see why they are incompatible in the first place.
-sortedArrayUsingSelector: is implemented in NSArray and returns an NSArray even when called on an NSMutableArray.
You should use one of the sort methods implemented in NSMutableArray. This would work:
[reversedNamesArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
#GerdK's answer is the right one, but I thought I would explain why you cannot just cast an NSArray into NSMutableArray. If you could, major optimizations would be lost. Consider the following:
NSArray *first = #[...];
NSArray *second = [first copy];
This is extremely cheap. second just adds an extra retain onto first and we're done. On the other hand:
NSMutableArray *first = [NSMutableArray arrayWith…]
NSArray *second = [first copy];
This is more expensive. In order to create second, we actually have to create new array and copy the pointers over and add extra retains.
Now, imagine what you're requesting were legal:
// Not legal, but let's say it were
NSArray *first = #[...];
NSMutableArray *second = (NSMutableArray *)[first copy];
Now [NSArray copy] has to be defensive against this. It has to implement copy as a full (expensive) copy. That's a real performance loss.
You might say "But I'll just use copy when I want to really copy and retain when I want to retain." Sure, but that's not good enough. Say I want to store an immutable copy of something I'm passed:
- (void)setSomething:(NSArray *)array {
_something = [array copy];
}
This is a very efficient and correct setter. If I pass a real NSArray, then it's cheap (and this is probably the normal case). If I pass an NSMutableArray (it's a subclass, so I can do that), then I automatically make a real copy (so the object can't change behind my back). You get this kind of optimization for free by keeping mutable and immutable objects separate. Otherwise, I'd have to interrogate the object and ask if it were mutable and then make decisions based on that.

why do you make temporary objects, set them to variables, then release them?

I often see something like:
NSArray *tmpArr = [[NSArray alloc] initWithObjects:#"Info", nil];
self.userInfo = tmpArr;
[tmpArr release];
instead of:
self.userInfo = [[NSArray alloc] initWithObjects:#"Info", nil];
Does anyone know why the top code sample is more popular? Is it more correct memory management than the second?
Second code snippet causes a memory leak due to the array not being released. In most cases properties of object types (like NSArray in this case) are either retain or copy properties and this means they either increase the reference count of the assigned value or copy the whole object. Then the local variable can be (and should be) released if it's not needed anymore.
Non-leaking alternative to the second code snipped would be using autorelease:
self.userInfo = [[[NSArray alloc] initWithObjects:#"Info", nil] autorelease];
or simply:
self.userInfo = [NSArray arrayWithObjects:#"Info", nil];
Assuming that the property userInfo is marked retain, the second form will leak memory. [[NSArray alloc] initWithObjects] will create an array with a reference count of one. Assigning it to a retain property will increase the reference count to two and it will never come back down to zero and be released. It can be fixed either by using the first form you listed or by:
self.userInfo = [[[NSArray alloc] initWithObjects:#"Info", nil] autorelease];
so that the auto release will decrement the reference count to one at the next iteration of the run loop. From then when userInfo is cleared, the reference count will go down to zero and the array will be destroyed.
You should also take a look at this question
Apart from any other reasons there might be, it makes the code more readable and helps to prevent errors.
Your two examples are not equivalent, because you forgot to release the newly alloc/init'ed array in the second one. You would have needed
self.userInfo = [[[NSArray alloc] initWithObjects:#"Info", nil] autorelease];
here.
QED first reason ;-P
Moreover, when you create a local variable first, you can build up more complex objects before publicizing them via a property. If, for example, you were using a mutable array here and filled it with some more complex logic, assigning it to the property right away and only when going on filling it up, clients of your class might access the property with its contents being only half ready – a great provision for sporadic and hard to reproduce bugs.
So even though in your case it would not have been strictly necessary to use a local variable (if you either had autorelease'd it our used the new Automatic Reference Couting "ARC", which would have solved the leak issue automatically), in my opinion it is always a good idea to first get everything ready and then make it visible.
Clean code rules :)

Quick NSMutableArray Question

I was wondering, would using a NSMutableArray be the best way for making an array that i will be adding objects to? Or, just a regular NSArray? secondly, I'm trying to make something sort of like an ArrayList in java (so there is no limit to the size), and I would like to know how to do that. What I've thought of is to make a bigger array and copy older array into it. My code:
- (void) addAccount:(BankAccount *)b
{
accountCount = [NSNumber numberWithDouble:[accountCount doubleValue] + 1];
NSMutableArray *oldList = accounts;
accounts = [[NSMutableArray alloc] (some code to make bigger and copy over)];
}
P.S. I taught myself this language yesterday, so I may not understand you response if it's too advanced
NSMutableArrays are what you want. Also, NSMutableArrays are already like ArrayLists or STL vectors, or anything else with "no limit to the size". You can say [myArray addObject:someObject]; until you run out of memory, and it will just keep resizing itself as needed.
The difference between an NSMutableArray and an NSArray lies in the meaning of the word "mutable". i.e.: A mutable array can be modified after it's created whereas a "normal" NSArray is immutable and can't be modified after it's created.
As such, using an NSMutableArray and adding objects to it via the addObject: method would seem an ideal solution.
If you want to be adding objects all at once use NSArray. If you're going to be adding some objects now, then more later, use NSMutableArray.
Your code snippet doesn't make much sense. To make an NSMutableArray, do this:
NSMutableArray *array = [NSMutableArray array];
If you don’t need an order (normally you don’t), use a NSSet/NSMutableSet.

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).

Pass NSMutableArray object

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.