According to the Google Objective-C Style Guide, we should autorelease then retain as so:
- (void)setFoo:(GMFoo *)aFoo {
[foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo|
foo_ = [aFoo retain];
}
In this case, foo_ will not be deallocated if being set to the same instance, making for a more defensive setter.
My question is, is this how #property & #synthesize work?
release due to an autorelease isn't called until the end of the current runloop so foo_ wont dealloc because retain is called first followed by release at the end of the current runloop. However, this isn't how the code generated in #synthesize works. It works more like
- (void)setFoo:(GMFoo *)aFoo {
if (aFoo != foo_) {
[aFoo retain];
[foo_ release];
foo_ = aFoo;
}
}
This method saves cpu cycles when no change is necessary and takes out the small overhead of using the autorelease pool.
Related
I am experiencing a bizarre behavior with object lifetimes in ARC. I have narrowed it down to this example:
//---------------------------------------------------------------------------------
#interface MyObject : NSObject
#end
#implementation MyObject
-(id)init
{
self = [super init];
if(!self) return nil;
NSLog(#" MyObject init %p", self);
return self;
}
-(void)dealloc
{
NSLog(#" MyObject dealloc %p", self);
}
#end
//---------------------------------------------------------------------------------
#implementation TLAppDelegate
-(MyObject *)createMyObject:(NSString *)unusedArg
{
return [MyObject new];
}
-(void)someOperation
{
NSLog(#" Entering someOperation");
MyObject* x = [self createMyObject:#"some message"];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"entering applicationDidFinishLaunching");
[self someOperation];
[self someOperation];
NSLog(#"exiting applicationDidFinishLaunching");
}
#end
Summary of code: applicationDidFinishLaunching calls someOperation twice in a row. someOperation creates a local object, which
I would expect is deallocated when someOperation returns.
Thus here is the output I would expect:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cd0
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
But here's the output I actually get:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0 <-- this object is retained until the end of the output!
Exiting someOperation; x should be deallocated right after this...
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
MyObject dealloc 0x600000012cd0
Why is the first object retained all the way until applicationDidFinishLaunching returns?
As far as I can tell, no MyObject instance should ever live outside of someOperation.
It's not a leak, because it's deallocated on the next scope.
It feels almost like the compiler inlines someOperation, merging scope with the caller. But this is also not true as
(correct me if I'm wrong), the compiler cannot inline objective-C methods.
For my real project, this is causing problems in multiple areas. First in our logging class we keep track of some scope, but because of this behavior, the following log:
Generating some list {
Calculating item #1 {
}
Calculating item #2 {
}
Calculating item #3 {
}
}
Turns into this, which is meaningless:
Generating some list {
Calculating item #1 {
Calculating item #2 {
Calculating item #3 {
}
}
}
}
Also, it means we are holding on to way too much memory for no good reason.
Is there a way to make this behavior more predictable?
Note that this behavior does not change if I use __attribute__((objc_precise_lifetime)); it makes no change to the observed behavior.
MyObject wouldn't be released at the end of someOperation because the object that createMyObject returns is retained (when created) and autoreleased (when returned).
So while someOperation subsequently assigns that object to x and retains and releases it as you'd expect, there's still that autoreleased reference from createMyObject that won't be cleared until the autorelease pool is drained (which normally happens at the end of every run loop).
If, instead of getting MyObject from createMyObject, you instantiated it directly like:
-(void)someOperation{
NSLog(#" Entering someOperation");
MyObject* x = [[MyObject alloc] init];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
there won't be that autoreleased reference hanging and everything should be deallocated immediately as you expect.
Update
Martin R brings up a good point about ARC and naming conventions. By default, an object returned by a method is retained/autoreleased by ARC (if it weren't retained, it'd be dealloc'd immediately. If it weren't autoreleased, it'd leak).
There are a handful of method that, according to Cocoa naming conventions, are expected to return a "retained" object — that is, an non-autoreleased object with a +1 retain count. For these specific methods, whose names start with alloc…, copy…, init…, mutableCopy…, or new…, ARC will return a retained object. Everything else returns an autoreleased one.
In cocoa, ARC frees you of having to worry about retain, release, autorelease, etc. It also prohibits calling [super dealloc]. A -(void) dealloc method is allowed, but I'm not sure if/when it's called.
I get how this is all great for objects, etc., but where do I put the free() that matches the malloc() I did in -(id) init ?
Example:
#implementation SomeObject
- (id) initWithSize: (Vertex3Di) theSize
{
self = [super init];
if (self)
{
size = theSize;
filled = malloc(size.x * size.y * size.z);
if (filled == nil)
{
//* TODO: handle error
self = nil;
}
}
return self;
}
- (void) dealloc // does this ever get called? If so, at the normal time, like I expect?
{
if (filled)
free(filled); // is this the right way to do this?
// [super dealloc]; // This is certainly not allowed in ARC!
}
You are right, you have to implement dealloc and call free inside of it. dealloc will be called when the object is deallocated as before ARC. Also, you can't call [super dealloc]; as this will be done automatically.
Finally, note that you can use NSData to allocate the memory for filled:
self.filledData = [NSMutableData dataWithLength:size.x * size.y * size.z];
self.filled = [self.filledData mutableBytes];
When you do this, you don't have to explicitly free the memory as it will be done automatically when the object and consequently filledData are deallocated.
Yes, you put it in -dealloc just like you do under MRR. The only difference with -dealloc is you must not call [super dealloc]. Other than that, it's exactly the same, and will get called when the object is fully released.
As an aside, free() will accept the NULL pointer and do nothing, so you don't actually need that conditional in -dealloc. You could just say
- (void)dealloc {
free(filled);
}
Let us say we have some code that looks like below:
#interface SomeClass : NSObject
#property (nonatomic, retain) NSString *someString;
#end
#implementation SomeClass
#synthesize someString;
-(id)init {
if (self=[super init]) {
someString = [NSString stringWithString:#"some string"];
}
return self;
}
#end
Am I supposed to release the someString property in the dealloc method of SomeClass, even if someString was set to autorelease and I never actually retained it in my init method? If so, I'd simply add [someString release] before [super dealloc] in the -release method. Correct?
Now the real issue I am having is that while using Cocos2D, I've reached a contradicting situation. My code looks like below:
#interface SomeLayer : CCLayer
#property (nonatomic, retain) CCSprite *someSprite;
#end
#implementation SomeLayer
#synthesize someSprite;
-(id)init {
if (self=[super init]) {
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"];
[self addChild:someSprite];
}
return self;
}
#end
Now, I have added someSprite as a child to my layer SomeLayer. So, what should I do to make sure I have no memory leaks here? I could think of the following:
Obviously, I'd think of calling [someSprite release] in SomeLayer's -dealloc method. but it gives me EXC_BAD_ACCESS in [super dealloc] (the next line). Most likely because I didn't remove the child, and the superclass tries to access the child that I just released.
I call [self removeChild:someSprite cleanup:YES] in the -dealloc method, which would remove the child and also release it. So I do not need to follow up with [someSprite release]. But hey, the -dealloc method of the superclass CCNode already does all that for me.
I do nothing. I wouldn't override the -dealloc method at all. Well, this seems to work fine, but it contradicts the statement: "if you retain something, you're supposed to release it".
Any help on why I must release the object in case I, and why not in case II at an early stage would help save a lot of memory related issues in the long run.
Thanks
someString = [NSString stringWithString:#"some string"];
This is wrong. You are keeping a pointer to an autoreleased object that will disappear soon, and when you’ll try to use the someString pointer bad things will happen. You should use the accessor ([self setSomeString:…]), retain the autoreleased value (someString = [… retain]) or use a method that returns a retained value (someString = [[NSString alloc] init…]).
In your real use case you should do the same with the sprite, you are getting EXC_BAD_ACCESS because you over-release the sprite: you call release without ever retaining the value. Read the Cocoa Memory Management Guide, you will save yourself a lot of trouble in the long run.
By the way, I think your main problem is that you think that a simple assignment to the someString variable retains the assigned value. That is not the case (not without ARC, to be more precise). Assignment to the instance variable is just that, a plain assignment. If you want to go through the accessors, you have to send a message ([self setSomeString:…]) or use the dot notation (self.someString = …).
You only have to release objects that you explicitly allocate. None of the examples you gave were allocated, so they are autoreleased. If you want to keep an autoreleased object for a long period of time, you need to retain it, and only then you would need to release the object.
Additionally, if you have properties you should set them to nil in viewDidUnload
self.someString = nil;
You really need to read Memory Management Programming Guide.
There are two of four rules
You can take ownership of an object using retain.
When you no longer need it, you must relinquish ownership of an object you own
When you declare property as retain then you should call release for appropriate variable. In your case your dealloc should looks
- (void)dealloc
[someSprite release];
[super dealloc];
}
And look at this code
if (self=[super init]) {
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"]; // here you assign pointer to new object
[self addChild:someSprite]; // all right, you can use newly created object in this scope
}
// but here that object can be deleted from memory and someSprite can points to nothing
To avoid this you need to retain newly created sprite
someSprite = [[CCSprite spriteWithFile:#"SomeFile.png"] retain];
#synthesize someSprite;
this line makes the retain count of SomeSprite to 1 ..in dealloc you release it so retain is back to 0.. object release.
[CCSprite spriteWithFile:#"SomeFile.png"];
this is an autorelease object..
when you do
someSprite = [CCSprite spriteWithFile:#"SomeFile.png"];
you point someSprite to the autorelease object.. so both are now equal..
this line messes the whole point of synthesize(retain) ..so now change this line to
[self setsomeSprite] =[CCSprite spriteWithFile:#"SomeFile.png"];
now you just continue the way it was.. have someSprite release in the dealloc.. and everything will be good again
I see in some sample code that autorelease is used. I am not familiar with the instances when this is required. For example, if I create an annotation object
Header file
#interface someViewController: UIViewController
{
Annotation *annotation;
}
#property (nonatomic, retain) Annotation *annotation;
#end
Implementation file
#implementation someViewController
#synthesize annotation
#end
Question: Is it the correct approach if I initialize my annotation object in the implementation file like this?
self.annotation = [[Annotation alloc] initWithCoordinate:location];
Do I need to set autorelease for this? Or can I just do it the normal way and add the release in the dealloc method?
this is correct:
self.annotation = [[[Annotation alloc] initWithCoordinate:location] autorelease];
because annotation property is declared as a retain property, so assigning to it will increment its retain count.
you will also need, all the same, to release self.annotation in -dealloc.
in short:
init will set retain count to 1;
assigning to self.annotation, will set it to 2;
autorelease will set it back to 1 when the main loop is executed again;
release in dealloc will set the retain count to 0, so that the object will be deallocated);
the best way to think of autorelease is the following, in my opinion: autorelease will "schedule" an "automatic" release for your object at some (near) point in future (typically when the control flow goes back to the main loop, but details are hidden in the hands of Apple).
autorelease is mostly useful in conjunction with init, specifically in the following cases:
when you init a local variable, so that you don't have to release it explicitly before it goes out of scope (the main loop will do that for you);
when you return a pointer to an object you have just created without keeping ownership of it (typical case of the create/make* kind of selectors, the receiver is required to retain it to get ownership);
with properties that retain, when you assign to them an object that they should own uniquely;
with data structures that increment the retain count (NSMutableArray, NSMutableDictionary, etc): you should generally autorelease a newly inited object when you add it to such data structure.
apart from case 2, it is evident that the use of autorelease is meant to improve readability of the code and reduce the potential for errors (meaning that in all of the other cases, you could simply release explicitly your object after the assignment or at the end of the scope).
when using properties, you have always to check whether they are of the retain or assign/copy case; in the first case, assigning a newly inited object to a property generally requires autorelease.
Anyway, I would suggest at least skimming one of the many tutorial on memory management for iOS.
Autorelease is telling the object to release itself before leaving the scope.
Sometimes when you code, you'll encounter something like this
- (void)doSomething
{
if(true)
{
NSString *foo = [[NSString alloc] initWithString:#"foo"];
//Some execution here
[foo release];
}
}
- (void)doSomething
{
if(true)
{
//By doing this is telling to to release foo object before getting out of the scope
//which is similar with above practice
NSString *foo = [[[NSString alloc] initWithString:#"foo"] autorelease];
//Or you can do it this way
NSString *foo = [[NSString alloc] initWithString:#"foo"];
[foo autorelease];
//Some execution carry on, it'll release foo before entering next scope
}
//This is out of the scope
}
Of course, releasing an object doesn't mean deallocating the object.
Sometimes you retain the object so you can still use it outside of its scope.
Judging from your question, if your the object is located within your header file/interface.
You should release it in dealloc method. CMIIW.
I'm a objective c newbie, and i'm having a bit of problems with memory management, I've read the apple's memory management policies, however i need a bit of clarification here, this is pretty simple i guess, but i would like to ask you if I'm right:
Given this property:
#interface Test : NSObject {
NSArray *property1;
}
#property (nonatomic,retain) NSArray* property1;
#end
...
//And its implementation:
#implementation Test
#synthetize property1;
-(id) init {
if (self=[super init]) {
self.property1=[[[NSArray alloc] initWithCapacity:5] autorelease];
}
return self;
}
-(void) dealloc {
[super dealloc];
[property1 release];
}
#end
Is it right to issue an Autorelease message to the allocated object in the init method?, i do this cause in apple's document, says that every allocated object should be released by the developer, then, I think, alloc sets retain count to 1, then the property (nonatomic, retain) adds 1, so retain==2, then autorelease substracts 1, and when the dealloc method is called, property1 is released and retain count==0, am I right?
You have your memory management right, though Apple (and a lot of other people) generally recommend not using accessors in your initialization methods because accessors can have side effects beyond simply setting an instance variable that your class might not be set up to handle yet. And in that case, you wouldn't want to autorelease since you'd want ownership of the object.
one side note: in your dealloc, you need to release the property before calling [super dealloc], because [super dealloc] eventually deallocates the memory of the object, which includes the memory containing the property1 variable, so it is invalid to refer to that variable after you call [super dealloc]. It should be:
-(void) dealloc {
[property1 release];
[super dealloc];
}
One of the nice things about using properties is that you can encapsulate all of your "releasing" behavior regardless of whether your property is set to retain, copy, assign, or whatever by just doing this:
self.property1 = nil;
Personally I've gotten in the habit of setting all properties to nil (using self.property, not just accessing the member variable directly) in dealloc so that even if I change how the memory management works for the member variable it works correctly.