I have a requirement to create some NSDecimalNumber objects objects as part of my application (as I require the precision of calculation they offer) but I note that in calculations they return NSDecimalNumber objects which are, presumably, autoreleased.
My question is really whether this is potentially problematic in an iPhone application where I may carry out lots of calculations.
The question is not just relating to NSDecimalNumber specifically but to the sometimes unavoidable creation of autoreleased objects in the course of developing an iPhone application.
Any detailed answers on this point would be gratefully received.
Remember, you can create your own NSAutoreleasePool objects.
For example:
for (int i = 0; i < 1000; ++i) {
NSAutoreleasePool * p = [[NSAutoreleasePool alloc] init];
for (int j = 0; j < 1000; ++j) {
NSString * s = [NSString stringWithFormat:#"%d,%d", i, j];
NSLog(#"%#", s);
}
[p release];
}
If you do that, you'll never have more than 1000 of those strings in memory at a time.
Yes, creating lots of autoreleased instances on the iPhone can create memory problems, particularly in a tight loop, which is why I tend to avoid them when I can. You can create your own autorelease pools to manage this, but they also add some performance overhead and additional code that you have to keep track of.
It is for this reason that when I do high-precision calculations, I tend to use the NSDecimal C struct instead of NSDecimalNumbers. In fact, I performed some benchmarks on this and found a significant performance increase when going with the C struct (copied from my answer here):
NSDecimal
Additions per second: 3355476.75
Subtractions per second: 3866671.27
Multiplications per second: 3458770.51
Divisions per second: 276242.32
NSDecimalNumber
Additions per second: 676901.32
Subtractions per second: 671474.6
Multiplications per second: 720310.63
Divisions per second: 190249.33
As you can see, there is almost a fivefold increase in calculation speed between the NSDecimal paths and NSDecimalNumber ones. The biggest difference between the NSDecimal and NSDecimalNumber calculations was the memory allocation of the NSDecimalNumber instances. Therefore, you should avoid allocating temporary autoreleased instances wherever you can.
If you are worried about handling too many autoreleased objects you can create your own autorelease pool (see memory management):
for (count = 0; count < limit; count++)
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSString *fileContents;
NSString *fileName;
fileName = [args objectAtIndex:count];
fileContents = [[[NSString alloc] initWithContentsOfFile:fileName] autorelease];
// this is equivalent to using stringWithContentsOfFile:
/* Process the file, creating and autoreleasing more objects. */
[loopPool release];
}
Every object that is given a chunk of memory will have to relinquish it. The question is when.
I try to alloc/init/release when I can, so that objects only hang around as long as they are needed.
If I use autorelease I have less control over when the object is released. If the application tries to access a released object then it can crash. So tighter memory management is a good thing, I think.
So long as you retain autoreleased objects returned from a method, you should be okay. (Unless you're asking about something else, in which case I apologize in advance.)
Related
After trying to print retainCount of object I get 2147483647. Why do I get such a result? It should be 1, shouldn't?
NSString *myStr = [NSString new];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
2011-09-08 17:59:18.759 Test[51972:207] refCount = 2147483647
I use XCode Version 4.1. Tried compilers GCC 4.2 and LLVM GCC 4.2 - the same result.
Garbage Collection option was set to unsupported.
NSString is somewhat special when it comes to memory management. String literals (something like #"foo") are effectively singletons, every string with the same content is the same object because it can't be modified anyway. As [NSString new] always creates an empty string that cannot be modified, you'll always get the same instance which cannot be released (thus the high retainCount).
Try this snippet:
NSString *string1 = [NSString new];
NSString *string2 = [NSString new];
NSLog(#"Memory address of string1: %p", string1);
NSLog(#"Memory address of string2: %p", string2);
You'll see that both strings have the same memory address and are therefore the same object.
This doesn't directly answer your question, but retainCount is not really all that useful and should not be used for testing. See this SO post for details.
When to use -retainCount?
While NSString's are an odd case (there are others in the framework) you might also run across this in other clases - it's one of the ways of creating a singleton object.
A singleton only exists once in the app and it's pretty important that it never gets released! Therefore, it will overwrite some methods of NSObject including (but not limited to):
- (id)release {
// Ignore the release!
return self;
}
- (NSUInteger)retainCount {
// We are never going to be released
return UINT_MAX;
}
This object can never be released and tells the framework that it's a singleton by having a ludicrously high retain count.
Checkout this link for more information about singleton objects.
I've seen this a couple of times regarding NSStrings, the retainCount returns the maximum count instead of the actual one when you try to look at retainCounts of strings in this manner.
Try this;
NSString *myStr = [[NSString alloc] init];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
Edit: Restored NSUInteger
I am just reading through the Practical Memory Management guide.
I am somewhat confused by this block of code:
- (void)printHello {
NSString *string;
string = [NSString stringWithFormat:#"Hello"];
NSLog(#"%#", string);
}
It seems to me that string is going to have a reference count of 0. Is this true?
What stops string from being deallocated before we call NSLog(string)?
Is this somehow equivalent to this:
- (void)printHello {
NSString *string;
string = [[[NSString stringWithFormat:#"Hello"] retain] autorelease];
NSLog(#"%#", string);
}
Edit: Similarly this code is given in the Practical Memory Management
guide:
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:#"%# %#", firstName, lastName];
return string;
}
When and how does the return value get freed? Who is the owner? Does the caller of fullName need to release the string returned by full name?
Strictly speaking,
- (void)printHello {
NSString *string;
string = [NSString stringWithFormat:#"Hello"];
NSLog(#"%#", string);
}
Is not equivalent to
- (void)printHello {
NSString *string;
string = [[[NSString stringWithFormat:#"Hello"] retain] autorelease];
NSLog(#"%#", string);
}
The convention is that a method should autorelease any object it returns. The only exception (AFAIK) is for constructors, which return an object with a +1 retain count. Since [NSString stringWithFormat:] returns an object. In first snippet, stringWithFormat: returns an already autoreleased object. the second snippet, you're retaining it once more and it'll be released twice (which has the same effect, but the second retain/autorelease pair is redundant).
Ok, now to answer your question. Essentially, every time UIKit calls your code, it creates an NSAutoreleasePool object. Every time you autorelease an object, its added to this pool. Finally, when your code returns back to UIKit, it calls the drain method on the pool (i.e [pool drain]) and that releases every object which has been added to the pool and deallocates the pool. Also, autorelease pools can be nested, so you can create your own pools and drain them if you're going to be creating a lot of autoreleased objects. It isn't as complicated as it sounds.
I'd highly recommend that you read the Autorelease Pools chapter in the Memory Management Guide (Which incidentally, comes right after the Practical Memory Management chapter).
First of all:
NSLog(string);
Don’t do this. (I just realized it comes right from the Apple docs. Weird.) The first argument to NSLog is the formatting string. If your string contains some percent escapes, bad things will happen. The correct, if slightly longer way is:
NSLog(#"%#", string);
Now to the point: Autoreleased objects do not have zero retain count. They have retain count 1+ and have a pending –1 operation on them that will happen “soon in the future”.
The precise meaning of “soon in the future” depends on the situation. If you’re on the main thread and there is no additional autorelease pool in place, autoreleased objects will be released on the next runloop iteration. This does not have to be the case if you have an additional release pool:
// Let’s pretend this is a long loop and you don’t want to wait
// for the autoreleased objects to be collected by the main pool.
for (…) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *foo = [NSString stringWith…];
[pool drain];
// Now foo is no longer valid.
}
As for returning autoreleased objects, that’s one of the main use cases for autoreleasing. You are returning an object that will perish “soon”, but if the caller is interested, he can retain and take over the ownership. (It’s like, if you pardon the image, passing a bomb with a burning safety fuse. If the caller is interested, he’ll put out the fuse by retaining.) And if the caller is not interested, like maybe he’s ignoring an output from a function or just uses the value to construct some other object, he does not do anything and the object will get out of memory:
- (id) createObject {
return [NSString stringWith…];
}
- (void) interestedCaller {
NSString *value = [[self createObject] retain];
}
- (void) notInterestedCaller {
[self createObject]; // maybe just interested in side effects
NSString *differentString = [NSString stringWithString:[self createObject]];
}
This is really convenient and makes the manual memory management quite pleasant. You might be interested in run loops and the Objective-C tutorial by Scott Stevenson.
iPad application
I have a large text file which I want to divide into several pieces and process them in a loop.
I used the following code:
NSArray * contentArray = [stringFromFile componentsSeparatedByString:#" "];
for(...){
contentRange = NSMakeRange(lastPosition , newLength);
NSArray * subContentArray = [contentArray subarrayWithRange:contentRange];
NSString *subContent = [subContentArray componentsJoinedByString:#" "];
....
//Process sub content here...
}
After running, I receive malloc error, code 12
Observing the Activity monitor, the memory and the VM size increases and until system memory is exhausted and the application crashes.
Is there any way to prevent this?
One way to work around this issue will be to use a custom NSAutoreleasePool to constantly clear temporarily allocated memory. Like so:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSUInteger i=0;
for (...) {
... // your method contents
if (++i % 10 == 0) {
// you might want to play with the frequency of the releases, depending on the size of your loop
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
}
[pool release];
The autorelease pool thing will take care of the memory problem for the most part, but if the file is any size it might be slow.
It seems like scanning the original string in a loop getting substrings directly from the string would be faster. Also use at least 1/2 the memory, since you duplicate the string in the components array.
The code would use rangeOfString on the original string, until a substring of appropriate length was found, then process the substring, and onto the next one.
iPhone 3G phones only have about 5 - 15 MB of memory total for your application. You are on an iPad, though I see which does give you about 4x that amount.
I'm trying to swap two strings but I'm not sure if what I am doing is legal (coming from java I'm new to the whole retain count memory management).
Here's my code:
NSString *temp = [[NSString alloc] init];
temp = originalLanguageCode;
originalLanguageCode = translatedLanguageCode;
translatedLanguageCode = temp;
[temp release];
Is this allowed? I'm getting some strange bugs and I'm not sure what's causing them, hoping that this might be it. Any help is very appreciated, thanks!
After assigning a newly allocated string to temp you are immediately assigning originalLanguageCode to it, so the allocated string is completely lost. That is a memory leak. Then you are releasing temp, which was originally originalLanguageCode. This is an over-release.
Try this instead:
NSString *temp = originalLanguageCode;
originalLanguageCode = translatedLanguageCode;
translatedLanguageCode = temp;
Everything looks fine except that in your first line you're creating a string which you immediately leak on the second. You can just say:
NSString *temp = originalLanguageCode;
...
... and get rid of the [temp release] since you didn't create (and don't own) the object assigned to "temp."
Now you didn't say whether originalLanguageCode and translatedLanguageCode are instance variables. If so, use their accessors (which should themselves be doing the right thing with memory management).
I'm using Instruments to try to determine if there are places in my application that I could be more efficient with use of memory. I've taken the time to get somewhat familiar with Instruments but I'm generally a newbie with hunting memory management issues having come from a Java background. I seem to be using about 1.82mb by calls to this method:
+ (NSString *)stringFromDateWithFormat:(NSDate *)date withFormat:(NSString *)format
{
NSDateFormatter *dateFormatter;
NSString *result;
if (nil == date || nil == format)
return nil;
result = nil;
if (nil != (dateFormatter = [[NSDateFormatter allocWithZone:[self zone]] init])) {
[dateFormatter setDateFormat:format];
if (nil != (result = [dateFormatter stringFromDate:date])) {
[dateFormatter release];
return result;
}
[dateFormatter release];
}
return nil;
}
As I'm releasing the date formatter I'm wondering if the NSString result is my issue. It seems to me that the stringFromDate library call would return an autoreleased object so there's nothing I can do to 'manually' manage it. A bit unsure of how to optimize this method.
Is this method getting called a lot of times in a loop? Autoreleased objects only get released when the NSAutoreleasePool they're in gets released. As I understand it, the default autorelease pool is created and release every event loop. It's possible you're creating too many autoreleased objects in the course of a single event loop. The solution is to create your own NSAutoreleasePool in an appropriate place, and release it to clear up autoreleased objects. An extreme example that illustrates the point:
int i;
NSAutoreasePool* pool = nil;
for (i = 0; i < 1000000; ++i) {
/* Create a new pool every 10000 iterations */
if ((i % 10000) == 0) {
if (pool) [pool release];
pool = [[NSAutoreleasePool alloc] init];
}
[someObj someMethodThatCreatesAutoreleasedObjects];
}
[pool release];
In that example, the current pool is released every 10,000 iterations and a new one is created. You can read more about memory management in the Memory Management Programming Guide section on autorelease pools.
You need to return an autoreleased object anyway, so there's really nothing you should be doing about the result string. I don't see any mistakes related to memory, but your code is definitely more verbose than needed. Keep in mind that in Objective-C, if you call a method on nil, you get back nil (or 0 for an integer, but not for floating point values). You can take out all those if statements and the two return paths, and your code will still work the same way. Also, I would just use alloc instead of allocWithZone.
I am not a 100% with this. I am also just learning Mac/Iphone development. But you can use the auto release pool to help with memory management. It is used to get around release problems.
Here is an article with lots on memory management. Check out the left sided menu.