Objective-c memory management - objective-c

As a new comer to Objective-c and its memory management technique, comparing two pieces below. (the original code was exacted from apple.com autorelease pools)
Questions:
1. Do the two pieces achieve the same result?
2. Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
3. Does the 2nd code below violate anything as far as best practices? Performance?
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
for (NSString *fileName in args) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error] autorelease];
/* Process the string, creating and autoreleasing more objects. */
[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSString *fileContents = [[NSString alloc] autorelease];
for (NSString *fileName in args)
{
// NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
//[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}

Do the two pieces achieve the same result?
No. It's bound to blow up and/or leak when fileContents = [fileContents ... is called the second time. Implementations assume their instance's memory is zeroed because that is guaranteed by the runtime.
Even if one initializer is proven to work without issue or leaks, many of them will not.
Don't try to 'renew' your objects like this - just please do it the normal way =)
The sequence (in a non-gc environment) should be: 1) alloc 2) init...(using designated initializer) 3) dealloc.
Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No. In addition to what is outlined in #1, you should not assume the address returned from alloc will be the same returned from the initializer. The initializer may choose to destroy the result of alloc and create or return another allocation. In this case, you will introduce a reference count imbalance (effect: leak and/or zombie).
Does the 2nd code below violate anything as far as best practices?
NSString *fileContents = [[NSString alloc] autorelease]; is weird, very unconventional. The first time I read it, I missed the error because it is so conventional for init...] to be placed between alloc and autorelease.
Does the 2nd code below violate anything as far as Performance?
Performance may suffer by removing the autorelease pool from the inner loop. When large allocations and/or high iterations occur, it's best to create small localized pools, like the original.

To answer your questions:
1) Do the two pieces achieve the same result?
No, they don't, mainly because the second example is incorrect, on two accounts.
Firstly, NSString *fileContents = [[NSString alloc] autorelease] makes very little sense. This line essentially allocates room for a string, and without even initializing it (and increasing its initial retain count to 1), telling it to put itself onto an autorelease pool. This is a big no-no.
And second, fileContents = [fileContents initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error] makes just as little sense. You're initializing a new object using old memory that has not been released or reset in any way, which is just undefined behavior. As Sherm Pendley mentioned, you cannot allocate space once and simply reuse that memory without concern, and since you have no idea how NSString is defined internally, this can either simply leak memory or very simply blow up in your face. Another big no-no.
The rest of the code appears to be fine (it does depend on what you're doing in the 'process' step). As others have pointed out, +alloc and -init are almost always paired with each other, but not just because of convention, but because that this pairing is simply the right thing to do. You must +alloc memory before you can begin to use it, and you must -init to get that memory zeroed and in the right condition to run properly. Any other combination might not be fit for use.
2) Do the two pieces achieve the same memory management result? ( as far memory cleaning up, leaking, etc.)
No, they definitely do not. The second one will leak large amounts of memory, and very well might not even run. The first once is definitely okay, but might not run efficiently due to the use of inner autorelease pools.
3) Does the 2nd code below violate anything as far as best practices? Performance?
Yes, it does, and in fact, even the first one might not run efficiently. Creating an inner autorelease pool might not be in your best interest, and might even hinder your program. The venerable Mike Ash did some testing with autorelease pools, and the results are a little surprising. It turns out that differently-sized autorelease pools will work better on different computers (back then, on his old computer, 100 objects was the most efficent per pool; for me, 10000 objects works a little bit better, and even then only marginally better than 1000 objects)

Related

Is this a memory leak (using NSArray and NSCopying)

I tried something out in my code to see the effect on memory utilization. I wanted to find out if the line inside the loop was leaking. Running this loop took utilization up to 100MB and it didn't go back down again. Does this indicate a memory leak? If so why? (I'm using ARC)
for (i = 0; i < 10000000; i++)
{
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
(accounts is an array of AccountSummary objects which implements NSCopying like this: name city state phone are all NSStrings, isLocal is BOOL)
- (id)copyWithZone:(NSZone *)zone {
AccountSummary *newAccount = [[AccountSummary allocWithZone:zone] init];
newAccount.name = [self.name copyWithZone:zone];
newAccount.city = [self.city copyWithZone:zone];
newAccount.state = [self.state copyWithZone:zone];
newAccount.phone = [self.phone copyWithZone:zone];
newAccount.isLocal = self.isLocal;
return newAccount;
}
There's no leak here that I can see. There will, however, be quite a bit of peak memory usage.
The exact behaviour of things that should logically release memory varies. Quite often, instead of releasing the memory it's autorelased. (With MRR, there used to be a method called autorelease.) When you autorelease something, it isn't really released but is instead scheduled for release later, when your code is finished because it's returned to the main event loop.
If part of this is being autoreleased — and my guess is that the property assignment is autoreleasing, because autorelease is "safer" than hard releasing — that memory won't be deallocated until your next autoreleasepool flush. Code on the main thread has an autoreleasepool set up by the OS itself, so each time you return to the main event loop everything that's been autoreleased gets flushed out. Here, that probably means that all 10,000,000 copies are kept in memory until you return to the main event loop. Darn right that'll crash a real device. :)
(That's assuming you're on the main thread; if you're not, you may not even have an autorelasepool set up, which means you probably will get a leak. But I think you get warnings to console in this case, so you'd already have a hint about which way to go.)
You can reduce this peak memory usage by using #autoreleasepool:
for (i = 0; i < 10000000; i++) #autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
What will happen now is that the memory scheduled for release later in each iteration of the loop will actually be released each iteration of the loop. That should solve your immediate problem.
That said, i can't imagine why you're doing this except to check the behaviour. And if that's the case, this is unlikely your core problem.
Assuming your core problem is a leak, with ARC you're not really looking for leaks. You're looking for circular references. That means your answer likely lies elsewhere in your code. If you're sure it's self's accounts rather than dsl's accounts that are leaking, look for self being involved in a circular loop.
Also, keep in mind that calling copyWithZone: on a NSString will probably not copy the string. (There's no need to copy a read-only string, as the read-only string can't be changed. Both "copies" can be the same object.) So if you're leaking just strings, they could be associated with the original objects.
When creating lots of objects inside a loop, you should do that inside an auto release pool.
#autoreleasepool {
for (i = 0; i < 10000000; i++) {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
or, more likely in the real world...
for (i = 0; i < 10000000; i++) {
#autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}

Is this the right way to use NSAutoreleasePool?

I'm new to Objective-C and I'm not sure if I'm using NSAutoreleasePool the right way.
If I want to use autorelease only one time I use:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *newText = [NSString stringWithFormat:#"%d", prograssAsInt];
sliderLabel.text = newText;
[pool release]; //newText will be released
If I want to use autorelease several times I use:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *newText = [NSString stringWithFormat:#"%d", prograssAsInt];
sliderLabel.text = newText;
[pool drain]; //newText will be released
newText = [NSString stringWithFormat:#"%d", prograssAsInt];
sliderLabel.text = newText;
[pool drain]; //newText will be released
newText = [NSString stringWithFormat:#"%d", prograssAsInt];
sliderLabel.text = newText;
[pool release]; //newText will be released
Is this OK? Are there any memory leaks?
(2) is not OK. -drain and -release are equivalent (in a ref-counting environment), and aftering -draining the autorelease pool is deallocated. So you will double-release the autorelease pool object and crash the program.
Even before ARC, unless you are working in a very tight memory budget, it's atypical to create an NSAutoreleasePool besides the boilerplate main(). The objects -autoreleased into the pool will be released after every tick of NSRunLoop anyway. There will be no memory leak if you strictly follow the ownership transfer rules (see Understanding reference counting with Cocoa and Objective-C).
And with ARC turned on you don't even need to care about this — the compiler will insert the -retain and -release at the right place for you.
Also, if sliderLabel.text is marked as #property(retain) (or (strong)), then releasing the autorelease pool in (1) will not release the newText because that object has a new owner now.
I would say the calls to [pool drain] are unnecessary. I've never seen them used in practice. I suppose if you are allocating huge amounts of memory inside the autorelease pool, it might be necessary. But in the typical case, I would think not.
You will want to start using the following construct, by the way, for autorelease pools:
#autoreleasepool {
... your code ...
}
This is, apparently, much more efficient than the "old" way (the way you are doing it). Functionally, it's the same, but internally it performs much better. There was something about this in recent Xcode/iOS release notes.
Sorry to say this, but RTFM. After -drain is called, the pool deallocates itself, so that it is invalid.
And, currently, in objective-c with Apple's LLVM compiler, there is a language addition called #autoreleasepool that works with both ARC and non-ARC code, that you can leverage as such:
#autoreleasepool {
// code that will automatically have any -autoreleased variables cleaned up.
}
Normally, if you're on the main thread and you're not using a huge loop of resource intensive code, you don't ever need to create your own autorelease pools. Just use the default one that's created for you.
You only need to make your own if you're multithreading or if you're doing a memory intensive long-running loop (which you probably shouldn't do on the main thread anyway).

Objective-C: autoreleased object as parameter for method

could the following code lead to problems?
- (void) method1 {
NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
A sometimes-appearing crash in my app looks like it IS a problem; but I would not understand that... shouldn't myArray stay alive at least until the end of method1?
Thanks a lot for your help!
So my questions besides "can that be a problem" are:
- would it be enough to remove autorelease and make a release at the end of the method?
- if not: do I have to make a retain/release in "someMethod"?
EDIT: but this can be a problem, am I right?
- (void) method1 {
NSMutableArray *myArray = [self getArray];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
- (NSMutableArray) method2 {
return [[[NSMutableArray alloc] init] autorelease];
}
I'm going to assume for a moment that you meant this to be NSMutableArray rather than NSArray. If you really mean NSArray here, then it is all impossible. You can't "fill the array" on an immutable array.
Yes, this should work. Your problem is likely elsewhere. A common mistake here would be over-releasing something you put into myArray.
Looks like you're missing a couple of open brackets on the line where you allocate your array. Should be [[[NSMutableArray alloc] init] autorelease];
Your code is completely correct. Do not listen to people telling you to remove the autorelease and manually release the array after the call to someMethod:.
In 99% of cases using an autorelease'd object has absolutely no negative performance impact on your application. The only time you want to worry about it is in loops.
The [[[Foo alloc] init] autorelease] pattern is just the same as using a built-in helper method like [NSString stringWithFormat:...]. They both return an autoreleased object, but you probably don't worry about performance with the latter. Again, only worry about autorelease in large loops, or when Instruments tells you you have a problem.
Using the [[[Foo alloc] init] autorelease] style also keeps all the memory management on a single line. If you get used to typing [[[ you won't forget the release. If you are copying and pasting code, or moving code around, you won't accidentally lose the corresponding release because it's all on the same line.
It's also easier to review code that uses the [[[Foo alloc] init] autorelease] style because you don't have to go around hunting for the release further down the method to make sure the memory management is correct.
So in terms of readability and safety and correctness, your code is absolutely fine and good. This applies to both your original snippet and the follow up you added below. Performance of autorelease only becomes an issue when you have large loops.
Your crashing issue must be caused by some other factor that is not evident in the code you posted.
I also suggest you read the Memory Management Programming Guideline if you have not already. Basically, and autorelease'd object is guaranteed to remain valid until the enclosing pool is released. In your case, the autorelease pool exists higher up in the call stack (probably the main runloop) so you can be safe in the knowledge that your autorelease'd array will remain valid for the duration of any calls you make.
One last point. Your array allocation code could also make use of the array helper constructor:
NSMutableArray *myArray = [NSMutableArray array];
This is even simpler, cleaner and shorter than your original.
Best thing to do will be something like that:
- (void) method1 {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
[myArray release];
}
and it's best from two different angles. One way you are holding your array till you have no need for it, so it will no wipe from memory until you say so. Second is the same sentence, only the reason is that you will clean up memory from no more needed objects as soon as it could be done.The last may need some more explanation... Autoreleased objects should be autoreleased as soon as you have no need for it, but they are released on two basic rules: at the end of block(not always) or at the memory pressure after end of the block(yes always). In other words NSAutoreleasePool doesn't always release stuff at the end of some block of the code, and it's not even close to be rare that it delays those releases to later point.Anyway you should always check for over-releasing your object as it will lead to crash when you'll try to reach such object at your code.

Allocation and release problem

I'm trying to explain memory management on iOS to a friend and I'm showing him a wrong code. But when I'm launching the app, it's working and I don't know why.
Here's the snippet :
NSString *myString = [[NSString alloc] initWithString:#"myString"];
[myString release];
NSLog(#"%#",myString);
I don't understand why my NSLog is working ...
Do you have some explanations ?
Thanks !
There are two things to bear in mind with regard to your example.
As MByD explained, accessing an object that’s been deallocated is undefined behaviour. It might or might not crash your program — it depends on whether the memory that had been allocated to that object has been reused and what’s been put there. In some cases the memory hasn’t been reused yet (and you might think the object is still alive but that object is in fact a ghost object), in other cases the memory may have been reused by another Objective-C object (the program won’t crash but you’ll see a different object) and in other cases the memory may have been reused by something that’s not an Objective-C object (in which case the program will likely — but not necessarily — crash).
Your string object is a constant string. As explained in the answer to this question and its comments, a constant string is never deallocated. When you send -[NSString initWithString:] passing a constant string as the argument, Cocoa returns the original constant string so your code is effectively the same as NSString *myString = #"myString";. This is an internal Cocoa implementation detail though. Production code should always consider that objects returned by +alloc (and, usually, subsequent -init) are owned by the caller, should be released when the caller is not interested in them anymore, and won’t be available after deallocation.
For the sake of experimentation, try the following code:
#import <Foundation/Foundation.h>
#include <stdio.h>
int main(void) {
[NSAutoreleasePool new];
NSString *s1 = [[NSString alloc] initWithString:#"myString"];
NSString *s2 = [[NSString alloc] initWithString:#"myString"];
NSString *s3 = [[NSString alloc] initWithString:#"myString"];
NSString *s4 = [[NSString alloc] initWithString:#"myString"];
printf("s1 = %p\n", s1);
printf("s2 = %p\n", s2);
printf("s3 = %p\n", s3);
printf("s4 = %p\n", s4);
[s1 release];
[s2 release];
[s3 release];
[s4 release];
return 0;
}
Conceptually, s1, s2, s3, s4 should be different objects. However, by running this program you can see that they are effectively the same object (they have the same address):
$ ./a.out
s1 = 0x10080b090
s2 = 0x10080b090
s3 = 0x10080b090
s4 = 0x10080b090
This is an undefined behavior. You are not allowed to access this string, yet it might be available.

Assign nil to NSString within Autorelease pool

I'm making a great deal of NSString manipulations within an autorelease pool. Problem is my program will sometimes crash before the pool drains. I'm wondering if there is a way to circumvent this problem by assigning nil to NSString. The assignment to userLetters happens a lot. See code below
Before
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"];
[pool drain];
After
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *temp = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"]; //remove that found character so it can't be reused again
usersLetters = nil;
usersLetters = temp;
temp = nil;
[pool drain];
I doubt what assigning to nil will help in what you want to achieve.
(I assume you mean that your program crashes because the memory is exhausted, otherwise, it is much more likely that you released to often somewhere, you should also run the Status Analyzer over your code.)
What you can do is to send a retain message to all objects that you still need (in your case usersLetters) and drain the pool afterwards. The objects that you still need should then have a retain count of 1, all other autoreleased objects should have been deallocated.
In your case, this would be
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [[usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"] retain]; //sending retain to an object increases its retain count by 1 and returns the object itself
// some more stuff that needs memory
[pool drain];
// ...
[usersLetters release];
[pool release];
You don't mention if your project targets iOS or Mac OS X. If the latter, the best way to resolve this issue may be to simply use garbage collection.
If GC is not an option (which it is not on iOS), the idiomatic way to deal with this is to wrap a nested autorelease pool around your inner, fast-allocating operations. In this case you must retain any objects which need to outlive the nested pool, as mrueg has explained.