I enabled zombies with my Xcode to find if my process crashes to a memory leak. Here is a code snippet:
- (NSString *)facVersion
{
return facVersion;
}
- (void) setFacVersion:(NSString*)_facVersion
{
if(facVersion != nil) [facVersion release];
facVersion = [_facVersion retain];
}
Now when I call
NSLog(#"%#", facVersion);
[self setFacVersion:facVersion];
the code crashes with the message
[CFString retain]: message sent to deallocated
Do you know what the problem is?
This is a typical problem with badly written setters. When the object itself is the last owner of the backing ivar of the property, assigning the property to itself causes a release effectively deallocating the object, then a retain on the same deallocated object. You can fix this in two ways. Either check for the to-be-assigned object not being the same as the current value of the property, or retain first and release only after. All in all, solution one:
- (void) setFacVersion:(NSString*)_facVersion
{
if (facVersion == _facVersion) return;
[facVersion release];
facVersion = [_facVersion retain];
}
Soltion two:
- (void) setFacVersion:(NSString*)_facVersion
{
[_facVersion retain];
[facVersion release];
facVersion = _facVersion;
}
By the way, checking for an object not being nil before releasing it is superfluous. Objective-C is not Java.
It's supposed to be if (facVersion != _facVersion) in the setter. Otherwise if you set the same object again, your setter will release it (resulting in the object being deallocated) and you can't use it (retain) after that:
- (void) setFacVersion:(NSString*)_facVersion
{
if(facVersion != _facVersion) {
[facVersion release];
facVersion = [_facVersion retain];
}
}
Also, did you know about Automatic Reference Counting (ARC)?
Automatic Reference Counting (ARC) is a compiler feature that provides
automatic memory management of Objective-C objects. Rather than having
to think about about retain and release operations, ARC allows you to
concentrate on the interesting code, the object graphs, and the
relationships between objects in your application.
If you pass the same object to setFacVersion as already set, it will first release it and then try to retain released (and deallocated) object.
You're releasing it before you call retain.
Unless you're comparing the pointers before releasing and retaining, you should use something like:
- (void) setFacVersion:(NSString*)_facVersion
[_facVersion retain];
[facVersion release];
facVersion = _facVersion;
}
That way, if the pointers are the same, as you demonstrated, you won't get a crash.
Or just let the compiler synthesize the setter for you.
Related
I used to do this till once I found the retain count of one of my retained propery is zero before dealloc function. (This situation is normal or abnormal?)
NOTE: It's a RC condition, not ARC.
For example, I got 4 retained properties below, should they always be released in dealloc function?
If not, how could I know when to release, and when not to release? Manually judge the retainCount?
#property (nonatomic, retain) NSString *fileName;
#property (nonatomic, retain) UIImage *fullSizeImage;
#property (nonatomic, retain) UIImage *thumbnailImage;
#property (nonatomic, retain) UIImageView *checkedImageView;
- (void)dealloc {
[checkedImageView release];
checkedImageView = nil;
[fileName release];
fileName = nil;
[fullSizeImage release];
fullSizeImage = nil;
[thumbnailImage release];
thumbnailImage = nil;
[super dealloc];
}
Well, if the question is "always?", then Wain is almost right...
a SHORT answer is YES...
because in general, when someone set-up a property, it means he's going to use it as a property, that is he uses its setter method to initialize it.
BUT (LONG answer): NO, NOT ALWAYS:
what if you, somewhere in your code, initialize the private var associated to the property without it's setter method? Keep in mind that a property is not a var, but just a useful way to get methods from Xcode to get and set a var associated to it.
in other words, when you write in .h:
#property (nonatomic, retain) NSString *fileName;
and in .m:
#synthesize fileName;
you are declaring a var called fileName and are asking xcode to create 2 (invisible) methods for you:
a setter, used to set a new retained value in fileName:
-(void)setFileName:(NSString *)newString{
if (fileName == newString) {
return;
}
NSString *oldString = fileName;
fileName = [newString retain];
[oldString release];
}
and a getter, used to get the value of fileName:
-(NSString)fileName{
return fileName
}
so, when you somewhere in your code use:
self.fileName = #"ciao";
you are using the property setter method, exactly as if you'd call it directly (and you can do it, the invisible method setFileName: really exist):
[self setFileName:#"ciao"];
doing so, as you can see in the setter method, from now on fileName is retained, and so you should release it in dealloc.
BUT, to answer your question:
if you use the dot rule to set a new string in your var, ok, everything is fine,
but you may decide to set it in the standard way, somewhere, maybe just for mistake:
fileName = #"ciao";
// code
fileName = #"Hallo";
// code
fileName = #"Bye";
this way you are not using the property setter method, but you are using the var directly, and so fileName is not retained, and if you try to release it, well you may get a crash...
PS:
Manually judge the retainCount?
no, never do that
Yes, they should always be released in dealloc. If you get to dealloc and something is already released and not set to nil then you did something wrong with your memory management elsewhere in the app.
Technically in dealloc you don't need to set to nil after releasing but setting to nil after releasing is a generally good idea.
Your dealloc is unnecessarily calling the getter for each property and then immediately releasing it. Just assign nil to release the properties:
- (void)dealloc {
self.checkedImageView = nil;
self.fileName = nil;
self.fullSizeImage = nil;
self.thumbnailImage = nil;
[super dealloc];
}
Although if you are following the current trend of letting clang auto-generate your backing instance variables, then this is better, as it won't cause KVO side-effects:
- (void)dealloc {
[_checkedImageView release];
[_fileName release];
[_fullSizeImage release];
[_thumbnailImage release];
[super dealloc];
}
Yes, they should normally all be released. If you have a retain count of zero, that usually means you've made a mistake somewhere in your memory management code.
You ask: If not, how could I know when to release, and when not to release? Manually judge the retainCount?
Possibly, but you could also let Xcode help you, using static analysis. Go to Product -> Analyze. It will quite often help you find erroneous releases, etc.
When to release? Quite obviously, if your object was holding a reference to another object, and your object goes away, then it should stop holding a reference to the other object. Why would you even look at the retain count? Retain count is about other people holding on the same object, but they are none of your business. They should know what they are doing. So you release the object. You do your job; everyone else has to do theirs. The easiest way, as others said, is to assign
self.someproperty = nil;
If your object was the only one holding a reference, that other object will go away. If others held a reference, it won't go away. Just as everyone would expect. The "release" method should be the only one ever caring about what the retain count of an object is.
Until yesterday I thought I understood how properties memory management works, but then I ran an "Analize" task with XCode and got plenty of "This object is not own here". Here is a simple example that describes my problem :
MyObservingObject.h:
#interface MyObservingObject : NSObject
#property(nonatomic, retain) NSMutableDictionary *observedDictionary;
-(id)initWithDictCapacity:(int)capacity;
#end
MyObservingObject.m:
#synthesize observedDictionary;
-(id)initWithDictCapacity:(int)capacity {
self = [super init];
if (self) {
self.observedDictionary = [[[NSMutableDictionary alloc] initWithCapacity:capacity] autorelease];
}
return self;
}
- (void)dealloc {
// The following line makes the Analize action say :
// "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
[self.observedDictionary release], self.observedDictionary=nil;
[super dealloc];
}
What I don't understand is Why should I leave this property without calling release on it? My #property is set as retain (copy does the same), so when I'm doing self.myRetainProperty = X, then X got its retain count increased (it's owned by self), didn't it ?
You should let the setter do the releasing for you, so remove the call to release in dealloc:
- (void)dealloc {
self.observedDictionary=nil;
[super dealloc];
}
This is because the setter will be synthensized to something like:
- (void)setObject:(id)object
{
[object retain];
[_object release];
_object = object;
}
Which will work as desired when you pass in nil.
It did get increased, but when you set it to nil, the setter method first releases the backing instance variable, and only then does it retain and assign the new value. Thus setting the property to nil is enough, setting the ivar to nil leaks memory, though.
For your better understanding: the typical implementation of an autogenerated retaining setter is equivalent to something like
- (void)setFoo:(id)foo
{
if (_foo != foo) {
[_foo release];
_foo = [foo retain];
}
}
Also note that, as a consequence, you should never release properties like this. If you do so, the backing ivar may be deallocated, and messaging it (release by the accessor when setting the property to nil afterwards) can crash.
You don't need to do
[self.observedDictionary release]
before
self.observedDictionary=nil;
This is enough, because this is a property, and it will automatically send release to previous value
self.observedDictionary=nil;
The reason for the compiler warning is because of the way you are retrieving the object.
By calling
[self.observedDictionary release];
you are in fact going through the accessor method defined as
- (NSDictionary *)observedDictionary;
This returns your object but due to the naming of observedDictionary the compiler assumes that there is no transfer of ownership e.g. the callee will not have to release this object unless they take a further retain. It is because of this that the compiler thinks you are going to do an overrelease by releasing an object that you don't actually own.
More specifically the convention for method names that transfer ownership is for them to start with copy, mutableCopy, alloc or new.
Some examples
Here I have used a name that does not imply transfer for ownership so I get a warning
- (id)object;
{
return [[NSObject alloc] init];
}
//=> Object leaked: allocated object is returned from a method whose name ('object') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa
Fix 1: (don't transfer ownership)
- (id)object;
{
return [[[NSObject alloc] init] autorelease];
}
Fix 2: (make the name more appropriate)
- (id)newObject;
{
return [[NSObject alloc] init];
}
With this knowledge we can of naming convention we can see that the below is wrong because we do not own the returned object
[self.object release]; //=> Produced warnings
And to show a final example - releasing an object that implies ownership transfer with it's name
[self.newObject release]; //=> No Warning
I created DownloadAndParseBook class. It will not autorelesed before it gеt any data or network error.
I used [self release], [self retain]. Is it good approach to use [self release], [self retain]? Is DownloadAndParseBook contain any potential bugs?
#implementation GetBooks
-(void) books
{
for(int i =0; i<10; i++)
{
DownloadAndParseBook *downloadAndParseBook =
[[[DownloadAndParseBook alloc] init]autorelease];
[downloadAndParseBook startLoadingBook];
}
}
#end
#implementation DownloadAndParseBook
- (id)initWithAbook:(int)bookID
{
if(self = [super init])
{
[self retain];
}
return self;
}
- (void)startLoadingBook
{
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self saveResultToDatabase];
[self release];
}
#end
Self retaining is very occasionally an appropriate pattern. It's rare, but sometimes in certain kinds of multi-threaded code its important to make sure that you don't vanish in the middle of processing something. That said, this is not one of those times. I'm having trouble imagining a case where your current approach would be helpful. If someone creates your object and then never calls startLoadingBook, then it leaks. If someone calls startLoadingBook, then your object is retained anyway, because NSURLConnection retains its delegate until it finishes.
That said, I believe much of your problem is coming from the fact that your object model is wrong. Neither GetBooks nor DownloadAndParseBook make sense as classes. What you likely mean is BookManager (something to hold all the books) and BookDownloadController (something to manage the downloading of a single book). The BookManager should keep track of all the current BookDownloadControllers (in an NSSet or NSArray ivar). Each BookDownloadController should keep track of its NSURLConnection (in an ivar). You should not just create connections and have them "hang on themselves" (i.e. self-retain). This feels convenient, but it makes the code very hard to deal with later. You have no way to control how many connections you're making. You have no way to cancel connections. It becomes a mess really quickly.
No it is not a best practice.
Retaining / releasing your object should be done by the "owner" of your object.
For your particular example, the owner of your DownloadAndParseBook object is the object that does the alloc/init. That should be the oen retaining/releasing your DownloadAndParseBook instance.
Best practice here would be alloc/init for DownloadAndParseBook, retain done by the owner, all your download/parse logic, then sending a callback to the owner that all the operations are done (through a delegate for example), at which point, the ower sends a release message to your object.
The question would be: Why does an object require to retain itself? You may want to implement your class like a singleton.
Unlike the other responders I would say that your pattern might work. See also Is calling [self release] allowed to control object lifetime?
There are some other issues in your code however:
In -(void) books I guess you want to send the startLoadingBook message to downloadAndParseBook and not to self
If you create a initWithAbook method it will not be called when you init your book with the standard init method. In the current code above [self retain] will be never called
In your code above bookID will not be saved
I would not use "init" pattern here, but everything in a static function thus the caller can not make mistake with the ownership of the class.
Code:
- (id) initWithId:(int)bookId {
self = [super init];
if (self) {
// save bookId here
}
return self;
}
+ (void) startLoadingBookWithID:(int)bookId {
DownloadAndParseBook* book = [[DownloadAndParseBook alloc] initWithId:bookId];
[NSURLConnection connectionWithRequest:request delegate:book];
}
// release self when it finished the operation
// and document well that its behaviour
If you think well, NSURLConnection itself should work exactly the same way: when you don't release an NSURLConnection when it finished its work, it does it itself. However in the connectionWithRequest it also can not autorelease itself since it has to be alive until the request is served. So the only way it can work is the pattern described above
Never use [self release]. The only possible exception would be in an singleton class/object. The methods release and retain should only be sent by the owner of an object. This usually means, whichever object created the object in question, should also be the one to release it.
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)
I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar