I have the following questions about allocating properties in Objective-C,
if I have the following property:
#property (nonatomic, retain) NSArray *arr;
#synthize arr=_arr;
should I allocate this array like:
arr=[[NSArray alloc]init];
and if I should to, where is the best place to to allocate it?
I know I should release it, but retaining a property will increase its retain count by 1 and allocating it will increase it by another 1, am I right.
You can allocate the array in two ways:
Set the ivar directly with a retained value, like this:
_arr = [[NSArray alloc] init];
Set the property with an autoreleased value like this:
self.arr = [NSArray array];
You can do this in your class's init method, although you've not said what superclass this is using so I'm not sure how the init method should look. If it's an NSObject, it will look like this:
- (id)init
{
if ((self = [super init]))
{
self.arr = [NSArray array];
}
return self;
}
But if it's a UIViewController, you'll need to use initWithNibName:bundle, like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundleOrNil
{
if ((self = [super itWithNibName:nibNameOrNil bundle:bundleOrNil]))
{
self.arr = [NSArray array];
}
return self;
}
Then you need to release it in your dealloc method, like this:
- (void)dealloc
{
[_arr release];
[super dealloc];
}
However, you should bear in mind that NSArrays can't be changed once they're created, so you probably either want to initialise it with some objects, like this:
self.arr = [NSArray arrayWithObjects:object1, object2, nil];
Or if you want to add objects to it later, you should define it as an NSMutableArray, like this:
#property (nonatomic, retain) NSMutableArray *arr;
And init it with:
self.arr = [NSMutableArray array];
That will let you add objects to it later.
By the way, if you get errors in your dealloc method, it probably means that your project is using ARC, which is a new technology in iOS 5 that means you don't need to write release and retain statements any more.
If you aren't using ARC I suggest you do so because it will save you having to worry about this retain/release stuff - any iOS developer who is just starting out now should really use ARC for every project, but because it was only introduced in iOS 5, a lot of the Objective-C books don't cover it.
Related
I am using following code to create NSMutableArray. When I run the same in “Profile” mode, it is showing a memory leakage.
SampleArray.h
#interface SampleArray: NSObject {
}
#property (assign, retain) NSMutableArray *array;
#end
SampleArray.m
#import "SampleArray.h"
#implementation SampleArray
#synthesize array;
-(void) viewDidLoad {
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[self.array release];
}
#end
When I am using autorelease, then I can’t able to access the same in other function or method and return null value. Please help me to find the issue.
releasing this array in viewWilLDisappear is not a good idea, you should release in the dealloc function. You should worry about over-releasing this item and causing a program crash since viewWilLDisappear may get called multiple times during the lifetime of this ViewController.
Anyhow, you are double retaining the item beacuse your property has a retain on it (and make it nonatomic, not assign), add an autorelease to your alloc/init:
self.array =[[[NSMutableArray alloc] init] autorelease];
and move
[array release];
to your dealloc function. Or convert to ARC and don't worry any longer...
Try setting it to (nonatomic, retain), then autoreleasing.
It is better to handle memory de-allocation in your -dealloc() and set your array to nil to be more secure in your -viewDidUnload()
so it will be:
-(void) viewDidUnload
{
self.array = nil;
}
-(void) dealloc
{
[array release];
[super dealloc];
}
and like other people said, declare your property as (nonatomic, retain) instead of (assign, retain)
First of all I'm assuming that you are using
#property (nonatomic, retain) NSMutableArray *array;
use this
-(void) viewDidLoad {
array =[[NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[array release];
}
I will recommend you to use dealloc instead of viewWillDisappear
-(void) dealloc {
[array release];
[super dealloc];
}
Explanation of your code
-(void) viewDidLoad {
// here you are allocating a mutable array thus retain count becomes one
// then you are assigning it to the property which is retain and thus retains it
// making the retain count 2
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
// here you are releasing it so its retain count becomes 1 from 2
// thus shows memory leak
[self.array release];
}
I created a property for the NSArray which creates a getter/setter. I know Apple recommends using the instance variable in the init and dealloc method. I'm trying to figure what to do in the following code.
(1) Do I need an extra release statement? Wouldn't array have a retain count of 2 then 1 with the dealloc, leave a leak. Or would autorelease take care of this?
(2) Is there some way in xCode or instruments to follow a specific variable to see its retain count going through the process.
#property (nonatomic, retain) NSArray *array;
#synthesize arrary = _array;
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
initWithArray:(NSArray *)array
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_array = [[NSArray alloc] initWithArray:array];
}
return self;
}
- (void)dealloc
{
[_array release];
[super dealloc];
}
(1) Do I need an extra release statement? Wouldn't array have a retain count of 2 then 1 with the dealloc, leave a leak. Or would autorelease take care of this?
Let's step through this:
#property (nonatomic, retain) NSArray *array;
// The setter in this case will do the proper ref counting:
#synthesize arrary = _array;
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
initWithArray:(NSArray *)array
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// _array is nil at allocation
_array = [[NSArray alloc] initWithArray:array]; // << self holds one reference
}
return self;
}
- (void)dealloc
{
[_array release]; // << self holds zero references
[super dealloc];
}
In other words, you need nothing more. Personally, I would just use copy in the initializer and for the property (as opposed to retain).
(2) Is there some way in xCode or instruments to follow a specific variable to see its retain count going through the process.
Yes. There are several runtime shortcuts and exceptions to this, however.
The easiest way is to just run instruments with the allocation instrument, then enable reference count recording. It will then record the backtrace and time for every ref count of every individual object.
Your code looks ok to me. You shouldn't have to put an extra release. In your init method, the retain count for _array is 1. After you release in dealloc, you should do _array = nil, to avoid dangling pointers.
To check for leaks you can do either or both of the following:
(i) CMD + Shift + B ---> This runs analyze on your code to spot issues like this.
(ii) Use the "Leaks" and "Allocations" tools in "Instruments". Here you can watch the allocations and leaks (if any) that your application has while executing. It is very useful, and sometimes catches things that "Analyze" doesn't catch.
Edit: Changed to conform to Apple's recommendations. The comments that follow this answer refer to my original (and very bad) version. In future, I will pay proper attention to Practical Memory Management in the iOS Developer Library.
Question (1): No, you don't need an extra release. After [[ASArray alloc] initWithArray:array], your array will have a retain count of 1. In dealloc, the call to [_array release] will return it to 0, and it should be freed.
If [setArray:] is called in the meantime, it will obey its property declaration of retain by retaining the new object and releasing the old one.
If some other object or method retains your array, it is their responsibility to release it. If your array's retainCount is greater than 1 at the start of dealloc, some other object is retaining it, at least temporarily.
Question (2): You can check retainCount at run time, but as the NSObject Protocol Reference warns, it's easy for your object to end up in multiple autorelease pools, making its count meaninglessly high.
Bonus: You may be thinking that [[NSArray alloc] initWithArray:array] will use the retain count of its input and add 1. This only happens if the "copy" is made by retaining and passing a reference to an immutable source, in which case some other object is also retaining a reference to that source. [[NSString alloc] initWithString:] detects if its source is immutable and does this; [[NSArray alloc] initWithArray:] does not. [NSArray copy], on the other hand, will retain and return itself.
These optimizations are of course implementation-specific and should never be assumed. However, it can be educational to construct some objects, avoiding autorelease, and follow their addresses and retainCounts, like this:
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a", nil];
// Do not just assign array1 to [[NSArray alloc] init], or you'll get a singleton empty array with a high retain count.
// Make array1 mutable, and array2 should be copied differently.
NSLog(#"Constructed array1");
NSLog(#"array1 address = %p retainCount = %d", array1, [array1 retainCount]);
NSArray *array2 = [array1 copy];
// Use [[NSArray alloc] initWithArray:] and the results may be different.
NSLog(#"Constructed array2");
NSLog(#"array1 address = %p retainCount = %d", array1, [array1 retainCount]);
NSLog(#"array2 address = %p retainCount = %d", array2, [array2 retainCount]);
[array1 release];
NSLog(#"Released array1");
NSLog(#"array2 address = %p retainCount = %d", array2, [array2 retainCount]);
[array2 release];
#interface foo: NSObject
#property (nonatomic, retain) NSMutableArray *aMutableArray;
#end
#implementation
#synthesize aMutableArray
-(void)somefunction {
// Illustration
self.aMutableArray = [[[NSMutableArray alloc]init]autorelease];
self.aMutableArray = [[[NSMutableArray alloc]init]autorelease];
self.aMutableArray = [[[NSMutableArray alloc]init]autorelease];
}
#end
I have done code similar code to this in other parts of my program, but I needed to be certain that this does not cause a memory leak. With my understanding of autorelease, this object is released correctly right?
[EDIT - added question]
One question though: the property above has a retain attribute, so when the compiler creates the setter function, the setter code will look something like this:
somecode..
retain newObj
release oldObj
somecode..
in the code above, I assigned 3 objects to aMutableArray.
Each time they are assigned, the setter function did a retain on the newObj and a release on the oldObj. So, since the setter method already did a release, will there be a problem when the autorelease kicks-in to release the object a second time?
Yes, it will be released correctly if you also release it dealloc method:
- (void) dealloc{
[aMutableArray release];
[super dealloc];
}
Note also that you can shorten your code using equivalent convenience +array method of NSMutableArray:
self.aMutableArray = [NSMutableArray array];
I'm working on an iphone application and having some trouble with memory leaks. I've read some docs about garbage collection that it make it sound simple but I must be missing something. I've got a viewController that needs access to an array which may need to repopulated from time to time. Here is a simplified version of what I have:
//AppDelegate.m
- (NSMutableArray *)getMathFacts {
//Some database stuff
NSMutableArray * arr = [[NSMutableArray alloc] init];
while (sqlite3_step(math_fact_statement) == SQLITE_ROW) {
[arr addObject:[[NSNumber numberWithInt:sqlite3_column_int(math_fact_statement, 0)] autorelease]];
}
return arr;
}
//ViewController.h
#interface ReviewViewController : UIViewController {
NSMutableArray *reviewArr;
}
#property (retain, nonatomic) NSMutableArray *reviewArr;
//ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[self loadMathFacts];
}
- (void)loadMathFacts {
self.reviewArr = [appDelegate getMathFacts];
}
- (void)loadAllMathFacts {
self.reviewArr = [appDelegate getAllMathFacts];
}
-(IBAction) getAll {
[reviewArr release];
[self loadAllMathFacts]
}
GetAllMathFacts is similar to getMathFacts, it just has a different SQL statement.
When I run this checking for Leaks it is like a sieve. It seems like something simple, but I feel like I've tried everything and it just moves the leak around.
Any advice would be appreciated.
Thanks
The iPhone OS actually doesn't have garbage collection. What you're doing with retain/release is called reference counting.
The solution to your problem is probably to make getMathFacts return an autoreleased object (change return arr; to return [arr autorelease];), because the definition of the property reviewArr is probably something like #property (retain) NSArray *reviewArr;, which means every time you call self.reviewArr = something;, something is retained, which means after you set reviewArr in loadMathFacts and loadAllMathFacts, reviewArr is retained one time too much.
In getMathFacts, you do a
NSMutableArray * arr = [[NSMutableArray alloc] init];
that array is owned by you. It has a retain count of 1. Later when
- (void)loadMathFacts {
self.reviewArr = [appDelegate getMathFacts];
}
that same array is now retained by reviewArr and the retain count goes to 2.
When you then do a
-(IBAction) getAll {
[reviewArr release];
[self loadAllMathFacts]
}
in the first release statement, your array is now getting released once and the retain count goes to 1. In [self loadAllMathFacts]
- (void)loadAllMathFacts {
self.reviewArr = [appDelegate getAllMathFacts];
}
self.reviewArr will release the the array, before retaining a new array. After this release the retain count is down to 0. I don't see a leak here. Maybe in -getAllMathFacts?
Now, one thing I would change to make things look a little better is this:
- (void)loadMathFacts {
NSMUtableArray array = [appDelegate getMathFacts];
self.reviewArr = array;
[array release];
}
- (void)loadAllMathFacts {
NSMUtableArray array = [appDelegate getAllMathFacts];
self.reviewArr = array;
[array release];
}
-(IBAction) getAll { // you don't need to release in here
[self loadAllMathFacts]
}
Plus you should rename your get method calls to use "new" instead of "get" since the convention is that if you return something that is owned by the caller, it should have new or copy in the name of the method.
As another answerer said, you could use autorelease, although I'd rather use autorelease in other situations. But if you were to do it with autorelease this is how I'd do it:
//AppDelegate.m
- (NSMutableArray *)getMathFacts {
//Some database stuff
NSMutableArray * arr = [[NSMutableArray alloc] init];
while (sqlite3_step(math_fact_statement) == SQLITE_ROW) {
[arr addObject:[[NSNumber numberWithInt:sqlite3_column_int(math_fact_statement, 0)] autorelease]];
}
return [arr autorelease];
}
do the same with -getAllMathFacts. You should still change the code to be more like above, except you don't have to release after doing the self.reviewArray, and you don't have to change the name of the methods. What you have to remember is that if even autoreleased objects can leak if you call retain on them and then forget about them. The nice thing about autorelease is that the object is kept around for the duration of a runloop, long enough to hand it to some other object and let them decide whether they want to retain it or let it expire.
I hope I haven't missed anything. Go through my logic, and feel free to poke holes in it or ask questions. I can easily have missed something.
By the way, self.reviewArr when you have:
#property (retain, nonatomic) NSMutableArray *reviewArr;
does the following:
- (void)setReviewArr:(NSMutableArray*)array
{
NSMutableArray* oldReviewArr = reviewArr;
reviewArr = [array retain];
[oldReviewArr release];
}
As of Xcode 3.2 there is support for running the Clang analyzer. To do this choose Build->Build & Analyze. This will run the analyzer which is really a wonderful tool for finding reference counting issues.
For Xcode prior to 3.2, this might help: Using Clang Static Analyzer from within XCode
I'm writing an iPhone app. I have a header file that looks like this:
#interface EditTagsViewController : UITableViewController {
NSMutableArray *allTags;
NSMutableArray *selectedTags;
NSInteger currentFavorite;
}
#property (nonatomic, retain) NSMutableArray *allTags;
#property (nonatomic, retain) NSMutableArray *selectedTags;
#property (nonatomic) NSInteger currentFavorite;
#end
In the implementation file, my viewDidLoad method looks like this:
- (void)viewDidLoad {
NSMutableArray *aTags = [[NSMutableArray alloc] initWithArray:[Tag findAllTags]];
self.allTags = aTags;
[aTags release];
NSMutableArray *sTags = [[NSMutableArray alloc] initWithArray:[Tag findByFavoriteId:currentFavorite]];
self.selectedTags = sTags;
[sTags release];
UIBarButtonItem *add = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewTag:)];
self.navigationItem.rightBarButtonItem = add;
[add release];
[super viewDidLoad];
}
Here is my dealloc method:
- (void)dealloc {
[allTags release];
[selectedTags release];
[super dealloc];
}
What's confusing to me is that when I run the app both in the simulator and on the device itself, using Instruments (memory leaks), it tells me that this line in my viewDidLoad method is leaking an array:
self.selectedTags = sTags;
It's confusing because I'm using the exact same technique with 2 different variables, and yet no leak is reported with the first one.
I feel like I'm missing something obvious here. Any ideas?
Your code looks correct to me. Is it possible that one of [Tag findAllTags] or [Tag findByFavoriteId:] is leaking? Are you making sure to set self.allTags and self.selectedTags to nil in dealloc?
Be mindful of the difference between saying self.allTags = ... and allTags = .... Because allTags is a property and has the retain attribute, whenever you assign via self.allTags = ..., it implicitly calls the setter method [self setAllTags:...], which invokes retain on the new value and release on the old value (if any). You're doing it correctly in this code sample, but if elsewhere you're assigning straight to allTags (without the self.), you're not releaseing the old value, which may be the source of the leak. Likewise for selectedTags.
Have a look at findByFavoriteId is there a retain there? That is the only difference I can see between the aTags and sTags are used in your example