I have started to study Three20 and I have a simple question about TT_RELEASE_SAFELY
Up till now I like to write code in this way:
UILabel *lab = [[UILabel alloc] initWithFrame:rect];
[self.view addSubview:lab];
[lab release];
Here I think the main pool is responsible to free the memory of lab.
Now I have found TT_RELEASE_SAFELY which is defined like so:
#define TT_RELEASE_SAFELY(__POINTER) { [__POINTER release]; __POINTER = nil; }
As you can see, after release, it sets the object to nil.
I'd like to know the difference between the two ways and which way is better.
Thanks.
Sending a message to nil is valid in Objective-C. Sending a message to a deallocated object is not.
Sending a message to a deallocated object:
id obj = [[MyClass alloc] init];
[obj release];
[obj doSomething]; // Crash!
Sending a message to nil:
id obj = [[MyClass alloc] init];
[obj release], obj = nil;
[obj doSomething]; // Valid
Assigning nil to a variable after an object has been deallocated is controversial because it can prevent you from realizing that something is wrong. Sedate Alien's example:
[controlCenter dealloc];
...
float timeLeft = [controlCenter timeToWaitBeforeBombDetonation];
This code will crash since controlCenter has been deallocated. As a result this defect will be detected and fixed early.
[controlCenter dealloc], controlCenter = nil;
...
float timeLeft = [controlCenter timeToWaitBeforeBombDetonation];
This code will assign 0.0 to timeLeft which appears to be a valid wait time even though controlCenter is nil.
Take the above with a grain of salt, since if you are writing an Objective-C app, you are probably more concerned with keeping your users happy by avoiding crashes than destroying cities. If the latter is a concern, you should probably be using a type-safe language like Ada.
I believe that using these variants of "safe releases" is an expressly bad idea.
Your application will fail in silent and mysterious ways, as messages passed to nil will not raise any warnings. It's much better to not nil out your references and take advantage of all that NSZombieEnabled has to offer.
The only difference is that TT_RELEASE_SAFELY sets the pointer to nil after release, so the reference won't be used after release. The pattern is a good one to follow and the TT_RELEASE_SAFELY macro makes it simpler to implement.
Related
I am doing my project in xcode 4.2 (Older Version). For my application, I just set the variables, arrays in dto class for using in entire app lifecycle. so I set with a property like this.
AppDTO(sub class of NSObject)
AppDTO.h
#property(nonatomic,retain)anotherAppDTO *aAppDTO;
#property(nonatomic,retain)NSMutableArray *array1;
#property(nonatomic,retain)NSMutableArray *array2;
#property(nonatomic,retain)NSString *string1,*string2,*string3;
AppDTO.m
- (id)init
{
self = [super init];
if (self) {
self.aAppDTO = [[anotherAppDTO alloc]init];
self.array1 = [[NSMutableArray alloc]init];
self.array2 = [[NSMutableArray alloc]init];
self.string1 = #"Hello";
self.string2= #"Hai";
}
}
-(void)dealloc
{
if(array1 != nil)
{
[array1 release];
array1 = nil;
}
if(array2 != nil)
{
[array2 release];
array2 = nil;
}
[aAppDTO release];
aAppDTO = nil;
[super dealloc];
}
when I analyze my app in Xcode 4.3.2, I get memory warning in self.array1 and self.array2 (Potential leak on object allocated on line….), but when I change self.array1 to array1, warning goes away.
What is the reason for using self. do I need to use self if I set #property(nonatomic,retain) to variables(like array1,array2,string1,string2).
Also in dealloc method, I heard we don't want to use [self.array1 release], instead we can use [array1 release]. Is it Correct?
Do I need to release my string in dealloc method.
Also I am releasing aAppDTO in dealloc method. if I allocate some objects in anotherAppDTO class, will it release automatically when I call [aAppDTO release] method.
Can anyone clarify me.
Many Thanks,
Anish
You get the warning because when you write :
self.array1 = [[NSMutableArray alloc]init];
is the same as :
[self setArray1: [[NSMutableArray alloc]init]];
As you can notice you are not allocating the underlying array1 private variable, but you are calling the setter of the property that since it is declared as retain it retains the object once assigned, this means that when you eventually will assign another object the second time with the setter the first object will remain with a retain count of one until the application will be closed (since you don't have any reference to that object anymore ...) .
Take a look at this great article to understand better Manual Reference Counting in Objective-C .
when i analyze my app in Xcode 4.3.2, i get memory warning in self.array1 and self.array2 (Potential leak on object allocated on line….), but when i change self.array1 to array1, warning goes away.
the analyzer's right. the parameter is retained when set. as well, you should favor direct access in initialization and dealloc. so, you should just write array1 = [[NSMutableArray alloc] init];, and be done.
What is the reason for using self. do i need to use self if i set #property(nonatomic,retain) to variables(like array1,array2,string1,string2).
those go through the accessor methods. if not in initialization or dealloc, you should favor going through the accessor methods because that is the common correct execution path for a fully constructed object.
Also in dealloc method, i heard we don't want to use [self.array1 release], instead we can use [array1 release]. Is it Correct?
correct.
Do i need to release my string in dealloc method.
yes.
Also I am releasing aAppDTO in dealloc method. if i allocate some objects in anotherAppDTO class, will it release automatically when i call [aAppDTO release] method.
when its reference count reaches 0, its dealloc will be called.
I think the others have answered your question.
I do want to draw your attention to Apple's excellent Advance Memory Management Programming Guide: Practical Memory Management, in which they walk through these sorts of scenarios. It's hard to take it all in on the first reading, but it really does cover this stuff. In answer to your question about the use of instance variables versus the accessor methods, I draw your attention to the section labeled to "Don't Use Accessor Methods in Initializer Methods and dealloc".
According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!
myclass.m
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self; // send loc updates to myself
}
return self;
}
- (void)dealloc {
[locationManager release];
[super dealloc];
}
I use instrument to check leak. The leak always point to
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Why?
I use instrument to check leak. The
leak always point to
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Why?
Because that is the line of code where the allocation that is leaked occurred. Since you have balanced both implied retains in that code correctly (once here, once in -dealloc -- mmccomb's suggestion of direct assignment without autorelease is good, but won't fix the problem), the leak is elsewhere.
Specifically, the leak will be a retain that isn't balanced by a release. So, somewhere your are retaining the object and not releasing it.
Instruments can be used to show all retain/release events on any given object. Use that and look through the list of events related to your leaked object. There will be one more retain than release. Pair off the retains and releases. Whichever retain is left without a balanced release is the cause.
I wrote an article about using Heapshot analysis in Instruments to detect memory abuse. It includes discussion and screenshots showing the retain/release event inspector and will be applicable.
Try to avoid using the self. setter notation in init methods. When an object is being initialised there is no guarantee that it is in a consistent state. Change your implementation to directly set the ivar as follows...
locationManager = [[CLLocationManager alloc] init];
You no longer need the autorelease call which was previously accounting for the extra retain count brought about by invoking the setter. You do however need to release the object in your dealloc method (as you are already doing).
You can explicitly check the retain count in dealloc like so:
NSLog(#"locationManager retain count: %d", [locationManager retainCount]);
If it is more than 1, check where else you may be retaining it - like if you assign it to another retaining property (e.g. declared with retain keyword). You can add that NSLog call in other locations before and after you do something with locationManager and see where the retain count increases. Sometimes it may not be immediately obvious.
Another possibility: does the dealloc method even get called? Perhaps the whole "myclass" object is not properly released? (Although I suppose in that case you'd see a leak of type "myclass", too).
I came across this method:
-(void) someMethod {
NSMutableArray *anArray = [[NSMutableArray alloc] init];
// Do stuff with anArray ...
[anArray release];
anArray = nil;
}
Is setting the array to nil necessary?
In this code by sending a release message to the array, it will causes the array to be deallocated.
In this case, it is a pointless waste of key strokes because the variable anArray goes out of scope immediately.
In other cases, where the variable stays in scope for a while after you release the object its pointing to, it is a good idea, because, if you accidentally dereference it, you will get a EXC_BAD_ACCESS which is easy to spot, and if you send a message to it, it will be ignored (except for returning nil / 0).
As others have mentioned, setting it to nil will help your code not crash if you reference the dealloced object. If you reference a dealloced you will get EXC_BAD_ACCESS error and your app will crash. Since a nil object returns nil if a message is sent to it, your app will not crash.
In the example you provide, it is not necessary to nil it out, since it is contained in a method. However, you do not want to nil out a variable if you expect to use it somewhere else in the code, since the value will then be nil.
No it is not necessary.
It is just for safe reason (to not send a message to a zombie)
And you can test if your ivar is nil or not to realloc:
[ivar release];
ivar=nil;
...
if (ivar==nil) {
ivar = [[NSObject alloc] init];
}
[ivar setValue:#"toto"];
It's not necessary but considered good behaviour to set dangling pointers to nil.
Please see my comments in code:
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
{
[super init];
coordinate = c;
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
NSString* formattedDate = [NSString stringWithFormat:#"%# %#",
[dateFormatter stringFromDate:today], t];
[self setTitle:formattedDate]; //Why does the app crash when I try and release formattedDate? I have after all passed its reference to the title property?
[dateFormatter release]; //I need to release the dateformatter because I have finished using it and I have not passed on a reference to it
return self;
}
Nope, looks fine. stringWithFormat returns an autoreleased object - you don't own it unless you retain it, which you haven't, so you mustn't release it. You allocated dateFormatter yourself, so you do own it and must release.
(See the object ownership documentation and 8 squillion very similar SO questions. This must be the number one objective-c problem that crops up here by a significant margin.)
Edit: Actually, looking at your comment, there is a very slightly subtler issue here, though the upshot is the same (and actually underlines the point about ownership).
When you pass the string to setTitle, it could retain the string itself, in which case a call to release here might not cause a crash immediately. You would still be over-releasing, however, and it would bite you in the end. It just would be more difficult to locate the cause of the problem further down the line.
As it happens, setTitle makes a copy rather than retaining yours. This is pretty common with NSString properties. One reason is that you might pass an NSMutableString instead, and then randomly change it at some later point that might screw up the receiver. Taking a private copy is safer. As a bonus it exposes your over-releasing right away and allows you to fix it easily.
In any case, the point stands: only ever release something you know you own. If you've just passed it to some other object, it's that object's responsibility to handle whether it owns it or not, not yours.
You appear to have some radical misconceptions about how retain and release work. You say:
"Why does the app crash when I try and release formattedDate? I have after all passed its reference to the title property"
"I need to release the dateformatter because … I have not passed on a reference to it"
Whether you pass a reference to something is irrelevant to whether you're responsible for releasing it. The memory management rules dictate whether you should retain or release something, and they have practically nothing to do with passing references. You should read that document, because it's very fundamental and not really all that hard. Once you understand it, all your questions about this sort of thing will vanish.
The nutshell version is, you own an object and are responsible for releasing it if you obtained it from a new alloc or copy method or if you retained it. In the case of the NSDateFormatter, you got it from alloc, so you must release it. In the case of the string, none of those things are true, so you must not release this object that you don't own.
Slightly unrelated, but...
Initializsers in ObjC are supposed to look like
- (id)init;
{
self = [super init];
if (self == nil)
return self;
...
return self;
}
This allows the super class to return a replacement. This is pretty advanced usage of ObjC, though, but it's a good habit to stick to this pattern.
More info at ADC: Allocating and Initializing Objects.