On some intuitive (perhaps wrong) idea of performance, I always get a copy of a mutable instance before I store it. So if a property expects an NSArray I take the mutable array I'm working with and store it as self.array = mutableArray.copy (though the property is specified as strong or retain).
This seems silly to me, suddenly, but is it? Do mutable instances -- doing the exact same task -- perform the same?
Note: The mutable instance falls out of scope and (thanks to ARC) gets released right after this, so there's no worry that it'll be mutated once it's assigned to the property.
NSArray and NSMutableArray are both (as far as I'm aware) implemented on top of CFArray, which simply has a flag specifying whether it's mutable. CFArray functions which require a mutable array have an assertion right at the beginning, checking that flag:
void CFArraySetValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *value) {
// snip...
CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__);
Mutable and immutable CFArrays are identical other than passing or failing this assertion, and so should NSArrays and NSMutableArrays be, performance- or other-wise.
Partly answered here: NSArray size and mutability
NSMutableArray is not noticeably slower or larger (memory-wise) than an NSArray. It's basically just an NSArray that reallocates itself when it gets full as as bigger array, and keeps doing that as you add items to it.
The reason for copying mutable arrays as immutable ones when assigning them to values in your class is so you can guarantee that their values don't change. If you store a mutable array in your class, other code can change its values outside of your class without calling any of your methods. That leaves you vulnerable to crashes due to internal inconstancy errors within your classes.
For example, supposing that when the array was set, you cached the length of the array as an int property in your class. That would be fine if the array was immutable, but if it was mutable, someone else could change the array, and your cached value would now be wrong, but you have no way of knowing that.
However, it's not necessary to do the copying manually. If you declare your array properties as:
#property (nonatomic, copy) NSArray *foo;
Then whenever you assign an array to object.foo, it will automatically be copied. You don't need to copy it again yourself. It's best practice to use a property type of copy instead of strong/retain for any type that has a mutable variant, like so:
#property (nonatomic, copy) NSArray *foo;
#property (nonatomic, copy) NSString *foo;
#property (nonatomic, copy) NSDictionary *foo;
#property (nonatomic, copy) NSData *foo;
etc...
However be careful not to use it for mutable properties, or it will make an immutable copy stored in a property that thinks it's mutable and cause a crash if you try to mutate it. The synthesised copy property isn't smart enough to use mutableCopy automatically.
#property (nonatomic, copy) NSMutableArray *foo; //don't do this
For clarity you're asking if, given an NSArray and an NSMutableArray both subjected to a battery of non-mutating test methods, does the NSArray perform noticeably faster? I specify non-mutataing, because it looks like you're copying a mutable array to an immutable array with the belief that the immutable array will perform its non-mutating methods faster than the mutable array. Anyways, the answer is no. (But don't take my word for it; profile).
Even if NSMutableArray overrode some non-mutating methods (which we can't know about, one way or another), you wouldn't need to worry about it. Adding a couple CPU cycles is trivial compared to the overall computational complexity of the operation. As long as NSMutableArray doesn't manage to turn a O(n) lookup-operation into a O(n2) operation, you'll be fine 99% of the time. (Those complexities are just fictitious examples).
While there are perfectly valid reasons why you might want to copy a mutable array into an immutable array (as pointed out by #NickLockwood), performance shouldn't be one of them. Premature optimization is very bad, after all.
Related
My apologies if this has already been asked and answered. I am new to Objective-C and I am trying to create a project that contains an object with NSMutableString and I am creating an NSMutableArray of those objects.
Each of the strings in the object are declared as follows:
#property (assign) NSMutableString* propname;
In the initialization routine (initStringObject) for the object I am setting each of the strings as follows:
self.propname = [NSMutableString stringWithCapacity:16];
[self.propname setString:#"Name"];
There are a number of properties with multiple NSMutableStrings, a couple of NSInteger and a float. All NSMutableStrings are allocated with different capacities.
For the NSMutableArray, I am declaring that in the interface section of the view controller .m file as follows:
#property (strong) NSMutableArray *objectarray;
In the loadview, routine I am initializing the array as follows:
self.objectarray = [NSMutableArray array];
[self.objectarray addObject:[[StringObject alloc] initStringObject];
The project builds fine but when I go to populate the view with the information in the object, the strings are corrupted. The integer and float values in the object are correct. I figure I must be losing the pointer to the correct location in memory but I cannot figure out what is going on. Eventually, if I keep running the program, I get an EXC_BAD_ACCESS error message.
This is an OS X application.
Can anyone see what I am doing wrong?
Thank you in advance.
I suspect it's the assign attribute in #property (assign). Use strong (the default) instead to have ARC properly manage the string objects.
why would you use #property with NSArray?
The reason for my question is because I was under the impression that #properties were mainly used to take advantage of the getter and setter you get for free when you use #property, but when you assign an NSArray to a property you know you will never use the getter and setter to access the objects in the array, why use properties on this case. Does this have to do with ARC, in other words is this considered a better memory management practice?
You use a property to get or set the whole array. You also use the property to access individual elements of the array.
Using a property with an array has nothing to do with ARC or MRC. Properties are a way to encapsulate data. That's it.
If you don't wish to provide access to the whole array then don't use a property. Add method(s) to set/get individual elements of the internal array if that is appropriate.
Along with what already said by other answers, I usually like to copy my array for security purposes.
If you're accepting an NSArray by the client, she could potentially provide you a NSMutableArray instance. If that's the case and you want to ensure that the array you're working with doesn't change unexpectedly, you'd better copy it as opposed to retain it.
Properties come in handy in this case since you can declare
#property (nonatomic, copy) NSArray * myArray;
and be sure that you're the only owner of that array after you assigned it.
I typically use this strategy for any class with mutable subclasses, such as NSString, NSArray, NSSet and so on, whenever I care about the ownership of the data.
The downside of this strategy is of course memory efficiency, but in the end engineering is the art of intelligent compromise.
To use the array outside of a single method or class. ClassA.array = ClassB.array; or to simply read from the array in a different method of the same class since the array will dealloc immediately after execution.
but when you assign an NSArray to a property you know you will never
use the getter and setter to access the objects in the array, why use
properties on this case.
For the same reasons that you use properties for other objects:
accessors: The accessors are for the array itself, not objects in the array. It's not uncommon to get or set an entire array at once:
NSArray *kids = frank.children; // get the array
mary.children = #[#"Nick", #"Sam", #"Susan"]; // set the array
abstraction: Using properties instead of accessing the underlying array directly makes it easier to change the implementation later, should that become necessary.
KVC compliance: You get it for free with properties. With collections like arrays, KVC gives you a lot of power. You can use collection operators like #avg and #max as well as indexed accessors:
int employees = [accounting countOfEmployees]; // same as [accounting.employees count]
Employee *thirdEmployee = [engineering objectInEmployeesAtIndex:2]; // same as [engineering.employees objectAtIndex:2]
In the following example, does stringWithString:(NSString *) copy the memory address/location of theName to name or it actually copies the data from theName to name?
#interface AddressCard:NSObject
-(void)setName:(NSString *)theName;
#end
#implementation AddressCard
NSString *name;
-(void)setName:(NSString *)theName
{
if(name!=theName)
name = [NSString stringWithString:theName];
}
#end
If I change the code to following, what does copy do differently?
#interface AddressCard:NSObject
#property (copy, nonatomic) NSString *name;
#end
#implementation AddressCard
#synthesize name;
#end
In general, does copy (#property attribute) copy the address of the data or copies the data from one variable to another? If it is latter case, are we not consuming a lot of memory when the variable represents large data?
Thank you for your time and response!
+[NSString stringWithString:] will effectively 'copy' the string.
In general, does copy (#property attribute) copy the address of the data or copies the data from one variable to another?
It performs whatever the object considers is a copy. It may return a new object, or it may return itself. For example, +[NSString stringWithString:]could just return the parameter retained and autoreleased if the parameter is already immutable. If the parameter is mutable, then it will return a new instance, so you are guaranteed to have an immutable instance.
If it is latter case, are we not consuming a lot of memory when the variable represents large data?
Aha - but that's the trick! Yes, you could end up making many new allocations with copy, but the trick is often that copies of reference counted objects are truly very shallow in most cases when you favor immutable types and using copy. Many collections types can simply return themselves if they are already immutable, or their ivars may do so, so it's actually a really good idea to ensure you are not passing around mutable objects -- so creating an immutable copy early really allows this optimization to propagate, and saves you a ton of allocations (but not always -- there are a number of corner cases for all these variants).
Note: Not all classes distinguish immutability from mutability, so a copy does not always return an immutable object.
stringWithString will create a copy if it's mutable. But be aware since it's not alloc, init, copy method it's autoreleased. The copy you are now holding will go poof at some point right after that set method exits. If you did initWithString instead, it would create another string as well but retain it.
The copy attribute means the property will be assigned the object returned after sending the copy message to the object that was passed in. That means it's up to that object type to determine how it handles a copy. For your specific string example, (copy) will create a copy of the string back to the caller - a copy that's retained. It's up to the caller to release the retained object. According to the memory guidelines, copy will retain the object.
I've noticed that I cannot use the #property / #synthesize for member vars that are arrays in obj-c. For instance the member var int mVar[5] cannot use the #property/#synthesize.
However, I've noticed that I can set these vars simply by not using self.mVar[n] but instead using mVar[n].
Can someone explain why this works, if this is good or terrible practice, and what alternative I should use if it is not good practice?
Properties are syntactic sugar for set/get-style methods. Passing arrays in as parameters and out as return values via these methods is fraught with semantic and performance problems, so they probably just put them in the too-hard basket and deliberately excluded them.
As regular data members, arrays don't exhibit these difficulties because you are accessing them directly rather than copying them in and out via methods.
If you want to make the contents of an array accessible as a property (which you should only need to do if you want to make the contents public), you can expose them as:
#property (readonly) int *vars;
#property (readonly) int numVars;
Or you could do the Objective-C thing:
#property (nonatomic, retain) NSArray *vars;
But then you would have to create lots of NSNumber objects (ick).
I really need some clarification — I have a few questions and I'm all mixed up right now.
Here is a simple class interface:
#import <UIKit/UIKit.h>
#interface Car : NSObject{
NSInteger carID;
NSString *carName;
}
#property (nonatomic, assign) NSInteger carID;
#property (nonatomic, copy) NSString * carName;
#end
Why is carID not declared as a pointer?
Why does it use "assign" for carID instead of "copy"?
Why even declare class members as pointers in the first place? (In my main program, my Car object will be used as a pointer.)
NSInteger is simply a typedef for a primitive type (int on 32-bit, long on 64-bit) — it is not an object, and can as such not be retained or copied.
Class members are always pointers; you never pass the "real" objects around; as that would be, at best, unmanageable.
Edit: To expand on the last paragraph: Objective-C class instances always exist on the heap, never on the stack; this is to facilitate things like reference counting and self-managed object life cycle.
This also means that it's very hard to accidentally copy an object; but on the flip side it can be somewhat easier to accidentally dispose of an object you still need. Still, the latter is more readily debugged (as it causes a nice, big crash (at best, anyway)) than the last (which at worst causes a slow leak).
The property for carID is not really correct. For types that are not pointers, the correct definition looks like:
#property (nonatomic) NSInteger carID;
It's always going to be copying a value anyway, but "copy" has a very different meaning in properties - for objects it's going to call [object copy] when that property is used to set a new value.
Or you could drop off the nonatomic, but then the property is more expensive to call (by some small amount). Just leave in the nonatomic unless you have a good reason not to.
Thanks guys!
So in Objective-C , you have int and Pointer Int.
How do you declare these in objective C
-int being a regular int.
-Pointer Int being an object representation of an integer. Since it is an object, it can also point to pointers*. Right?
And Pointer Int pointers can point to pointers of any type If I wanted to. Right?
It will cause a crash if it doesn't point to a Pointer int. But it will compile successfully, Right?
But in what scenarios would I prefer using a regular int to a Pointer Int?
I would like to add some clarification why you would want to use:
#property (nonatomic, copy) NSString * carName;
instead of
#property (nonatomic, retain) NSString * carName;
The copy keyword implies language semantics that you want to have a COPY of the NSString passed into your current object reference. So the pointer does not change (that is why you don't have to release the object ref).
The retain keyword makes it so that you get the pointer which will be retained because the pointer reference changes for this data member (and the current one will be released). Copying a NSString might not be a considerably heavy operation, so copying NSString is used often. You have to be careful what type of property you declare as copy. There might be a considerable amount of effort to produce a copy of types like Dictionaries etc (see shallow, deep copy etc).
Hope that helps!