Is using copy better than retain in all the Mutable Classes of iPhone SDK? - objective-c

I came to know that, it is better to use copy on #property of NSString than retain due to the problems you can get if you are provided with a NSMutableString. Is the same condition holds true for NSArray , NSDictionary, NSMutableData ?

By problems when retaining I'm assuming you mean that the data may unexpectedly change.
I would consider using copy if the above is unexpected for the circumstances that you are using the string or collection. So you can maintain a known copy of the string, collection or other object that is not going to change without you knowing about it.
However, there may be other reasons to use retain rather than copy on a non-mutable collection or string. You may accept that the contents may change, however use NSArray in the property to show that the object using the container will not modify it itself.
I would think the NSString properties probably want to use copy more so than not. Collections may be different, look at your requirements and see what fits best in your situation.

Related

Objective-c copy a strong property [duplicate]

When not compiling with ARC, it is recommended to use copy properties for data types such as NSString. I could not find proper documentation on the use of copy in ARC mode. Can someone tell me what's applicable for ARC?
It is still recommended to copy because you want to avoid something passing a mutable string and then changing it without you knowing. A copy guarantees that the string you have will not change.
Copying and ARC are orthogonal: you make copies of mutable objects to "freeze" their state; ARC keeps track of object's reference count.
NSString objects may or may not be mutable. When you receive an NSString* as a parameter, you cannot be certain that it is immutable unless you check its type (and even then you may get false positives). If your algorithm relies on the string not changing after being set, making a copy is the right thing to do. ARC, on the other hand, will ensure that the object is not released while you are holding a strong reference to it.
It doesn't matter if you're using ARC or non-ARC.
The reasoning behind the copy is so that you can guarantee that your class' internal state can't be modified from outside the implementation.
This could happen if someone passes you an NSMutableString, and then modifies it later. That consideration is independent of the memory management environment.
copy counts as strong. Use:
#property(nonatomic,copy) NSString *name;
https://devforums.apple.com/message/654033#654033
or even:
#property NSString *firstName;
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html#//apple_ref/doc/uid/TP40011210-CH3-SW7

When to use setter attribute 'copy' in objective-c?

I understand that normally you use copy for NSStrings, so that your property stays as the same value as when you assigned it, even when there's an attempt to re-set it somewhere else.
But I am having hard time completely understanding this concept. Doesn't this apply to basically any kind of object (not just NSStrings)?
So my question is, "What kind of properties should I set as 'copy', and why?"
Objects that are simple bits of data, like strings, that won't have references to a ton of other objects in your application are great for copying.
Now you can, of course, retain things like strings instead. This will work fine. But what if you had a mutable string instead, and you modified it. Now every other object that had a reference to that string will see that modification. This may not be what you want. This is one reason copying is "simpler", because any changes to that data is localized to just that bit of code.
On the other hand, lets say you have a instance of a class you wrote for your app. It has references to other objects in your app, it has a ton of it's own strings or other values in it, and it's a complex beast. Now copying this object may not be a good idea. Chances are that if you modify this object then you want the changes to propogate to every object that holds a reference. And even if you did copy it, do you need a shallow copy (a new instance but it's ivars references the same objects) or a deep copy (a new instance containing containing new copies of every ivar)? And the object in question may not even support <NSCopying>, meaning it can't technically be copied at all.
So to sum up:
copy: Objects that are small, atomic bits of data without any internal references to other objects.
retain: Nearly every other kind of object.
Client code can assign an NSMutableString object to an NSString property. If the property was defined as strong or some other non-copy attribute, then if the client later changes the mutable string, the property's value would now be different. By setting the property to be 'copy', a copy of the string value is made and this ensures the value can't change behind your back.
So basically you should use copy whenever the property is for a type that has a mutable counterpart and you want to ensure the value doesn't change on you.

NSArray property: copy or retain?

According to this: NSString property: copy or retain?
For NSString/NSMutableString, copy is recommended.
How about NSArray/NSMutableArray?
Since you're asking about NSArray (rather than NSMutableArray), you should use copy. NSArray is immutable, so you don't expect a property of that type to change. But NSMutableArray is a subclass of NSArray, so it's perfectly valid for someone to pass in a NSMutableArray. If you just retain that object, then it may change right under your nose. If you copy rather than retain, then the object won't change.
However, you should be aware that when you copy a container like NSArray, you're copying the container only and not its contents. If the array contains mutable objects, the contents of those objects may change even though the array itself is immutable.
choose copy, unless you have a very specific reason not to, as well as all the supporting code/interface to back that up.
i detailed the rationale and several implications here:
NSMutableString as retain/copy
that example is based on NSStrings, but the same applies for NSArrays.
If it is a problem when the underlying data changes, use copy. In fact, this is what you want most of the time, as changing data behind someone's back is a good source for bugs.
Note that copy will essentially just be a retain for an NSArray. Only when you throw an NSMutableArray in, there is more work involved.
From the link you included, it pretty much comes down to this: NSString property: copy or retain?
If you want to make sure the value of the object won't change during execution, you use the copy attribute, otherwise retain will be fine. Generally, retain will be ok for NSMutableArrays and NSArrays (as well as many other objects) as you are (usually) more interested in the object then in the value it contains. In case of an NSString you are always interested in the value, so you copy it to make sure it won't change.
#jlehr:
It depends if the developer is interested in the actual value or not. Whenever interested in the actual value, use copy (since you don't want the value to change during execution), otherwise retain is fine. From Apple's docs:
It is common practice in Objective-C code to copy value objects—objects that represent attributes. C-type variables can usually be substituted for value objects, but value objects have the advantage of encapsulating convenient utilities for common manipulations. For example, NSString objects are used instead of character pointers because they encapsulate encoding and storage.
Also from Apple's docs, on the topic of value objects:
A value object is in essence an object-oriented wrapper for a simple data element such as a string, number, or date. The common value classes in Cocoa are NSString, NSDate, and NSNumber. Value objects are often attributes of other custom objects you create.

What happens when an NSArray element gets deallocated?

Let's suppose I create a few objects and I add them to an array.
House *myCrib = [House house];
House *johnHome = [House house];
House *lisaHome = [House house];
House *whiteHouse = [House house];
NSArray *houses = [NSArray arrayWithObjects: myCrib, johnHome, lisaHome, whiteHouse, nil];
Normally, all House objects have a retain count of two, but they're being autoreleased once. After a while, I decide to release myCrib, even if I'm not the owner — I never retained or initialized.
[myCrib release];
The retain count should drop to zero and my object should be deallocated. My question now is: will this illegal action cause my app to work erroneously or even crash, or will NSArray simply delete my object from its list with bad consequences.
I'm looking for a way to maintain a list of objects, but I want the list to maintain itself. When some object disappears, I want the reference to it to disappear from my array gracefully and automatically. I'm thinking of subclassing or wrapping NSArray.
Thank you.
My question now is: will this illegal
action cause my app to work
erroneously or even crash, or will
NSArray simply delete my object from
its list with bad consequences.
Your array now has an invalid object pointer. There's no way to tell that the pointer is invalid just by looking at it, and the array isn't notified that the object has been deallocated. The problem isn't with the array, after all, the problem is with the code that improperly releases the object. So yes, the application will likely crash or otherwise behave incorrectly due to that bad pointer, and no, NSArray won't detect and deal with the problem for you.
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically.
If the objects in the list are all instances of a common class, you could define your own memory management methods that both retain/release the object and add/remove it from the list, or broadcast appropriate notifications in case there can be multiple lists. I suppose you could even override -retain and -release for this purpose, but I'd think long and hard about that before doing it, and document it well if you do; it's not the sort of thing that other developers would expect.
Another option might be Core Data. If you delete a managed object from the object graph, it'll disappear from any relationships. Strictly speaking, a to-many relationship is a set, not a list, but the difference may not be a concern for your purposes.
Update: I just noticed that you didn't tag your question ios. If you're working under MacOS X, you should definitely take a look at NSPointerArray. If you use garbage collection, NSPointerArray can be configured to use weak references and to replace references to collected objects with null references. This is exactly what you seem to be looking for.
You should not release myCrib if you are not the owner. To do so is a violation of the memory management guidelines and will make your code extremely difficult to maintain. I cannot stress enough that you absolutely should never do this under any sort of circumstance. You're asking for crashes; the array has declared ownership of the object, and you must not subvert that ownership in any way.
So the answer here is: your code is absolutely wrong and you should fix it. If you can't fix it, you should trash it and start over and keep rewriting it until you've come up with another way to achieve the same effect without subverting object ownership. I guarantee that it's possible.
If what you want is a weak-referencing array, then there are a couple ways you can do this (this was just asked a couple of days ago):
NSPointerArray - weakly references its pointers. When you use garbage collection, they're autozeroing (ie, the pointers get removed when the object is deallocated). Unfortunately, this is not available on iOS.
CFMutableArrayRef - you can specify a custom retain and release callback, or just not specify one at all. If you leave them out, the array will simply not retain the objects it contains. However, this does not automatically remove the pointer when the object is deallocated.
DDAutozeroingArray - an NSMutableArray subclass I wrote the other day to provide a weakly-referencing and auto-zeroing array that works on both Mac OS and iOS. However, I strongly encourage you to use this only as a last resort; There are probably much better ways of doing what you're looking for. https://github.com/davedelong/Demos
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically. I'm thinking of
subclassing or wrapping NSArray.
If I have understood right, what you want is an array of weak references. Then, you might be interested in reading this post.
You're asking for a crash here. Your NSArray will still have a reference to the object that now no longer exists -- and who knows what it will be pointing to after a while?
Subclassing NSArray might not be the answer either. It's a class cluster which, in short, means that it's harder to subclass than you might hope.
Not entirely sure how you'd implement this. Something like the element sending a notification when they're about to be deallocated which the array would then pick up. You'd need to be careful that you didn't leak or over-release your objects.
I created a wrapper class — in my code it's called a controller — which maintains the (mutable) array for me. I initialize the controller class in my view controllers — the place where I need them — instead of using an array directly.
No invalid code for me. :-p

Is there any reason not to return a mutable object where one is not expected?

I have a number of functions similar to the following:
+ (NSArray *)arrayOfSomething
{
NSMutableArray *array = [NSMutableArray array];
// Add objects to the array
return [[array copy] autorelease];
}
My question is about the last line of this method: is it better to return the mutable object and avoid a copy operation, or to return an immutable copy? Are there any good reasons to avoid returning a mutable object where one is not expected?
(I know that it is legal to return a NSMutableArray since it is a subclass of NSArray. My question is whether or not this is a good idea.)
This is a complex topic. I think it's best to refer you to Apple's guidelines on object mutability.
Apple has this to say on the subject of using introspection to determine a returned object's mutability:
To determine whether it can change a received object, the receiver must rely on the formal type of the return value. If it receives, for instance, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership
(my emphasis)
The article goes on to give several very good reasons why you should not use introspection on a returned object to determine if you can mutate it e.g.
You read a property list from a file. When the Foundation framework processes the list it notices that various subsets of the property list are identical, so it creates a set of objects that it shares among all those subsets. Afterwards you look at the created property list objects and decide to mutate one subset. Suddenly, and without being aware of it, you’ve changed the tree in multiple places.
and
You ask NSView for its subviews (subviews method) and it returns an object that is declared to be an NSArray but which could be an NSMutableArray internally. Then you pass that array to some other code that, through introspection, determines it to be mutable and changes it. By changing this array, the code is mutating NSView’s internal data structures.
Given the above, it is perfectly acceptable for you to return the mutable array in your example (provided of course, you never mutate it yourself after having returned it, because then you would be breaking the contract).
Having said that, almost nobody has read that section of the Cocoa Objects Guide, so defensive programming would call for you to make an immutable copy and return that unless performance profiling shows that it is a problem to do that.
Short Answer: Don't do it
Long Answer: It depends. If the array is getting changed while being used by someone who expects it be static, you can cause some baffling errors that would be a pain to track down. It would be better to just do the copy/autorelease like you've done and only come back and revisit the return type of that method if it turns out that there is a significant performance hit.
In response to the comments, I think it's unlikely that returning a mutable array would cause any trouble, but, if it does cause trouble, it could be difficult to track down exactly what the issue is. If making a copy of the mutable array turns out to be a big performance hit, it will be very easy to determine what's causing the problem. You have a choice between two very unlikely issues, one that's easy to solve, one that's very difficult.