Need a memory management clarification wrt NSArray - objective-c

Say I have:
NSDictionary *stuff; // {"1" => "hi", "2" => "bye"}
NSArray *array = [stuff allKeys];
allKeys makes a copy of stuff's keys, so array is now responsible for releasing this information.
Later on, when I want to
I cannot do:
array = [newStuff allKeys];
because it would just reassign the pointers and orphan the original array. I must first remove the objects
[array removeAllObjects];
Wanted to know if my understand is correct? Thanks!

Not quite.
NSArray *array = [stuff allKeys];
This gives you an array that you don't own. Whether it's technically a copy or not is not your problem. Since the accessor doesn't start with the word "alloc" or "new", or contain the word "copy", you don't own the return value, which means you don't need to release it. (But you do need to retain it if you want to keep it.)
If you later do this:
array = [newStuff allKeys];
that's fine. It stomps on the original reference, as you know, but since you don't own that reference anyways, it's OK to let it go. This new reference is also, of course, not yours unless you retain it.

No. allKeys returns an autoreleased NSArray. It will be released later unless you explicitly retain it. So setting array = [newStuff allKeys]; is perfectly fine. You should probably read this guide on Objective-C.

Related

Efficiency + safety: declare new NSArray var, or overwrite existing NSMutableArray with mutableCopy?

I need to sort a mutable array, but in this specific case when it comes time to sort, I don't need it to be mutable anymore. The sortedArrayUsingSelector: method returns an NSArray * even when called by an NSMutableArray * object. I have 3 options:
1) I can make a mutableCopy of the returned NSArray * and store it in the var I already have
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [[mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] mutableCopy];
2) I can make a new NSArray * var to hold the returned NSArray *
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
NSArray *array = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
3) I can ignore Xcode's warnings and store the returned NSArray * object in an NSMutableArray * var
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I realize that for most cases the difference is negligible, but considering available resources, efficiency, and safety, which would be the overall "best" option?
EDIT: I hadn't considered that the best option might be to create a non-mutable array from the mutable one before sorting it. Not sure if this would be, in fact, the best option, but something I figured I'd mention.
Since you have a mutable array already, just use the following method on NSMutableArray:
- (void)sortUsingSelector:(SEL)comparator
That way you're not recreating anything. This is likely more efficient than creating a new, sorted array from the original and then creating a mutable copy of that. Part of the point of having a mutable array in the first place is that you can change stuff around without needing to recreate it each time, so sorting is a very obvious thing to have here.
The array you get back from sortedArrayUsingSelector won't be a deep copy - it will contain the same pointers as the original array, just in a different order. These array methods in foundation will be well optimized, so I wouldn't worry about it too much.
Instead, just decide what you want. If you want a sorted NSArray * that won't look like an NSMutableArray *, just use sortedArrayUsingSelector, which returns an NSArray *
If you don't need the original unsorted array anymore, just sort the mutable array, like #Gavin suggests.

objective c arrays linked somehow

Hi i have an array which i am trying to copy then remove an element from. The issue is i think i am removing the element from the copy but it also removes the element from the original array. I have no idea what is going on. Im new to objective c so any help would be much appreciated.
NSArray *newarray = appDelegate.orginalArray;
[newarray removeObjectAtIndex: 2];
When i look at the arrays after removing the object it had removed it from both newarray and orginalArray why would this be?
Many thanks
This isn't making a copy. Both newArray and appDelegate.originalArray refer to the same object. You would need to do this:
NSMutableArray *newArray = [appDelegate.originalArray mutableCopy];
Also note that I'm using NSMutableArray and mutableCopy to ensure the copy is mutable (i.e. supports adding and removing items)
Pointers, buddy, and mutable/immutable abstractions. newarray and appDelegate.originalArray are both pointers (references) to the same immutable (unchangeable) array. You want:
NSArray *newarray = [appDelegate.orginalArray mutableCopy];

Can I reuse my pointer after it's been added to a mutable array?

Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!

Release Quickie

How to succinctly handle this situation. I'm not properly releasing contactDictionary in the if statement...
NSNumber *pIDAsNumber;
...
NSMutableDictionary *contactDictionary = [NSMutableDictionary dictionaryWithDictionary:[defaults dictionaryForKey:kContactDictionary]];
if (!contactDictionary) {
contactDictionary = [[NSMutableDictionary alloc] initWithCapacity:1];
}
[contactDictionary setObject:pIDAsNumber forKey:[myClass.personIDAsNumber stringValue]];
[defaults setObject:contactDictionary forKey:kContactDictionary];
Normally, use [NSMutableDictionary dictionaryWithCapacity:1] instead of alloc/init. That will give you an autoreleased dictionary that will, from a memory management perspective, behave identically to the one above. However...
In this specific case, your if clause will never be true (unless you run out of memory, in which case you have bigger problems). -dictionaryWithDictionary: returns an empty dictionary rather than nil if it is passed nil. So, even if -dictionaryForKey: returns nil, -dictionaryWithDictionary: will still create an empty mutable dictionary for you to add to.
You can drop the if statement entirely, because -[NSMutableDictionary dictionaryWithDictionary:] always returns a dictionary.
Also, don't use [NSMutableDictionary dictionaryWithCapacity:1] to get an empty, autoreleased mutable dictionary. Just use [NSMutableDictionary dictionary].

Deallocating NSMutableArray of custom objects

I need help with deallocation of my NSMutableArray of custom objects. I need to retain the array and so I have added a property in .h and I release it in dealloc in .m file. When I add objects to the array, I do the following:
myarray = [[NSMutableArray alloc] init];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj1]];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj2]];
Now, I don't know how to release mycustomobject. If I do the following:
[myarray addObject:[[[mycustomObject alloc]initWithObject:obj1] autorelease]];
I run in to problems when I access the array later. Please advice.
I don't think you understand how memory management in Cocoa works. The array will retain the objects you add to it, and it will release them by itself when the array no longer needs them (such as when you release the array).
In other words, add the autoreleased object to the array, and don't worry about its retain count after that. If you want to remove it from the array simply remove it (using removeObjectAtIndex: or something similiar). If you think you want to release the object without removing it from the array then you are doing something wrong, since that may leave a dangling pointer in your array that will cause you to crash later.
You should really really go over the documentation again, particularly the section on Object Ownership and Disposal.
The proper way to do this is to let the array maintain ownership of the custom object:
NSMutableArray * array = [[NSMutabelArray alloc] init];
for (id obj in anArrayOfObjects) {
mycustomObject * customObj = [[mycustomObject alloc] initWithObject:obj];
[array addObject:customObj];
[customObj release];
}
If you're having difficulties accessing your array later, then you're doing something wrong with the memory management of the array.