Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Mutable classes "heavier?"

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.

like image 835
Dan Rosenstark Avatar asked Feb 22 '23 14:02

Dan Rosenstark


2 Answers

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.

like image 57
jscs Avatar answered Mar 05 '23 13:03

jscs


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
like image 22
Nick Lockwood Avatar answered Mar 05 '23 13:03

Nick Lockwood