Worth converting NSMutable<T> to NS<T> - objective-c

I know the basics of memory management, but not the internals of how iOS handles the different datatypes. Something always nags me that it's best to use an NSData vs NSMutableData or NSString rather than an NSMutableString. Is there really any performance difference unless the code's going to be running in a 10k loop or am I wasting my time?

Use the mutable types where you want to be able to change the contents in place. Use the immutable types otherwise.
In some cases you don't have a choice - such as the data object that holds the returned data from an NSURLRequest: Using a mutable data object is a lot easier than creating a lot of immutable data types and joining them up at the end.
After that, run your code through the profiler and see whether you should do anything differently. i.e. whether the overhead of creating new immutable types to hold objects is more efficient than using a mutable type. Anything else is a premature optimisation.

Like you said its only worth if you call use Mutable many times. But if you call it only a few times this isnt any Problem.
You should think about alternatives, e.g. in a larger TableViews cellForRowAtIndexPath: method. Use [NSString stringWithFormat:#"%#%#", fristString, secondString], Instead of *foo = [[NSMutableString alloc] init] and [foo append:firstString] and so on.

Related

Performance of mutable versus immutable objects

I would like to ask all the Cocoa veterans out there - is there any difference in performance between using mutable versus immutable objects in cases like:
NSString's stringByAppendingString: versus NSMutableString's appendString:
NSArray's arrayByAddingObject: versus NSMutableArray's addObject:
...
Thank you and happy coding!
This question is hard to answer : NSArray and NSString aren't actual implementations, they are class-clusters and so are NSMutableArray and NSMutableString. The true implementations underneath can't be determined and thus performances would be hard to compare.
You probably won't find a definite answer to that one.
But what I would guess is : stringByAppendingString and arrayByAddingObject create new objects which contains the modifications, ie copy the current items to a new place in memory, NSMutableArray and NSMutableString should have better performances because they are built to prevent copying when possible (not actually true because NSMutableArray might recopy memory when elements are added but not every time).
I think you should trust the CoreFoundation coders on this one : you wan't to mutate objects ? Use the mutables one.

objective c "Did you forget to nest alloc and init?"

I am just starting climbing the Objective C learning curve (using Nerd Ranch iOS programming book).
Based on what I have know from other languages about "nesting" multiple executions within one line I assumed that I can alter:
NSString* descriptionString = [[NSString alloc] initWithFormat:#"%#", possesionName]
with a two line version:
NSString* descriptionString = [NSString alloc];
[descriptionString initWithFormat:#"%#", possesionName]
but it seems that the second attempt raises an exception
2012-01-22 18:25:09.753 RandomPossessions[4183:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -length only defined for abstract class. Define -[NSPlaceholderString length]!'
Could someone help me understand what exactly I am doing wrong here? Thanks a lot in advance.
PS. If this is a way Objective C messages work and you have to make alloc and init in one line just let me know - I assumed this is just a set of functions that either can be executed two in one go or one after another.
An important difference between both versions (they are not exactly equal) is that in the first version you use the result of initWithFormat for the variable descriptionString, while you use the result of alloc in the second. If you change your code to
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName]
all should be well again. It is specified that an object returned by alloc shall not be seen as initialized and functional until some init Method has been called and init might return something else.
The alloc method will allocate memory for a new object. But the init method might throw away that memory and return a completely different object. Or it might return nil. This is why you must always do self = [super init] when you override an init method.
NSString is one class that does this kind of thing all the time.
I'm not exactly sure why the exception is happening, but I believe it could be ARC injecting code in between your two lines of code or something similar. Whatever it is, something is trying to act on the allocated object that has never been initialised, and this is a huge problem that can lead to all kinds of issues. Consider yourself lucky it threw an exception, sometimes it wont.
The NSString class might not actually be a real class. It may contain almost no methods and almost no variables. All it has is a bunch of factory methods to create "real" string objects of some other class, and this is done using methods like initWithFormat:. So, by long standing convention alloc/init must always be done in a single statement and there are a handful of places where, usually for performance reasons, something will rely on this convention being used.
Basically, objective-c is a language where you don't need to know exactly what is going on inside an object. You just need to know what messages can be sent to an object, and how it will respond. Anything else is undefined behaviour and even if you learn how it works, it is subject to change without notice. Sometimes the behaviour will change depending on circumstances that are completely illogical, for example you might expect the "copy" method to give you a copy of the object you send it to, and while this is the default behaviour, there are many cases where it will actually just return the same object with slightly different memory management flags. This is because the internal logic of the class knows that returning the same object is much faster and effectively identical to returning an actual copy.
My understanding is copy sent to NSString may return a new object, or it may return itself. It depends on which NSString subclass is actually being used, and there isn't even any documentation for what subclasses exist, let alone how they're implemented. All you need to know, is that copy will return a pointer to an object that is perfectly safe to treat as if it was a copy even though it might not be.
In a "proper" object oriented language like Objective-C, objects are "black boxes" which can intelligently change their internal behaviour at any time for any reason, but their external behaviour always remains the same.
With regard to avoiding nesting... The coding style for Objective-C often does require extensive nesting, or else you'll be writing 10 lines of code when only 1 is really needed. The square brace syntax is particularly suited to nesting without making your code messy.
As a rule of thumb, I turn on Xcode's "Page Guide at column" feature, and set it to 120 characters. If the line of code exceeds that width then I'll think about breaking it into multiple lines. But often it's cleaner to have a really long line than three short lines.
Be pragmatic about it. :)
From Apple's library reference, initWithFormat:
Returns an NSString object initialized by converting given data into Unicode characters using a given encoding.
So you can use these two lines of code:
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName];
For more info please go to:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/initWithFormat:

Performance of factory methods in Objective-C

Is there any difference performance-wise in the following 2 snippets of code?
NSString* str = [[NSString alloc] initWithFormat:#"%i", 10];
// Do something with |str|.
[str release];
NSAutorelasePool* pool = [[NSAutreleasePool alloc] init];
NSString* str = [NSString stringWithFormat:#"%i", 10];
// Do something with |str|.
[pool drain];
I see people trying to suggest using factory methods whenever possible.
Isn't it better to release objects as soon as possible rather than when a pool is drained.
I would see the first type being very efficient in certain cases such as in tight-loops.
I see people trying to suggest using
factory methods possible.
I'd guess that's because it's basically the same thing, but you don't have to remember to release the object. Some might say that using the convenience method is a little more readable, too, especially since you don't have to end every method with a sequence of -release messages.
Isn't it better to release objects as
soon as possible rather than when a
pool is drained.
You can make that case in some situations, such as inside a loop. That's often not an important consideration, though... many methods don't loop at all and only create a handful of objects.
I would see the first type being very
efficient in certain cases such as in
tight-loops.
Sure. So you should know when it is and when it isn't appropriate to autorelease objects, and you should write your code accordingly. But it doesn't follow that you should always try to avoid autoreleasing objects any more than it makes sense to always try to use convenience methods.
BTW, if you're writing loops that iterate many times, you should consider creating an autorelease pool. Chances are, you'll be using other methods inside your loop, and those methods might create autoreleased objects. Using your own pool and draining it periodically prevents those objects from piling up. If you do that, though, it takes a great deal of wind out of the the idea that you shouldn't use autoreleased objects in your loop.
Yes it is. First one is better memory management, worth it if you are doing it, like you said, in a loop to avoid allocating a lot before the next pool drain.

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.

Cocoa/Objective-C: how much optimization should I do myself?

I recently found myself writing a piece of code that executed a Core Data fetch, then allocated two mutable arrays with the initial capacity being equal to the number of results returned from the fetch:
// Have some existing context, request, and error objects
NSArray *results = [context executeFetchRequest:request error:&error];
NSMutableArray *firstArray = [[[NSMutableArray alloc]
initWithCapacity:[results count]] autorelease];
NSMutableArray *secondArray = [[[NSMutableArray alloc]
initWithCapacity:[results count]] autorelease];
I looked this over again once I'd written it, and something struck me as odd: I was calling [results count] twice. The resultset is potentially pretty large (hundreds, maybe a thousand, objects).
My first instict was to break out [results count] into a separate NSUInteger, then use that integer for the capacity of each of the arrays. My question: is this kind of by-hand optimization necessary? Will the compiler recognize it's running [results count] twice and just hold the value without me having to explicitly specify that behavior? Or will it run the method twice - a potentially costly operation?
In a similar vein, what other optimizations should programmers (especially iPhone programmers, where there's a limited amount of memory/processing power available) do by hand, as opposed to trusting the compiler?
To answer the actual question: no, an Objective-C compiler cannot optimize away method sends, ever. (Well, actually, there’s one possible case: if it knows for certain that the receiver is nil.)
There is no way for a class to provide guarantees about method behaviour (in particular, you can’t use gcc’s __attribute__((const)) and such with a method), and there is no way for the compiler to tell what method implementation will be called because of Objective-C’s dynamic nature. For example, results could actually be a proxy object which forwards the count method to a random object each time it’s called. There’s no particular reason for Core Data to do that, but the compiler doesn’t know that.
All that aside, the cost of calling -[NSArray count] is trivial, and there’s absolutely no way that particular method will be a bottleneck in anything but extremely contrived code. The habit of avoiding double calls can reasonably be argued to be worthwhile, but actually worrying about the cost or going back to “correct” it for performance reasons would be a waste of time, quite likely more time than your program will spend calling -[NSArray count] throughout its useful lifetime.
First rule of optimization: never optimize without benchmarks verifying what needs optimizing. Otherwise you're suffering from 'premature optimization', and a lot of (potentially) wasted effort.
Now, if you know that something takes 2s to run,and you're running in 4 times when you could be running it once - that would be justifiable. but it sounds like you're not sure in this case, so the answer is "benchmark it"
The short answer: none. At least not right now.
I don't think the compiler will recognize that you're running [results count] twice, BUT that shouldn't matter. The usual quote on this subject is that "premature optimization is the root of all evil". Write your code however you want to; think about readability first. Then, after you've finished, and you've actually noticed that your code is slow, then go back and find optimizations if necessary.
So the rule is: write sensible, readable code, and optimize afterwards if problems are discovered.
The compiler will not pick up the fact that you already ran it.
Fixing this is not an optimization, it's simply programming correctly. There are a few things that you just have to be aware of that go beyond optimization.. most of them involve unnecessary looping of one sort or another.
Would it be really hard to just pull it out into a separate variable?
NSArray *results = [context executeFetchRequest:request error:&error];
NSUInteger count=[results count];
NSMutableArray *firstArray = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
NSMutableArray *secondArray = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
Which is absolutely no less clear (possibly more clear).
Anyway, executing unbounded unnecessary loops need to be avoided, it's not optimization, it's just wrong.
And please don't think I don't understand the cons of premature optimization, I've ranted about it repeatedly in replies to other questions...
As for your other question, never "optimize" without a test or spec showing that your code is running too slow, but DON'T code poorly. Always be aware of inner loops and things that might cause inner loops--these are the most critical.
An insertion sort into an array list causes an inner loop, an insertion sort into a linked list does not...
iterating over a linked list causes an inner loop (assuming you are using the loop index to retrieve the "Next" value instead of holding onto the node and referencing .next which would be fine), iterating over an array list does not.
Comparing two lists often requires an inner loop and it's often worth looking over your data to try to figure out a way that an inner loop is not required (for instance, if they are both sorted you can code such that you won't get an inner loop.