what's property copy means in Cocoa's Framework?(like UITabBar's items property) - objective-c

In UITabBar.h, a propery signed copy
#property(nonatomic,copy) NSArray *items; // get/set visible
It's a array
And what "copy" means?
copy NSArray container obj?
copy every obj NSArray contains?
or something.
so there's a test
UITabBar* testBar = [[UITabBar alloc] init];
UITabBarItem* item = [[UITabBarItem alloc] init];
NSArray* array = [[NSArray alloc] initWithObjects:item, nil];
NSLog(#"bar:%p,%d", testBar, testBar.retainCount);
NSLog(#"item:%p,%d", item, item.retainCount);
NSLog(#"array:%p,%d", array, array.retainCount);
testBar.items = array;
NSLog(#"that item:%p,%d", [testBar.items lastObject], [[testBar.items lastObject] retainCount]);
NSLog(#"testBar.items:%p,%d", testBar.items, testBar.items.retainCount);
result
bar:0x96a9750,1
item:0x96aa230,2
array:0x96aa280,1
that item:0x96aa230,2
testBar.items:0x96aa280,6
why neither container array nor obj in array has been "copied"?

Two things:
collection -copy is always shallow. It doesn't copy the collections elements (In fact, nothing guarantees that these elements are even copyable – i.e. are conforming to NSCopying protocol). This explains why obj is not copied – it doesn't get any extra retain.
Foundation tries to optimizes its implementation of -copy to -retain whenever is possible. For example, -[NSString copy] is a retain for immutable strings. Since collection copies are shallow, the same optimization works for immutable collections. That's why array is not copied but just retained.

The reason the copy has not been made in this case is that NSArray is immutable. You do not need to make a copy of it to guard against changes to the array, because such changes cannot be made; it is sufficient to retain the same immutable array.
If you try this experiment with NSMutableArray, you will get a different result.

Related

mutable copy copies by reference, not value?

Apparently mutableCopy copies by reference, not value. Ie if I do this:
NSMutableArray arrayA = [arrayB mutableCopy];
then change values of arrayB, then arrayA's values will also be changed.
I think Java has a clone() method to copy by value.. is there an equivalent in objective c?
The mutableCopy method performs “shallow” copy. Each element of arrayA is a reference to an object that is also in arrayB. If you add elements to arrayA (or remove elements), arrayB will be unchanged, and vice versa. But since the elements of arrayA and arrayB reference the same objects, a change to one of those objects “shows up” in both arrays.
If you want a one-level deep copy of arrayB, you can do this:
NSMutableArray *arrayA = [[NSMutableArray alloc] initWithArray:arrayB copyItems:YES];
That will have this effect:
NSMutableArray *arrayA = [[NSMutableArray alloc] init];
for (id element in arrayB) {
[arrayA addObject:[element copy]]; //copies immutable objects to new array
}
To deep copy an array you need to use:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray: oldArray copyItems:YES];
This performs a copyWithZone: on each object in the array
The regular [NSMutableArray copy] method will, as per Apple's documentation, return a "functionally independent object with values identical to the original at the time the copy was made." You should probably just use that.
To be totally sure that it is mutable, use [[NSMutableArray alloc] initWithArray:[otherArray copy]].

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!

Returning an NSArray without Leaking?

I have been struggling with the best pattern for returning an array from a static method.
In my static method getList (in the BIUtility Class), I am allocating an NSArray to return. in the return line, I do:
return [array autorelease];
Then in the calling method, I am allocating an array like this:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
Later I release the list using:
[list release];
I think this is causing a memory leak as the retain is increasing the retain count one too many. However, if I do not do the retain, I get a Bad_Exec because it has already freed the class.
I feel like I am overthinking this and there must be a typical pattern. I have been looking all over the place and I cannot find a "best practice".
I appreciate your help.
You should replace:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
With:
NSArray * list = [[BIUtility getList] retain];
This is because getList actually returns a pointer to the NSArray.
If it were a mutable array, however, you should say [[BIUtility getList] copy]; so that you don't accidentally mutate an array that another object has a reference to.
If you are curious, you were getting a memory leak because your original statement increments two counters, while you only release one later.
These parts of the statement increase counts:
[anObject]] retain]
[anClassname alloc]
[anObject copy] will also create an object with a count of 1.

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.

Assigning values to Instance variables in Objective C

The function I'm looking at:
-(void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"statedictionary" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
NSArray *components = [self.stateZips allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.States = sorted;
NSString *selectedState = [self.states objectAtIndex:0];
NSArray *array = [stateZips objectForKey: selectedState];
self.zips = array;
}
Why is an NSDictionary allocated, then assigned to a pointer called *dictionary, and then assigned to the instance variable stateZips? Why not allocate it and assign it directly to the instance variable and save memory of creating and releasing another NSDictionary? The same methodology is always followed, including later in this function with the NSArray...
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.statesZips = dictionary;
[dictionary release];
Also, this sorting puts the keys from a hash table (dictionary) in alphabetical order. I'm not sure I understand this line:
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
No one seems to have addressed the fact that the line
self.statesZips = dictionary;
is not directly an instance variable assignment. stateZips is a property, and so that line of code calls the setStateZips: method. That method retains or copies the dictionary, so unless the viewDidLoad method intends to use it again for some purpose, it's not needed any longer. That makes it OK to release it.
The previous line:
[[NSDictionary alloc] initWithContentsOfFile:plistPath];
allocates an object. That makes it your responsibility to release it once you don't need it any more. After assigning it to the statesZips property, it's no longer needed, so it's released and you shouldn't use dictionary any more. You'll notice that later code only refers to self.stateZips, not dictionary.
In the case of the NSArray later in the method, viewDidLoad does not allocate the object, so that method is not responsible for calling release on it. The rule of thumb is that if you alloc it, you're responsible for making sure it gets released. Otherwise, it's not your problem.
Sorting the array uses the sortedArrayUsingSelector: method. A selector identifies a method in Objective-C. And the #selector is the literal syntax for selectors (kind of like how #"" is the literal syntax for NSString objects). So, what that code says, is "give me an array where the objects in components are sorted, and use the compare: method to compare each object when you do the sort. When it sorts the array, it will call compare: on the objects in the array to determine how to put them in order.
The statesZips property is probably retained, that's the reasoning.
When the NSDictionary is first allocated, its retain count is 1. When it's assigned to statesZips, the retain count becomes 2. When it's released, the retain count drops to 1, which is usually the desired outcome.
Note that the code below would have produced (almost) the same result:
self.statesZips = [NSDictionary dictionaryWithContentsOfFile:plistPath];
because dictionaryWithContentsOfFile returns an autoreleased object.
As a convention, class methods like [NSDictionary dictionary] return autoreleased objects (which automatically get released after some time), while the usual alloc-init method (as in [[NSDictionary alloc] init]) return retained objects.
I suggest you read the Memory Management Programming Guide for Cocoa for further information.
EDIT: I must have missed the last part of your question when I first read it, but Barry has already answered that part.
This code uses reference-counted memory management (not the automatic garbage collection memory management available in Objective-C 2.0 on OS X). When any object (in this case, the NSDictionary and the NSArray) are alloc'd, the caller is responsible for calling -release on that instance. Failing to call release causes a memory leak. The code could have been written as
self.statesZips = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] autorelease];
but at the expense of less explicit memory management (relying on NSAutoreleasePool to release the alloc'd instance at the end of the event loop iteration.
the call
[components sortedArrayUsingSelector:#selector(compare:)];
returns an array of whose elements come from components but according to the return value of calling [elem1 compare:elem2] to compare two array elements.