[iOS开发站在巨人肩膀上]之How to avoid memory leaks in iPhone applications
2011-09-04 23:51
756 查看
本文来源:http://www.codeproject.com/KB/iPhone/avoidiphoneleaks.aspx
This article lists some tips to avoid memory leaks in your iPhone apps.
Ownership is the overall idea behind how memory management should work on the iPhone. When an object has an owner, they are responsible for releasing the object when they have finished using it.
An object can have more than one owner, and when it has no owners, it is set for de-allocation.
Ownership is made when creating an object with
or
an object, or when calling Cocoa functions that have Create or Copy in their name. There are two ways memory is released: either explicitly calling
an object, or using the auto-release pool.
Behind ownership is a system called reference counting. Most objects in the iPhone SDK are strongly referenced, this means they use reference counting.
When you create an object, it will have a reference count of 1, and calling
an object will increment the reference count by 1. Calling
zero, the object is de-allocated. Calling
the object will be de-allocated at a later time automatically.
Objects can also be weakly referenced, meaning that a reference count isn't kept and the object will need to be de-allocated manually.
When should you use
before you use it.
Every time you use
or a Cocoa function that has Create or Copy in its name, you need to have a matching
The developer should think about objects in terms of ownership, and not worry about reference counts. If you have matching
it is obvious that you will have matching +1 and -1 additions to the reference count.
Note: It may be tempting to use
can be misleading due to the behind the scenes code in the SDK. It is not recommended to manage memory this way.
Objects set to auto-release mean that they do not need to be explicitly released because they will be released when an auto-release pool is popped. The iPhone has an auto-release pool that runs
on the main thread which usually releases objects at the end of an event loop. When you create your own threads, you must create your own auto-release pool.
On the iPhone, there are convenience constructors; objects created with convenience constructors are set to auto-release.
Examples:
Collapse | Copy
Code
An allocated object can be set to auto-release like this:
Collapse | Copy
Code
Or like this:
Collapse | Copy
Code
Ownership for auto-released objects is relinquished when the pointer goes out of scope or when an auto-release pool is popped.
The built in auto-release pool is usually popped at the end of an event loop, but this may not be desired when you have a loop that is allocating a lot of memory in each iteration. In that case,
you can create an auto-release pool in the loop. Auto-release pools can be nested so the objects allocated in the inner pool will be released when that pool is popped. In the example below, objects will be released at the end of each iteration.
Collapse | Copy
Code
Note: At the time of writing, the iPhone doesn't support garbage collecting, so
work the same as
often used in cases where you want to port the program to OSX, or if garbage collecting is added to the iPhone later.
to the garbage collector that memory is being released.
When following the rules of ownership, the developer needs to be aware of what functions have ownership of an object. This is an example of returning a pointer to an object and releasing it.
Wrong way
Collapse | Copy
Code
In this example,
Having
the Coccoa Memory Management Guide; this will not leak memory, but it is not a good practice because
If
retained or not. It is therefore free to retain and release the returned object without disrupting any other code in the application.
Correct way
Collapse | Copy
Code
In the second example,
The reference count for
relinquishing its ownership of
is now free to retain and release the object, and be sure it won't leak when it's done.
In the example,
will not have ownership of it when the function ends, but what if it wanted to store the object somewhere else?
The object will then need to be retained by a new owner.
A setter function must retain the object it is storing, which means claiming ownership. If we want to create a setter function, we need to do two things before assigning a new pointer to our member
variable.
In the function:
Collapse | Copy
Code
First, we would decrement the reference count of our member variable:
Collapse | Copy
Code
This will allow the
it will allow any other owners of the object to continue to use the object.
Then, we will increment the reference count of the new
Collapse | Copy
Code
So,
finishes. The object
to, with a different reference count.
Now we can set
Collapse | Copy
Code
But what if
the same object? We can't release it with the possibility of it being de-allocated and then retain it!
Simply retain the incoming object before releasing the stored object:
Collapse | Copy
Code
Now if the objects are the same, it will up the reference count then subtract from it, causing it to stay the same before assigning it.
Another way to do this is to use the Objective-C properties.
A property for an object is declared like so:
Collapse | Copy
Code
time.
Instead of
Collapse | Copy
Code
This would be the same as a function like this:
Collapse | Copy
Code
Here,
now
released and
retains the string, so both
it. Finally,
the only owner of the copied string.
Setters like this are imported to retain the member object, if we had a function like this:
Collapse | Copy
Code
be released when the pool is drained.
If we replaced the assignment with:
Collapse | Copy
Code
Then
called.
But when do we release the object?
Since
of the class it belongs to.
Collapse | Copy
Code
Note: As
trigger something could be dangerous. On exit, the iPhone OS may clear all the application memory before
When assigning an object with a setter, be careful of lines like this:
Collapse | Copy
Code
not have a matching
Collapse | Copy
Code
Or with
Collapse | Copy
Code
Auto-release pools will release objects that are assigned in between their allocation and drain functions.
In the function below, we have a function that has a loop. In this loop, we are assigning a copy of
We are also setting
a large amount of assignments).
Collapse | Copy
Code
The problem is that
So when the loop finishes,
be undefined.
In this case, we can retain
Collapse | Copy
Code
We have taken ownership of
By adding a pair of
we are saying that
explicitly called.
When an object is added to a collection, it will be owned by the collection.
In this example, we allocate a string; it now has one owner:
Collapse | Copy
Code
We then add it to the array; now it has two owners:
Collapse | Copy
Code
We can safely release the string, leaving it to be owned only by the array:
Collapse | Copy
Code
When a collection is released, it will release all its objects as well.
Collapse | Copy
Code
In the above example, we allocate an array, allocate a string, add the string to the array, and release the array. This leaves the string with one owner, and it will not be de-allocated until we
call
In this function, we are passing in the string
then releasing
Collapse | Copy
Code
and releases it when the thread finishes. This is why we can release
or finishes.
Collapse | Copy
Code
finished.
Auto-release pools are thread specific, so if we are creating auto-released objects on a new thread, we need to create an auto-release pool to release them.
Collapse | Copy
Code
This calls the function
Collapse | Copy
Code
The object
will be released before the end of the function.
Collapse | Copy
Code
There is an auto-release pool created for the main thread automatically, so in
we do not need to create an auto-release pool as this function runs on the main thread.
To avoid memory leaks in your iPhone apps, it is important to keep in mind who owns each object that is allocated, when to relinquish that ownership, and keeping
in pairs. If you follow the rules of ownership, your apps will be more stable and you will cut down a lot of bug fixing time.
Introduction
This article lists some tips to avoid memory leaks in your iPhone apps.
Ownership
Ownership is the overall idea behind how memory management should work on the iPhone. When an object has an owner, they are responsible for releasing the object when they have finished using it.An object can have more than one owner, and when it has no owners, it is set for de-allocation.
Ownership is made when creating an object with
alloc,
new,
or
copy, when calling
retainon
an object, or when calling Cocoa functions that have Create or Copy in their name. There are two ways memory is released: either explicitly calling
releaseon
an object, or using the auto-release pool.
Behind ownership is a system called reference counting. Most objects in the iPhone SDK are strongly referenced, this means they use reference counting.
When you create an object, it will have a reference count of 1, and calling
retainon
an object will increment the reference count by 1. Calling
releasewill decrement the reference count by 1; when the reference count reaches
zero, the object is de-allocated. Calling
autoreleaseinstead of
releasemeans
the object will be de-allocated at a later time automatically.
Objects can also be weakly referenced, meaning that a reference count isn't kept and the object will need to be de-allocated manually.
When should you use
retain? When you want to prevent an object from being de-allocated
before you use it.
Every time you use
copy,
alloc,
retain,
or a Cocoa function that has Create or Copy in its name, you need to have a matching
releaseor
autorelease.
The developer should think about objects in terms of ownership, and not worry about reference counts. If you have matching
retainand
releasecalls,
it is obvious that you will have matching +1 and -1 additions to the reference count.
Note: It may be tempting to use
[object retainCount], but the values this returns
can be misleading due to the behind the scenes code in the SDK. It is not recommended to manage memory this way.
Auto-release
Objects set to auto-release mean that they do not need to be explicitly released because they will be released when an auto-release pool is popped. The iPhone has an auto-release pool that runson the main thread which usually releases objects at the end of an event loop. When you create your own threads, you must create your own auto-release pool.
On the iPhone, there are convenience constructors; objects created with convenience constructors are set to auto-release.
Examples:
Collapse | Copy
Code
NSString* str0 = @"hello"; NSString* str1 = [NSString stringWithString:@"world"]; NSString* str2 = str1;
An allocated object can be set to auto-release like this:
Collapse | Copy
Code
NSString* str = [[NSString alloc] initWithString:@"the flash?"]; [str autorelease];
Or like this:
Collapse | Copy
Code
NSString* str = [[[NSString alloc] initWithString:@"batman!"] autorelease];
Ownership for auto-released objects is relinquished when the pointer goes out of scope or when an auto-release pool is popped.
The built in auto-release pool is usually popped at the end of an event loop, but this may not be desired when you have a loop that is allocating a lot of memory in each iteration. In that case,
you can create an auto-release pool in the loop. Auto-release pools can be nested so the objects allocated in the inner pool will be released when that pool is popped. In the example below, objects will be released at the end of each iteration.
Collapse | Copy
Code
for (int i = 0; i < 10; ++i) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString* str = [NSString stringWithString:@"hello world"]; [self ProcessMessage: str]; [pool drain]; }
Note: At the time of writing, the iPhone doesn't support garbage collecting, so
drainwill
work the same as
release.
drainis
often used in cases where you want to port the program to OSX, or if garbage collecting is added to the iPhone later.
drainprovides a hit
to the garbage collector that memory is being released.
Returning a pointer to an object
When following the rules of ownership, the developer needs to be aware of what functions have ownership of an object. This is an example of returning a pointer to an object and releasing it.Wrong way
Collapse | Copy
Code
- (NSMutableString*) GetOutput { NSMutableString* output = [[NSMutableString alloc] initWithString:@"output"]; return output; } - (void) Test { NSMutableString* obj = [self GetOutput]; NSLog(@"count: %d", [obj retainCount]); [obj release]; }
In this example,
outputis owned by
GetOutput.
Having
Testrelease
objviolates the rules in
the Coccoa Memory Management Guide; this will not leak memory, but it is not a good practice because
Testshouldn't release an object it doesn't own.
If
GetOutputis called, the caller shouldn't need to know if the object returned from
GetOutputis
retained or not. It is therefore free to retain and release the returned object without disrupting any other code in the application.
Correct way
Collapse | Copy
Code
- (NSMutableString*) GetOutput { NSMutableString* output = [[NSMutableString alloc] initWithString:@"output"]; return [output autorelease]; } - (void) Test { NSMutableString* obj = [self GetOutput]; NSLog(@"count: %d", [obj retainCount]); }
In the second example,
outputis set to auto-release when
GetOutputreturns.
The reference count for
outputis decremented, and
GetObjectis
relinquishing its ownership of
output. The
Testfunction
is now free to retain and release the object, and be sure it won't leak when it's done.
In the example,
objis set to auto-release, so the
Testfunction
will not have ownership of it when the function ends, but what if it wanted to store the object somewhere else?
The object will then need to be retained by a new owner.
Setters
A setter function must retain the object it is storing, which means claiming ownership. If we want to create a setter function, we need to do two things before assigning a new pointer to our membervariable.
In the function:
Collapse | Copy
Code
- (void) setName:(NSString*)newName
First, we would decrement the reference count of our member variable:
Collapse | Copy
Code
[name release];
This will allow the
nameobject to be de-allocated if the reference count is zero, but
it will allow any other owners of the object to continue to use the object.
Then, we will increment the reference count of the new
NSStringobject:
Collapse | Copy
Code
[newName retain];
So,
newNamewill not be de-allocated when the
setNameselector
finishes. The object
newNamenow points to is different to the one
namepoints
to, with a different reference count.
Now we can set
nameto point to the
newNameobject:
Collapse | Copy
Code
name = newName;
But what if
nameand
newNameare
the same object? We can't release it with the possibility of it being de-allocated and then retain it!
Simply retain the incoming object before releasing the stored object:
Collapse | Copy
Code
[newName retain]; [name release]; name = newName;
Now if the objects are the same, it will up the reference count then subtract from it, causing it to stay the same before assigning it.
Another way to do this is to use the Objective-C properties.
A property for an object is declared like so:
Collapse | Copy
Code
@property(nonatomic, retain) NSString *name;
nonatomicmeans there is no blocking for multiple threads accessing the data at the same
time.
atomicwill lock the data for a single thread, but is slower, so it is not used unless necessary.
retainmeans that we want to retain the
newNameobject.
Instead of
retain, we could use
copy:
Collapse | Copy
Code
@property(nonatomic, copy) NSString *name;
This would be the same as a function like this:
Collapse | Copy
Code
- (void) setName:(NSString*)newName { NSString* copiedName = [newName copy]; [name release]; name = copiedName; [name retain]; [copiedName release]; }
Here,
newNameis copied to
copiedName,
now
copiedNamehas a copy of the string and owns it.
nameis
released and
copiedNameis assigned to
name.
namethen
retains the string, so both
copiedNameand
nameown
it. Finally,
copiedNamereleases the object and
nameis
the only owner of the copied string.
Setters like this are imported to retain the member object, if we had a function like this:
Collapse | Copy
Code
- (void) Test { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // do something... name = [self GetOutput]; // do something else... NSLog(@"Client Name before drain: %@", name); [pool drain]; NSLog(@"Client Name after drain: %@", name); }
namewould be undefined after the call to
drainbecause
namewill
be released when the pool is drained.
If we replaced the assignment with:
Collapse | Copy
Code
[self setName:[self GetOutput]];
Then
namewill be owned by the class and be retained for use until
releaseis
called.
But when do we release the object?
Since
nameis a member variable, the safest place to release it is in the
deallocfunction
of the class it belongs to.
Collapse | Copy
Code
- (void)dealloc { [name release]; [super dealloc]; }
Note: As
deallocis not always called, relying on objects to be released in
deallocto
trigger something could be dangerous. On exit, the iPhone OS may clear all the application memory before
deallocis called.
When assigning an object with a setter, be careful of lines like this:
Collapse | Copy
Code
[self setName:[[NSString alloc] init]];
namewill be set fine, but
allocdoes
not have a matching
release. A better way would be something like this:
Collapse | Copy
Code
NSString* s = [[NSString alloc] init]; [self setName:s]; [s release];
Or with
autorelease:
Collapse | Copy
Code
[self setName:[[[NSString alloc] init] autorelease]];
Auto-Release pools
Auto-release pools will release objects that are assigned in between their allocation and drain functions.In the function below, we have a function that has a loop. In this loop, we are assigning a copy of
NSNumberto
magicNumber.
We are also setting
magicNumberto be auto-released. In this example, we want to drain the pool on each iteration (this can save memory for loops with
a large amount of assignments).
Collapse | Copy
Code
- (void) Test { NSString* clientName = nil; NSNumber* magicNumber = nil; for (int i = 0; i < 10; ++i) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; magicNumber = [[self GetMagicNumber] copy]; [magicNumber autorelease]; if (i == [magicNumber intValue]) { clientName = [self GetOutput]; } [pool drain]; } if (clientName != nil) { NSLog(@"Client Name: %@", clientName); } }
The problem is that
clientNameis assigned and released in the local auto-release pool.
So when the loop finishes,
clientNamehas already been released and any further use of
clientNamewill
be undefined.
In this case, we can retain
clientNameafter we assign it, and release it when we are done:
Collapse | Copy
Code
- (void) Test { NSString* clientName = nil; NSNumber* magicNumber = nil; for (int i = 0; i < 10; ++i) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; magicNumber = [[self GetMagicNumber] copy]; [magicNumber autorelease]; if (i == [magicNumber intValue]) { clientName = [self GetOutput]; [clientName retain]; } [pool drain]; } if (clientName != nil) { NSLog(@"Client Name: %@", clientName); [clientName release]; } }
We have taken ownership of
clientNamefrom the period between the
retainand
releasecalls.
By adding a pair of
retainand
releasecalls,
we are saying that
clientNamewill not be released until
releaseis
explicitly called.
Collections
When an object is added to a collection, it will be owned by the collection.In this example, we allocate a string; it now has one owner:
Collapse | Copy
Code
NSString* str = [[NSString alloc] initWithString:@"Bruce Wayne"];
We then add it to the array; now it has two owners:
Collapse | Copy
Code
[array addObject: str];
We can safely release the string, leaving it to be owned only by the array:
Collapse | Copy
Code
[str release];
When a collection is released, it will release all its objects as well.
Collapse | Copy
Code
NSMutableArray* array = [[NSMutableArray alloc] init]; NSString* str = [[NSString alloc] initWithString:@"Bruce Wayne"]; [array addObject: str]; [array release];
In the above example, we allocate an array, allocate a string, add the string to the array, and release the array. This leaves the string with one owner, and it will not be de-allocated until we
call
[str release].
Passing in pointers with threads
In this function, we are passing in the string inputto the function
DoSomething,
then releasing
input.
Collapse | Copy
Code
- (void) Test { NSMutableString* input = [[NSMutableString alloc] initWithString:@"batman!"]; [NSThread detachNewThreadSelector:@selector(DoSomething:) toTarget:self withObject:input]; [input release]; }
detatchNewThreadSelectorups the reference count for the
inputobject
and releases it when the thread finishes. This is why we can release
inputright after starting the thread no matter when the function
DoSomethingstarts
or finishes.
Collapse | Copy
Code
- (void) DoSomething:(NSString*)str { [self performSelectorOnMainThread:@selector(FinishSomething:) withObject:str waitUntilDone:false]; }
performSeclectorOnMainThreadwill also retain the object passed in until the selector has
finished.
Auto-release pools are thread specific, so if we are creating auto-released objects on a new thread, we need to create an auto-release pool to release them.
Collapse | Copy
Code
[NSThread detachNewThreadSelector:@selector(Process) toTarget:self withObject:nil];
This calls the function
Processon a different thread.
Collapse | Copy
Code
- (void) Process { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSMutableString* output = [[[NSMutableString alloc] initWithString:@"batman!"] autorelease]; NSLog(@"output: %@", output); [self performSelectorOnMainThread:@selector(FinishProcess) withObject:nil waitUntilDone:false]; [pool drain]; }
The object
outputis allocated and set to auto-release inside the auto-release pool, and
will be released before the end of the function.
Collapse | Copy
Code
- (void) FinishProcess { NSMutableString* output = [[[NSMutableString alloc] initWithString:@"superman?"] autorelease]; NSLog(@"output: %@", output); }
There is an auto-release pool created for the main thread automatically, so in
FinishProcess,
we do not need to create an auto-release pool as this function runs on the main thread.
Summary
To avoid memory leaks in your iPhone apps, it is important to keep in mind who owns each object that is allocated, when to relinquish that ownership, and keeping retainand
releasecalls
in pairs. If you follow the rules of ownership, your apps will be more stable and you will cut down a lot of bug fixing time.
相关文章推荐
- How to avoid memory leaks in iPhone applications
- How to detect and avoid memory and resources leaks in .NET applications
- How to detect and avoid memory and resources leaks in .NET applications
- How to detect and avoid memory and resources leaks in .NET applications()
- [iOS开发站在巨人肩膀上]之How to implement Queue in Objective-C
- [iOS开发站在巨人肩膀上]之How to implement a stack in Objective-C
- How to detect and avoid memory and resources leaks in .NET applications
- How to detect and avoid memory and resources leaks in .NET applications
- How to check memory leaks in android app?
- Instruments Tutorial for iOS: How To Debug Memory Leaks
- How to Increase the Memory Limit for 32-bit Applications in Windows 64-bit OS
- iOS开发笔记(八)How to test an iPhone application update?
- iOS Programming 101: How To Get the User Location in iPhone App
- (iPhone/iPad开发)how to browse the document directory of iOS Devices
- Instruments Tutorial for iOS: How To Debug Memory Leaks【转】
- [iOS开发站在巨人肩膀上]之iPhone Images from URL using XML File
- How to avoid memory leaks with Doctrine?
- How to prevent memory leaks when reloading web applications(“java.lang.OutOfMemoryError: PermGen")
- iphone dev 入门实例7:How to Add Splash Screen in Your iOS App
- How to Fix Memory Leaks in Java