For some reason, the retain/release behavior in the following code has me completely baffled.
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage release];
This should break but does not. Why? I thought imageNamed autoreleased itself which means the release here is redundant and should break when the autorelease occurs.
Here are snippets relevant to selectedImage from the .h and .m files:
#property (nonatomic, readonly) UIImage *selectedImage;
#synthesize delegate, selectedImage, spacerBottom, currentIndex;
Other notes, this does break:
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage release];
[selectedImage release];
//objc[55541]: FREED(id): message release sent to freed object=0x59245b0
//Program received signal: “EXC_BAD_INSTRUCTION”.
As does this:
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage release];
[selectedImage autorelease];
//objc[55403]: FREED(id): message autorelease sent to freed object=0x59b54c0
//Program received signal: “EXC_BAD_INSTRUCTION”.
And so does the following:
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage autorelease];
[selectedImage release];
//objc[55264]: FREED(id): message release sent to freed object=0x592c9a0
//Program received signal: “EXC_BAD_INSTRUCTION”.
And so does this:
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage autorelease];
[selectedImage autorelease];
//objc[55635]: FREED(id): message release sent to freed object=0x5b305d0
//Program received signal: “EXC_BAD_INSTRUCTION”.
-imageNamed: returns an autoreleased image, which, as deanWombourne says, will be autoreleased at some time in the future (the exact time is undefined).
The reason it's not being autoreleased as early as you are perhaps used to is that -imageNamed also caches the image it returns. The cache is retaining the image.
So essentially, the retain cycle is something like this:
-imageNamed: called,
System allocs and init's an image -- retain count = 1;
System caches image -- retain count = 2;
System autoreleases image and returns to you -- retain count = 1; (theoretically, the image still has retain count of 2, because the auto release pool has not yet released it).
you call release on the image -- retain count should be 0 and the object should be deallocated.
At some point in the future (at the end of the run loop), the auto release pool should release the image, and will crash because you have over released it.
If you do not release it, the cache will continue to retain the image until it releases it, for instance when a memory warning occurs. So when you get an image using imageNamed, it doesn't get deallocated, until the cache is purged.
Hope this clears things up.
Odd and wierd, yes. But not completely inexplicable. this is what I think is happening.
You're correct; imageNamed: returns an autoreleased object. this means that it's going to get released sometime in the future so you calling release on it straight away won't cause an error - release isn't psychic, it doesn't know that an autorelease pool is also going to release it!
If you left your code running the autorelease pool will eventually try to release it again and then you will get the error you're expecting.
You've actually answered our own question - you say 'should break when the autorelease occurs' which is absolutely correct, when the autorelease occurs, it will break :)
The other examples break because you're forcing releases to happen by either calling them directly or doing enough stuff that the autorelase pool is triggered to run and calls release for you. (You can't predict when the autorelease pool will run, you can just know that at some point in your run loop, autoreleased things maight be released.)
You say that "This should break"
selectedImage = [UIImage imageNamed:#"icon_72.png"];
[selectedImage release];
You are wrong.
It probably would break if UIImage was an instance of the kind of Class you and i would write and learnt to write from our Cocoa books, but we didn't write it so we shouldn't guess at it's implementation.
How UIImage works is an implementation detail and not your concern. All you know is that you should be able to EXPECT it to work if you follow the rules, which i believe are now being referred to as NARC, and which you haven't done here. Nowhere are objects guaranteed to 'break' if you use them incorrectly. You cannot count on Objects being deallocated when you are thru with them - that isn't part of the memory management contract.
Not all of Apple's objects work like text book class/instances - in reality, objects may be cached, reused, recycled or not even be objects at all.
Don't worry about it, follow the rules.
When I create this category:
#implementation UIImage (ReleaseChecks)
+ (id)allocWithZone:(NSZone *)zone
{
id o = [super allocWithZone:(NSZone *)zone];
NSLog(#"Image post-ALLOC: 0x%x",
(unsigned int)o );
return o;
}
- (id)autorelease
{
NSLog(#"Image pre-AUTORELEASE: 0x%x; Retain Count %u",
(unsigned int)self,
(unsigned int)[self retainCount]
);
return [super autorelease];
}
- (void)release
{
NSLog(#"Image pre-RELEASE: 0x%x\n; Retain Count %u",
(unsigned int)self,
(unsigned int)[self retainCount]
);
[super release];
}
- (void)dealloc {
NSLog(#"Image pre-DEALLOC: 0x%x\n; Retain Count %u",
(unsigned int)self,
(unsigned int)[self retainCount]
);
[super dealloc];
}
It appears that -autorelease is not called when allocated with +imageNamed:.
However, when I have created a whole bunch of these with +imageNamed: and then later get a memory warning, I can see them all release and dealloc. This was tested on iPhone Simulator 4.0.
Related
I understand that when using Alloc, new or copy you own the object and need to release the object. I understand that if I retain an object that I need to release it.
But if I have the following statment at the end of a method:
return [[UIImage alloc] initWithContentsOfFile:path];
I own the UIImage object becaused I allocated the memory space, but I dont have a reference to it anymore because it was returned to the caller. In my dealloc() method I can't release it, since I dont have a reference.
So my question is, is this correct:
return [[[UIImage alloc] initWithContentsOfFile:path] autorelease];
I beleive in this case the caller then can retain the returned object if they like to take ownership and will eventually need to release themselves.
Thanks for your help.
Your assumptions are right. When you're returning an object, you should pass it as an autorelease object.
You use autorelease when you need to send a deferred release message—typically when returning an object from a method...
The whole point of autorelease was built around returning objects.
- (id)bad {
MyObj *obj = [[MyObj alloc] init];
return obj;
}
This code returns everything correctly, but you (as developer) must be sure to release the object later on.
- (id)moreBad {
MyObj *obj = [[MyObj alloc] init];
return [obj release];
}
This code uses the memory as expected, balancing retain and release in one scope, but returned object is garbage now (expect this to crash).
- (id)good {
MyObj *obj = [[MyObj alloc] init];
return [obj autorelease];
}
This code is "delayed release". I.e. you can retain the object at the caller side and the object would be safe. It could surely be broken if you don't have NSAutoreleasePool in scope, but that's a pretty rare condition, and you mostly know when that happens (the most common scenario is to start a new thread where there is no "default" autorelease pool).
So, the common practice is to balance the retain (and retain-like) and release methods in one scope. Next suggestion is to always have objects alloc-init-autoreleased, and switch to distinct releases if you have memory issues (like autoreleasing lots of objects in a loop). Next suggestion is to switch to ARC.
Both answers are correct, you may however not always have a convenience method at your disposal, so what I like to do is what you've done with autorelease:
- (MySomethingClass*)giveMeAFancyObject{
MySomethingClass *obj = [[[MySomethingClass alloc] init] autorelease];
obj.name = #"Something";
// do some setting up maybe
return obj;
}
If you use release, however, the string will be deallocated before it is returned (and the method would return an invalid object). Using autorelease, you signify that you want to relinquish ownership, but you allow the caller of the method to use the returned string before it is deallocated.
Why trying to alloc it? Just say:
return [UIImage imageWithContentsOfFile:path];
Nothing to alloc - nothing to release.
Also, this is the best option while using ARC, where a releasing attempt leads to error.
[[UIImage alloc] initWithContentsOfFile:path]
return nil when the method can't initialize the image. Then next code is not releasing the allocated UIImage, as image is nil in the [image release] line:
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
if(image)
{
....
}
//Here image is nil and not releases allocated UIImage.
[image release];
Is this really a memory leak?
if init returns nil, how must release the object?
if I do
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
and image is nil because init fails,
[image release] is the same as [nil release]. Ok there's not an error, but is not releasing anything.
Retain count in this example has nothing to do with whether or not the image is nil. You manually allocated the image using
UIImage* test = [UIImage alloc];
and therefore the retain count will be one until you manually release it, as you are the sole owner of that object.
See Memory Management Rules for more information on the subject.
release on nil is a no-op, so always ok. And it won't leak as you didn't have an object to start with.
UIImage* test = [UIImage alloc];
test is already an UIImage object on its own (though you failed to initialize it at this line).
You really should always do alloc/init on the same line (and on the same variable) - or the code logic is really hard to follow. Your code generates only one object, and then assigns it to another variable.
This is the same, though much clearer:
UIImage* test = [[UIImage alloc] initWithContentsOfFile:path];
UIImage* image = test;
int n = [test retainCount]
Here it is obvious that test and image are the same object (and hence have the same retainCount). Whenever you release one of them, the the object goes away (unless you retain it before).
Please also note that retainCount is not something you should rely on or make much assumptions on. It often is misleading at best.
Hello Stackoverflow fellow family members!
I've got question regarding memory management in iPhone.
What I did understand was below method
-(void) dealloc
{
// something else to release whatever
// such as Object Created using keyword 'alloc'
// but also Object destroy here its retain value reaches iff 0
// if I do put here NSLog(#"%d", [obj retainCount]); and when it reaches
// not equal to 0 means failure with memory leak.
[super dealloc];
}
So am I understand right? or It is still alight even if retain count reachs > 0 here?
The reason I ask about this question because,
I checked with
NSLog(#"%d", obj.retainCount);
to check the retain count of the object and received value 3. So I tried to release here 3 times to make retainCount here equal to 0, but compiler gives me critical error.
Please, I'm new to the memory de-allocation and retain, release.
Object that I used was 'UIImageView' object and created another instance as,
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
// at this point retain count was '1'
instance = imageView;
//[imageView retain];
// at this point retain count was '2'
[self.view addSubView: imageView];
// at this point retain count was '3'
[imageView release];// crashes
// at this point retain count was '2'
but if I do
// but if I add retain on the 'instance = imageView'
// such as
instance = imageView; // then
[imageView retain];
// works but still count is 2...
Thank You.
retainCount is not a reliable debugging-tool:
other objects might still hold references to obj
there are objects that you can't destroy (e.g. string constants)
objects are deallocated if released with a retain count of 1
What you should take care of instead:
balancing references you have with the right amount of release/autorelease
using the Analyzer
using Leaks
The normal process for overriding dealloc is to release any objects that have been previously retained (or alloc'ed) by this instance.
So if somewhere else in the object you have called an alloc or retain method your dealloc would look like:
-(void)someOtherMethod
{
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
instance = imageView;
[instance retain];
}
-(void) dealloc
{
//release any retained objects here
[instance release]
[super dealloc];
}
Note that it doesn't matter if the release count hasn't dropped to zero after your particular release, that just means that some other bit of code has also retained the object memory (and that other bit of code will be responsible for releasing it).
Hope this helps.
That's not correct and you should rarely use retainCount. It does not have to be 0 at this point as other objects could have references to the objects you are releasing. What is important to do in dealloc is to release objects that you have ownership on. Which would be objects created with alloc or new etc.
Is mystring over-released?
-(void)dealloc {
[mystring release];
[mystring release];
[super dealloc];
}
I assume this will not based on [nil release] does nothing:
-(void)dealloc {
[mystring release];
mystring = nil;
[mystring release];
[super dealloc];
}
-EDIT-
Let's say I allocate mystring in init and release it in doSomething:
-(id)init {
if (self = [super init]) {
mystring = [[NSString string] retain];
}
return self;
}
-(void)doSomething {
[mystring release]; // for some good reason
// ...etc
}
Now to avoid over-releasing in dealloc based on my example above do I have to explicitly do this in the doSomething method?
-(void)doSomething {
[mystring release];
mystring = nil; // <- is this mandatory to avoid over-releasing in dealloc?
}
The big question is do I have to explicitly set it to nil when I release it somewhere else in the class to avoid over-releasing in dealloc?
So could I do as many releases as I want in dealloc based if I explicitly set to nil in doSomething?
-(void)dealloc {
[mystring release];
[mystring release]; // <- does nothing because mystring explicitly nil?
}
For your first example, the answer is probably!
In general, each object that holds a reference to another object should retain it until they're done, then call release once, however in some highly exotic (and generally poorly written) cases this may not hold. If you retain the object x times, you need to release it x times, and other cases of poor memory management. But best practice, yes, one retain, one release, don't release more than you retain!
There are two risks with over-releasing like this:
The first is if the first release call makes the refcount of mystring equal to 0, it will be dealloc'd, and then you're sending a message to a piece of memory that is no longer a valid object. Objective-C doesn't really like this, and may react in a variety of ways, including CRASHING. Sending messages to nil, kosher, messages to things dealloc'd, not so much.
The second is if the refcount isn't zero, you just released someone else's reference, so at some point in the future, an object that has a reference to mystring may think that reference is valid and it won't be because it was dealloc'd when the refcount hit zero on a subsequent release call. This will be harder to detect than the previous error, where a debugger will at least show you the real area the problem originates from in the stack frame trace.
Your second example is correct - sending a message to nil does nothing. If you fear you'll do things like this, be sure to set your variables to nil after releasing.
EDIT: Yes. That's what you should do if you intend to release in more than one place like that, however depending on the application, you might consider using an auto-release pool.
It all depends on how many time you retained mystring. If you have two members that may refer to the same object and both retain the object, they should both release the object.
So your first example would be correct if you use mystring = [[somestring retain] retain];
The second one will be better. Because when you release, you release the memory the mystring point to not the memory of the mystring. So, the mystring is not null and if somebody accidentally use it, your app will crash. It also depends on how many times your mystring is retained
I have a problem that I am getting EX_BAD_ACCESS when calling release on an NSStream object in my dealloc on the iPhone.
The following code
- (void)dealloc {
DLog(#"dealloc started for: %#",self);
#synchronized(self) {
lookupCount--;
if (lookupCount==0) {
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = NO;
}
}
DLog(#"inStream retain count before release: %d",[inStream retainCount]);
[inStream release];
DLog(#"outStream retain count before release: %d",[outStream retainCount]);
[outStream release];
[queryToSend release];
[resultString release];
[data release];
[super dealloc];
NSLog(#"dealloc finsihed for : %#",self);
}
crashes with EX_BAD_ACCESS on the
[outstream release];
line.
Log output is as follows
2009-04-29 13:16:28.547 App[30580:20b] -[SimpleQuery dealloc] [Line 160] dealloc started for: <SimpleQuery: 0x56e540>
2009-04-29 13:16:28.547 App[30580:20b] -[SimpleQuery dealloc] [Line 168] inStream retain count before release: 1
2009-04-29 13:16:28.548 App[30580:20b] -[SimpleQuery dealloc] [Line 170] outStream retain count before release: 1
Wondering if anyone has any ideas why this might be ?
In a comment you said this about outstream
It's created by a call to
getStreamsToHostNamed:port:inputStream:outputStream:
which shouldn't return autoreleased
objects I don't think.
It is in fact, auto-released. Unless you are retaining that object somewhere in your code, you are not responsible for the memory management of it.
You should take a look at the Apple Memory Management Guidelines.
Many classes provide methods of the
form +className... that you can use to
obtain a new instance of the class.
Often referred to as “convenience
constructors”, these methods create a
new instance of the class, initialize
it, and return it for you to use.
Although you might think you are
responsible for releasing objects
created in this manner, that is not
the case according to the policy Cocoa
set—the method name does not contain
"alloc" or "copy", or begin with
"new". Because the class creates the
new object, it is responsible for
disposing of the new object.
Some potential problems:
You're locking on self to do the lookupCount decrement loop, which I assume means you expect this code to be run from different threads. That should be a red flag right there, since if you're deallocating an instance from two threads simultaneously, one of those threads is going to end up trying to dealloc an already deallocated instance.
The final NSLog call will try to print self which would already have been deallocated.
I know none of these pertains specifically to [outStream release], but they could be related. You might want to try debugging this with NSZombieEnabled to get more info.
Also, make sure releasing inStream doesn't also implicitly release outStream, etc.