This question already has answers here:
app crash in the dealloc
(2 answers)
Closed 9 years ago.
I'm facing small problem,
I declare an array in .h file and allocate it in viewDodLoad method. In dealloc method I check if array not equal to nil then array=nil. But it's crashing in iOS 5.1.1. I can't understand reason for this crash.
My code,
#interface SampleApp : UIViewController
{
NSMutableArray *objArray;
}
#end
#implementation SampleApp
- (void)viewDidLoad
{
[super viewDidLoad];
objArray=[[NSMutableArray alloc]init];
}
-(void)dealloc
{
[super dealloc];
if (objArray!=nil)
{
[objArray removeAllObjects];
[objArray release];objArray=nil;
}
}
Add [super dealloc]; at the end of dealloc method and not at beginning. It is recommended by Apple in its documentation for dealloc method.
When not using ARC, your implementation of dealloc must invoke the superclass’s implementation as its last instruction.
Modify your code as below,
-(void)dealloc
{
if (objArray!=nil)
{
[objArray removeAllObjects];
[objArray release];objArray=nil;
}
[super dealloc];
}
Also, you need not call [objArray removeAllObjects] when you are releasing the entire array. When array is released, internally it will call release on all contained objects.
Hope that helps!
[super dealloc] method must be call in end of this method. Because you can not access variables of the superclass anymore because they are released when you call [super dealloc]. It is always safe to call the superclass in the last line.
-(void)dealloc
{
// ----------- your stuff ------------
[super dealloc];
}
With manual memory management, your -dealloc method looks as follows:
-(void)dealloc
{
[objArray release]; // objArray *may* be nil, and this is
// sufficient to release all elements as well.
// call super at the end
[super dealloc];
}
Additionally, you have a potential memory leak in your method -viewDidLoad. If you do it like your example:
- (void)viewDidLoad
{
[super viewDidLoad];
objArray=[[NSMutableArray alloc]init];
}
you may assign objArray a new pointer even if objArray already holds a valid object. The new pointer value will simply override the old, and thus you cannot release the old anymore.
One way is to check whether objArray is not nil and then release it before assigning it a new value:
- (void)viewDidLoad
{
[super viewDidLoad];
if (objArray) {
[objArray release], objArray = nil;
}
objArray = [[NSMutableArray alloc]init];
}
The better approach however is to employ "lazy initializing properties":
First, define an "internal property" for your array (unless you want the array publicly accessible). In your .m file:
// In your implementation file define a private property in a class extension:
#interface SampleApp ()
#property (nonatomic) NSMutableArray* objArray;
#end
#implementation SampleApp
#synthesize objArray = _objArray; // this will create the setter
-(void)dealloc
{
[_objArray release];
[super dealloc];
}
// Lazy init property: (this is the getter)
- (NSMutableArray*) objArray {
if (_objArray == nil) {
_objArray = [[NSMutableArray alloc] init];
}
return _objArray;
}
- (void) viewDidLoad {
[super viewDidLoad];
// When needing the array, simply *and always* access it
// through the property "self.objArray":
NSUInteger count = [self.objArray count];
}
...
Lazy initialization of properties are quite handy. Basically, you won't worry anymore if they are initialized or not - they simply are, when you use the property accessor.
Related
I have a small confusion. When I print my array in tableView dataSource method numberOfRowsInSection , my App crashes.
This is my code : in .h file
#interface AddColor : UIViewController<UITableViewDataSource,UITableViewDelegate>
{
UITableView *tblView;
NSArray *arrayColors;
}
#property(nonatomic,retain)NSArray *arrayColors;
#end
In .m file
#synthesize arrayColors;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:NO];
arrayColors = [NSArray arrayWithObjects:#"blackColor", #"darkGrayColor", #"lightGrayColor", #"whiteColor", #"grayColor", #"redColor", #"greenColor", #"blueColor", #"cyanColor", #"yellowColor", #"magentaColor", #"orangeColor", #"purpleColor", #"brownColor", nil];
tblView=[[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 460) style:UITableViewStylePlain];
tblView.delegate=self;
tblView.dataSource=self;
[self.view addSubview:tblView];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSLog(#"%d",[arrayColors count]);//App crashes here.
return [arrayColors count];
}
My app crashes when I print [arrayColors count];
I find the solution for crash, I simply retain the array in viewDidLoad
[arrayColor retain];
and now working fine. but why my app crashes before when I print the [arrayColor count];
+ [NSArray arrayWithObjects:]
creates an autoreleased array - it's unsepcified when it is released, but most likely it's released when the viewDidLoad method returns, so it's an invalid (garbage) pointer that you access in [arrayColor count].
By retaining the array, you get rid of the deallocation error, but now you're leaking memory. The general approach of solving this the right way is to allocate and initialize the array in one of the initializer methods, like
- (id)init
{
if ((self = [super init])) {
arrayColors = [[NSArray alloc] initWithObjects:..., nil];
}
return self;
}
and then purge it in - dealloc in order not to leak memory:
- (void)dealloc
{
[arrayColors release];
[super dealloc];
}
More on the topic on Apple Developer.
Try using [self.arrayColors count] - use the ivar getters and setters to access the retained property
As an aside, I would strongly advise against hardcoding the size of your tableview. At the very least set the autoresizing mask.
You might've linked the DataSource and Delegate to your TableView instead of the File Owner.
if you do this,
[self setArrayColors:[NSArray arrayWithObjects:#"blackColor", #"darkGrayColor", #"lightGrayColor", #"whiteColor", #"grayColor", #"redColor", #"greenColor", #"blueColor", #"cyanColor", #"yellowColor", #"magentaColor", #"orangeColor", #"purpleColor", #"brownColor", nil]];
it should fix the problem without requiring another retain, but make sure you release it in the dealloc.
[arrayColors release];
For one of my custom classes, I have defined a method called initialize to set some instance variables at the same time as the init. The code is below. The analyzer tool is reporting a leak in viewDidLoad on the line with [[Employee alloc]..
Since I am releasing the variable in the dealloc, I thought that this should be fine..what could be the issue? thanks in advance.
#interface testViewController : UIViewController <UITextFieldDelegate>{
Employee *employee;
}
- (void)viewDidLoad {
if(employee ==nil)
employee = [[Employee alloc] initialize:#"John"];
if (![employee.entityName isEqualToString:#"Test"]) { //The leak is reported here for object allocated above
///...
}
}
- (void)viewDidUnload {
[super viewDidUnload];
employee = nil;
}
- (void)dealloc {
[super dealloc];
[employee release];
}
//In the Employee class
-(id) initialize:(NSString*) name{
self = [super init];
self.entityName = name;
return self;
}
In your viewDidUnLoad you need to release employee before it gets set to nil. Else in your dealloc, you are just releasing nil.
ie
- (void)viewDidUnload {
[super viewDidUnload];
[employee release];
employee = nil;
}
i use a UIView subclass with an NSMutableArray of other Views to indicate values as bars.
I init it in my initWithFrame. The Instruments tells after a few create and remove of my UIView subclass that there is a leak on the alloc of the NSMutableArray.
Thats why i framed it with the if to avoid multiple objects. But doesn't help
- (id) initWithFrame :(CGRect)frame
{
self = [super initWithFrame:frame];
if (self.uiValueSubviews == nil){
self.uiValueSubviews = [[NSMutableArray alloc]init];
}
return self;
}
- (void)dealloc {
[self.uiValueSubviews release];
[super dealloc];
}
Am i doing something wrong with the dealloc?
Thanks for your Help
Two problems I see with memory management involving your property.
Properties should always be set to
an autoreleased object or an
object you will be releasing on your
own.
Never send release directly to a property. I prefer to release underlying variable if possible (ex. [_uiValueSubviews release];)
Change the code to the following.
- (id) initWithFrame :(CGRect)frame
{
self = [super initWithFrame:frame];
if (self.uiValueSubviews == nil){
//Set to autoreleased array
self.uiValueSubviews = [NSMutableArray array];
}
return self;
}
- (void)dealloc {
//nil the value
self.uiValueSubviews = nil;
[super dealloc];
}
You should do like this :
- (id) initWithFrame :(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
if (self.uiValueSubviews == nil){
uiValueSubviews = [[NSMutableArray alloc]init];
}
return self;
}
- (void)dealloc
{
self.uiValueSubviews = nil;
[super dealloc];
}
You uiValueSubviews is probably a retain property so when you alloc, your retainCount is +1 and self. +1 too.
An other way, avoiding autoreleased objects, would be:
// ...
if (self.uiValueSubviews == nil)
{
NSMutableArray *uiValueSubviews_tmp = [[NSMutableArray alloc] init];
// maybe do something with uiValueSubviews_tmp
self.uiValueSubviews = uiValueSubviews_tmp;
[uiValueSubviews_tmp release];
}
// ....
As far as I know, that's how Apple does it in their examples.
I wanna ask if I allocated an instance variable for private use in that class, should i release it immediately on site, or i can depend on dealloc function. (because maybe i will need it on other function) ?
//Player.h
#interface Player : NSObject
{
NSMutableArray * objectArray;
}
- (void)awake;
- (void)add;
#end
//Player.m
#implementation Player : NSObject
{
-(id) init {
self = [super init];
if (self != nil ){
[self awake];
[self add];
}
return self;
}
- (void) awake {
objectArray = [[NSMutableArray alloc] init]; //is it cause leakage?
[objectArray addObject:#"foobar"];
}
- (void) add {
[objectArray addObject:#"foobar2"];
}
- (void) dealloc {
[objectArray release];
[super dealloc];
}
}
#end
or should i using property to set the objectArray iVar?
//Player.h
#interface Player : NSObject
{
NSMutableArray * objectArray;
}
#property (nonatomic,retain)NSMutableArray* objectArray;
- (void)awake;
- (void)add;
#end
//Player.m
#implementation Player : NSObject
{
-(id) init {
self = [super init];
if (self != nil ){
[self awake];
[self add];
}
return self;
}
- (void) awake {
self.objectArray = [[NSMutableArray alloc] init autorelease]; //cause leakage?
[objectArray addObject:#"foobar"];
}
- (void) add {
[objectArray addObject:#"foobar2"];
}
- (void) dealloc {
[objectArray release];
[super dealloc];
}
}
#end
if both of them doesn't cause a leakage, what type should i use?
should i always set iVar property, and access iVar value with self even if i only want to use it in this class?
I like to take the stance that if the instance variable should not be visible outside of the class then it should not be implemented as a property. But it's a personal thing that other developers may not agree with.
Either way you would need to release the objectArray in your classes dealloc method - which is what you're currently doing.
However you need to be careful with your awake method - if it's invoked multiple times then objectArray is leaked. This is the downside of not using properties. A use of self.objectArray = [[NSMutableArray alloc] init] here would have released the previous object.
In my opinion, you should only declare properties in your header if other objects are allowed to use them. There is no good reason why you would provide an -add: method (as in your example) that adds something to your array while also providing a getter for your array so other objects can manipulate it directly. It's called encapsulation.
If you do want to have the benefits of generated getters/setters for your implementation file, you can always use a class continuation (a nameless category) inside your implementation file and include your property declarations there. That way you get real, auto-generated properties that are only visible to your class' implementation.
Personally, I wouldn't use any getter or setter methods in your example. Just allocate the NSArray in your -init and release it in -dealloc. If this -awake method of yours might be called multiple times, just add an [objectArray removeAllObjects] call and you're sure to have an empty array without worrying about memory management.
It is very likely that memory will leak in your first example because you are not sending release to the previously set instance variable (if it already existed).
This is why you should use property setters - they handle all of this stuff for you.
Also, since you are obtaining ownership of the instance variable through the property (which is defined with the retain keyword), you will definitely leak memory if you don't send the instance variable the -release message in your -dealloc method.
So the verdict is that you should use the second example, not the first.
Usually when I create an object and assign it to an instance variable I alloc a temp object, call the iVar setter to retain the object and then release the temp object. However I was looking at init this morning and noticed that if I simply assign the iVar directly, its retained by the alloc whilst also being released correctly when either the setter is called or dealloc is executed. I am just curious if I am understand this correctly?
#property(nonatomic, retain) CLLocationManager *locationManager;
.
#synthesize locationManager;
// VERSION 001
- (id)init {
self = [super init];
if(self) {
CLLocationManager *tempManager = [[CLLocationManager alloc] init];
[self setLocationManager:tempManager];
[tempManager release];
}
return self;
}
// VERSION 002
- (id)init {
self = [super init];
if(self) {
locationManager = [[CLLocationManager alloc] init];
}
return self;
}
- (void)dealloc {
[locationManager release];
[super dealloc];
}
As far as the memory management goes both solutions are fine. But you might want to prefer the direct access in init and dealloc, see this related question.
Version 002 is the Apple approved answer because the pitfalls of using an accessor in init are theoretically worse. Basically, a subclass could choose to override your accessor and then you'd be sending a message to a subclass object that is not yet initialised.
However, everywhere else except init and dealloc, use version 001.