I have the following 2 code snippets.
Assume I have a Parent class and in Parent.h class I have
#property (retain) NSMutableArray *childrens;
and I have synthesize it in the .m file correctly.
assume in Parent.m file
-(void) dealloc
{
[childrens release];
[super dealloc];
}
In another class I declare like this.
1.
Parent *p = [[Parent alloc] init];
p.chidlrens = [[NSMutableArray alloc] init];
// do some other stuff
2.
Parent *p = [[Parent alloc] init];
NSMutableArray *childArray = [[NSMutableArray alloc] init];
p.childrens = childArray;
[childArray release];
From above 2 methods is there a leak in method 1?
Yes, there is a leak in method 1. You alloc a NSMutableArray but don't release it.
While not answering your question, I'd recommend having Parent init and alloc the array in it's Init method, so your code elsewhere doesn't have to worry about checking if it's already created and doing it. Then you don't need to have the classes that use Parent worry about Parent's memory management - Parent can do it.
the general rule is to release anything your doing alloc, init on.
but do a build + analyze to get some 'potential' leaks.
if you want to be 100% sure, run it though leaks
To check where is leakage you can without somebody help. For example, use any plugins for that. I use deleaker and know exactly where is a memory leak.
Related
I have a property in my class, which is an NSArray. I am retaining the property.
My question is, what is the proper way to add objects to that array without leaking and making the retain count too high?
This is what I am using:
.h:
NSArray *foodLocations;
#property (nonatomic, retain) NSArray *foodLocations;
// I make sure to synthesize and release the property in my dealloc.
.m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *tempFood = [[NSArray alloc] initWithArray:[self returnOtherArray]];
self.foodLocations = tempFood;
[tempFood release];
}
Is this the correct way to do it?
Yes this is correct and my preferred way of doing it as it renders the code more readable.
You are essentially allocating a temporary array and then assigning it to your property with a retain attribute, so it is safe to dealloc it as your property now "owns" it. Just remember that you still need to release it in your dealloc method.
You could also initialise the array and assign it to the property in the view controllers init method, depending on whether you need the property to be available to you before the view actually loads (i.e. in case you want to read the value of the property before pushing the view controller etc...)
you will typically want to declare the property copy in this case.
in most cases, immutable collection accessors should be copy, not retain. a lot of people get this wrong, and end up writing a lot of copying manually and sharing objects which should not be shared, thinking they are doing themselves good by cutting a corner.
copying in this form (the collection) is shallow. the objects in the array are not copied, just the array's allocation.
a good implementation of an immutable collection can simply implement copy by retaining self. if the argument is mutable, you want a copy anyhow (in the majority of cases).
your program is then simplified to a declaration of:
// note: copy, not retain. honor this if you implement the accessors.
#property (nonatomic, copy) NSArray * foodLocations;
and then the setter:
self.foodLocations = [self returnOtherArray];
of course, you must still init, dealloc, and handle thread-safety appropriately.
good luck
That looks fine. You don't actually need the tempFood variable, you can just do:
self.foodLocations = [[NSArray alloc] initWithArray:[self returnOtherArray]];
[self.foodLocations release];
or:
self.foodLocations = [[[NSArray alloc] initWithArray:[self returnOtherArray]] autorelease];
Or:
#synthesize foodLocations=_foodLocations;
then in code
_foodLocations = [[NSArray alloc] initWithArray:someOtherArray];
This avoids the autorelease required by
self.foodLocations = [[[NSArray alloc] initWithArray:someOtherArray] autorelease];
Yes, that is correct. Also good to keep in mind is what #synthesize is, in effect, doing for you. A synthesized (& retained) setter is functionally equivalent to the following code:
- (void)setVar:(id)_var {
[_var retain];
[var release];
var = _var;
[var retain];
[_var release];
}
So, basically, every time you call self.var = foo, it releases the previously stored value and retains the new one. You handle the reference counting in your code, and the setter handles its own.
I have a mutableArray that I fill up with objects. When I try to refill the array, I first use removeAllObjects - which produces a memory leak...
The properties of the object are synthesized, retained and released on dealloc.
The Array is initialized on viewDidLoad like this:
theArray = [[NSMutableArray alloc] initWithCapacity:10];
... and it's retained and synthesized. (#property (nonatomic, retain) NSMutableArray *theArray)
I'm adding the objects in a while-loop like this:
myObject *theObject = [[myObject alloc] init];
theObject.someProperty = #"theprop";
[theArray addObject: theObject];
[theObject release];
then on the next call of the method, I remove all objects like this:
[theArray removeAllObjects];
That's where the leak occurs. If I comment this line out, the leak doesn't appear. So I guess I'm doing something wrong in my object?
Seems like the problem is solved...
a) I didn't realize, that when I use instruments, the app isn't compiled before launch - thus, some of the changes I made were not taking into effect, when using instruments. So now I first build and run after a change and then run it in instruments.
b) thus, I don't really know what solved the problem. But it might be that I had the dealloc-method in my object wrong.
I was using:
[super dealloc];
[myProperty release];
instead of the other way around:
[myProperty release];
[super dealloc];
Thanks for the help, though!
Does myObject have any retained properties? If so, are you setting them to nil in the dealloc message? If not, when it is dealloced it won't release the objects that its properties are set to.
Is this proper memory management? What I'm wondering is if I am supposed to release after the alloc in -viewDidLoad.
SomeViewController.h
#import <UIKit/UIKit.h>
#interface SomeViewController : UIViewController {
UIButton *someButton;
NSString *someString;
}
#property (nonatomic, retain) IBOutlet UIButton *someButton;
#property (nonatomic, copy) NSString *someString;
#end
SomeViewController.m
#import "SomeViewController.h"
#implementation SomeViewController
#synthesize someButton, someString;
- (void)viewDidLoad {
[super viewDidLoad];
someButton = [[UIButton alloc] init];
someString = [[NSString alloc] init];
}
- (void)viewDidUnload {
self.someButton = nil;
self.someString = nil;
}
- (void)dealloc {
[someButton release], self.someButton = nil;
[someString release], self.someString = nil;
[super dealloc];
}
#end
Thanks
Edit: one more thing. If I place a UIButton in IB, do I still need to alloc it?
It's quite a long story which you may find by googling around, and someone may enter here, but to keep it short, here's a few tweaks:
- (void)viewDidLoad {
[super viewDidLoad];
self.someButton = [[[UIButton alloc] init] autorelease];
self.someString = [[[NSString alloc] init] autorelease];
}
I.e. use the setters (self.something = ...;) and always either release or autorelease any alloc you do. (The logical distinction would be who "owns" the objects; with these tweaks the function gives up ownership and the class gets it.)
edit: no, if you create a button in IB, the button will just be there, allocated and initialized with your styles.
When you call init on an object, the retain count goes to 1. You have two different setter attributes: one is "retain" for your UIButton, and the other is "copy" for your NSString. When you call
self.someButton = someUIButtonObject;
someUIButtonObject gets a retain message and so its retain count goes up to 1. In the case of your original code, calling release in dealloc will release one reference to someUIButtonObject, but it will still have a retain count of 1 and will thus be leaked.
The other case with your NSString has a different problem but still leaks memory. Here your call to [[NSString alloc] init] results in a new string object, and then calling
self.someString = someNSStringObject;
results in the creation of a brand new object which copies the content of someNSStringObject. In this case, someNSStringObject and the setter's copied object each have a retain count of one. Here, you leak the string you alloc init-ed because you no longer have a reference to it and it goes out of scope with a retain count of one.
A quick side note: I don't know what your actual code looks like, but don't forget that NSStrings are immutable (so just calling [[NSString alloc] init] is pretty useless) and UIButton needs a frame (try [[UIButton alloc] initWithFrame:(CGRect)frame];).
Basically you need to match each call to retain, copy, or alloc with a call to release (or autorelease). It is appropriate so use
self.someButton = [[[UIButton alloc] init] autorelease];
Which will release the object, though at some unknown time in the future. Don't use autorelease if your memory is very tight and you need memory ASAP. In that case you would do:
UIButton* tempButton = [[UIButton alloc] init];
self.someButton = tempButton;
[tempButton release];
which guarantees that you don't have large objects waiting around in your autorelease pool.
Also, always use the getters/setters to access these properties (self.someButton as opposed to someButton). That way you don't accidentally assign someButton to a new pointer and leak the old one.
When you create a button in IB, the XIB holds a reference to the button (+1 retain count). If you create an IBOutlet so you can access the button programmatically, you get another retain on the object, so if you have an IBOutlet to someButton, someButton has a retain count of 2 as soon as the XIB is loaded. You do not need to alloc the object, that is done automatically when the XIB is loaded into memory. Also, you are only responsible for releasing one reference to the button (your IBOutlet reference). In general, it is a good practice to release your reference as soon as you know you no longer need it. For example, if you know you need a button and have to do initial customization and nothing else, then you would probably do something like this:
-(void)viewDidLoad {
// Do some customization of someButton
[someButton release];
}
Otherwise you would probably release someButton in viewDidUnload since you know at that point you won't need the reference to the button anymore.
If your button is set in IB, you do not need to allocate it in the code (IB does it for you).
Basically, each object you created using alloc or new, should have a release somewhere in your code. If it's an interface variable, you should release them in the dealloc function.
This is what Instruments is pointing to.
students = [[NSMutableArray alloc] initWithArray:[course.students allObjects]];
I'm releasing the array in dealloc. In the rest of my code I'm only calling the array and I'm not alloc'ing it again. I've also tried filling the array via fast enumeration and I get the same problem.
Just to be sure, add an autorelease to it, like
students = [[[NSMutableArray alloc] initWithArray:[course.students allObjects]] autorelease];
Then see what happens. (maybe assign it to self.students btw, and make that a retained property using #property (nonatomic,retain))
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.