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];
Related
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.
i have the following code in .h:
#property (nonatomic, retain) NSArray *arrayData;
And in the .m in method initWithNibName:
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
is it right in order to call
[self.arrayData release]
safely in order to release the object?
No, it is not correct to call release on your property. The problem with it is, that you release your property, it will get deallocated, but you didn't set your pointer to nil, so somebody might send a message to your property and get a crash.
What you can do is the following:
self.arrayData = nil; ( which will release the previous saved instance, and set the property to nil)
[arrayData release]; arrayData = nil; (here you are accessing your ivar instead of your property; setting your ivar to nil is a precaution)
[self->arrayData release]; self->arrayData = nil (this is exactly the same as #2)
Hope this helps.
You need to call:
[arrayData release]
Calling [self.arrayData release]; will not have the effect you want it to in either case.
If you're wondering why this is, check this question out: difference between [self.property release] and [property release]
A)
it is a bad idea to do this in your initializer (e.g., initWithNibName:bundle:)
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
use this instead:
arrayData = [[NSArray alloc] initWithObjects:#"Usuario:",#"Password:",nil];
you should not call these accessors (properties) in initializers or dealloc.
B)
is it right in order to call
[self.arrayData release]
no. in many cases (assuming you implement some of the properties you've declared), you may not be returned the the ivar. you may receive a copy, a placeholder object, or a subclass may have chosen to re-implement the accessors (as some examples). in these cases, it's easy to over-release or over-retain (resulting in evil stuff, like leaks and crashes).
this is typical:
self.arrayData = nil;
unless you are in dealloc of the object which declared the ivar:
- (void)dealloc {
[arrayData release], arrayData = nil;
[super dealloc];
}
I've read a few posts on this, but there's still one thing that's not clear for me. I know this might be rather a n00b question, but I've actually got rather far into development without quite grasping this fundamental issue. A symptom of being self taught I guess.
You declare a variable in your header, like so:
#interface SomeClass : NSObject {
NSMutableArray *anArray;
}
#property (nonatomic, retain) NSMutableArray *anArray;
end
And then in your main file you synthesise it and set it to an initial value:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];
And release it when your Class deallocs:
- (void)dealloc {
[anArray release];
[super dealloc];
}
Now, when I run instruments, the line
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
is identified as a memory leak. Is it a memory leak because when you define the variable anArray in the header it allocates memory? (Because I thought it was a null pointer.) Therefore when you want to initialise it, and you call [[NSMutableArray alloc] initWithCapacity:10], you are reallocating the memory, and losing the pointer to the original allocation?
So instead, I use the convenience class method:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [NSMutableArray arrayWithCapacity:10];
}
[return self];
This is no longer identified as a memory leak in instruments. And since it's a convenience method, anArray is autoreleased. However, if I am to assume that the instance declaration in the header allocates memory, which would explain the previous issue, then should I still release anArray? Does setting the initial values in this way retain it perhaps?
I understand the difference between
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];
and
NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];
but what I'm not sure I understand is when you've declared NSMutableArray *anArray in your header, which of the two approaches you should use and why. And whether or not if you use the second approach, you should still release anArray when you call dealloc.
I might add that I've found the following posts/links useful:
Suggest the best way of initialization of array ( or other objects )
What is the cost of using autorelease in Cocoa?
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
What is the difference between class and instance methods?
alloc'ing an object starts it off with a reference count of 1.
Setting a property that has the 'retain' attribute also increases the reference count.
So, that means this is usually bad:
#property (nonatomic, retain) Object * variable;
...
self.variable = [[Object alloc] init];
Because variable now has a reference count of 2.
When setting a object's member variable, just do this:
variable = [[Object alloc] init];
You should also realize that this works
self.anArray = [NSMutableArray arrayWithCapacity:10];
Because "arrayWithCapacity" (and other similar factor methods) autoreleases the object it returns, so after you set the property, it essentially has a reference count of 1.
It's not the instance that allocates the memory. You're right to assume that in Objective-C (at least on all Apple-based operating systems), newly initialized classes have all their ivars set to 0 (or nil or NULL as appropriate).
The problem you're seeing is that you're using the property, not the ivar in your initialization. Since you declared your property as retain, using the property accessor to set it automatically retains it.
So, when you initialize you either have to take ownership and set the ivar directly, or do like you're doing and use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own or, as you did in your second instance, using the convenience constructor so that you never owned the returned instance).
So just remember, if you ever use the property accessors, even within the class itself, you will get the features you set on the property (e.g., nonatomic, retain, etc.). You use the property accessors whenever you do one of the following:
// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:#"anArray"];
You would access your ivar directly like:
anArray = something; // in this case you must take ownership
I'm getting this error when trying to see the contents of a NSMutableArray:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000021
0x94d5a688 in objc_msgSend ()
ViewController.h:
#interface PeopleViewController : UITableViewController {
NSMutableArray *people;
}
#property (nonatomic, retain) NSMutableArray *people;
ViewController.m:
#implementation PeopleViewController
#synthesize people;
In viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// initialize our people array with an autoreleased object
people = [NSMutableArray array];
... Populate the people array with Person objects.
}
When I'm at the point where I'm modifying the contents of a cell in the tableview, I'm unable to access the people array in gdb when typing 'po self.people':
Person *person = [[Person alloc] init];
person = [self.people objectAtIndex: indexPath.row]; // <--- 'po self.people' called
cell.textLabel.text = person.personName;
Any ideas why I can't access it?
The line
people = [NSMutableArray array];
returns an autoreleased array that will be released on the next iteration of the current run loop. You should retain that:
people = [[NSMutableArray array] retain];
and of course release it in your dealloc method.
However: Apple engineers have often mentioned in conferences to avoid autoreleased instances like this whenever possible in the iPhone, for performance reasons. Try using alloc/init instead:
people = [[NSMutableArray alloc] initWithCapacity:1];
with the corresponding release in the dealloc method. In this case you don't even need to retain (init returns an instance with a retain count of 1, which is what you need).
And justin's comment is correct: you should do this instead:
Person *person = [people objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
and this should work.
is indexPath.row > [people count]?
Also, why are you doing this:
Person *person = [[Person alloc] init]
You're allocating memory, and then pointing to completely different memory.
You can avoid having to fuss with retaining properties by using the self notation to call the accessor and setter methods created by the #synthesize directive.
When you set the people property directly in viewDidLoad it sets the property but does nothing for memory management. However, if you set it with self.people you actually call the synthesized setter method that because of the retain setting of the #property directive will automatically retain the assigned array.
As an aside, I would recommend always using -[NSMutableArray initWithCapacity:] instead of a bare init. It is the actual initializer for the class. You can call it with just '1' if you don't know how big it will be. In the past, I have seen odd problem arise from just using bare init.
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