PDA

View Full Version : Obj-c, RetainCount problem




ksiedzulek
Apr 8, 2011, 02:48 PM
hi,

I am learning obj-c. I know that dealloc automatically start to operate when retain count reaches 0. Im my code [obiektos RetainCount] return 1, but dealloc is called (personally I've thought that [obiektos RetainCount] will return 0 and that's why dealloc is called).

why there is 1, not 0 at output?

output:

2011-04-08 21:19:27.853 Metoda prostokta[20729:a0f] zadzialal dealloc
2011-04-08 21:19:27.869 Metoda prostokta[20729:a0f] 1




#import "nowycos.h"
//#import "ojej.h"


int main (int argc, const char * argv[]) {
nowycos*obiektos;
nowycos*obiektos1;

obiektos=[nowycos new];
[obiektos release];


NSLog(@"%d", [obiektos retainCount]);




return 0;
}


here is dealloc:
-(void) dealloc
{
NSLog(@"zadzialal dealloc");
[super dealloc];
}



Catfish_Man
Apr 8, 2011, 03:11 PM
When the retain count *would* reach zero, *instead* -dealloc is called. There's no point in changing the number just before getting rid of the object.


More generally, it's almost never a good idea to call -retainCount. It will lie to you in many many situations. Follow the memory management rules, use the analyzer, and investigate problems with Instruments, and you should be good.

chown33
Apr 8, 2011, 03:31 PM
After the last release of any object, the object itself is invalid.

Calling any method on an invalid object has an undefined result.

So expecting retainCount to be valid for an invalid object is nonsense.

If you ask a person you've just killed "Are you dead?", you should not expect them to say "Yes". You should not expect them to say "No", either. Any expectation of an answer is misinformed about the side-effects of death.

ChOas
Apr 8, 2011, 03:34 PM
You count, your objects don't... don't ask them for their count

Don't use retainCount (for your example).

[edit]
what chown33 just said

GorillaPaws
Apr 8, 2011, 04:34 PM
If you ask a person you've just killed "Are you dead?", you should not expect them to say "Yes". You should not expect them to say "No", either. Any expectation of an answer is misinformed about the side-effects of death.

Answers like these make me sad that Macrumors doesn't support voting up answers. Thanks for the laugh.

holmesf
Apr 8, 2011, 06:43 PM
After the last release of any object, the object itself is invalid.

Calling any method on an invalid object has an undefined result.

So expecting retainCount to be valid for an invalid object is nonsense.

If you ask a person you've just killed "Are you dead?", you should not expect them to say "Yes". You should not expect them to say "No", either. Any expectation of an answer is misinformed about the side-effects of death.

I love your answer.

PatrickCocoa
Apr 8, 2011, 09:28 PM
[CODE]
#import "nowycos.h"
//#import "ojej.h"


int main (int argc, const char * argv[]) {
nowycos*obiektos;
nowycos*obiektos1;

obiektos=[nowycos new];
[obiektos release];

------------> obiektos is dead(ish), it will no longer reply coherently.
------------> Cocoa's run loop may or may not kill it at any point.

NSLog(@"%d", [obiektos retainCount]);




return 0;
}


As per chown, at the point of "----->", all references to obiektos are invalid.

ksiedzulek
Apr 9, 2011, 05:16 AM
thanks for quick reply!

...but i still don't get it:/. I can "forget" about using retaincount, but that's not explain to me what happen here(please look at red code):

#import <Foundation/Foundation.h>
@interface SubObject : NSObject
{
int x;
int y;
}
@property int x,y;
@end

@implementation SubObject
-(void) dealloc
{
NSLog(@"dealloc called");
[super dealloc];
}
@synthesize x,y;
@end

@interface PrimObject: NSObject
{
int szer;
int wys;
SubObject*mySubObject;
}
@property int szer,wys;
-(void) setMySubObject: (SubObject*) nobject;
-(SubObject*) mySubObject;
@end

@implementation PrimObject
@synthesize szer,wys;
-(SubObject*) mySubObject
{
return mySubObject;
}

-(void) setMySubObject: (SubObject*) nobject
{
mySubObject=nobject;
}
@end





int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SubObject*thisIsSubObject=[SubObject new];
[thisIsSubObject setX:6];
PrimObject*Object=[PrimObject new];
NSMutableArray *myarray=[NSMutableArray arrayWithCapacity:10];
NSLog(@"retain number: %d", [thisIsSubObject retainCount]);
[myarray addObject:thisIsSubObject];
NSLog(@"retain number: %d", [thisIsSubObject retainCount]);
[Object setMySubObject: thisIsSubObject];
NSLog(@"retain number: %d", [thisIsSubObject retainCount]);

[thisIsSubObject release];
[thisIsSubObject release];// here is dealloc called

[thisIsSubObject setX:7];// so why I can still set this?


NSLog(@"number is: %d", Object.mySubObject.x);




[pool drain];
return 0;
}



here is output:
2011-04-09 12:08:05.992 hum[2021:a0f] retain number: 1
2011-04-09 12:08:05.994 hum[2021:a0f] retain number: 2
2011-04-09 12:08:05.995 hum[2021:a0f] retain number: 2
2011-04-09 12:08:05.995 hum[2021:a0f] dealloc called
2011-04-09 12:08:05.996 hum[2021:a0f] number is: 7

why do I can set "7"?

p.s. why "-(void) setMySubObject: (SubObject*) nobject;" doesn't increase retain number?

jiminaus
Apr 9, 2011, 06:38 AM
p.s. why "-(void) setMySubObject: (SubObject*) nobject;" doesn't increase retain number?

Because you never call retain. All you do is assign the pointer. You might want to read carefully the Accessor Methods chapter of the Memory Management Programming Guide.

Or better yet, don't implement the methods yourself. Instead synthesise them.


@interface PrimObject: NSObject
{
int szer;
int wys;
SubObject*mySubObject;
}
@property int szer,wys;
@property (retain) SubObject *mySubObject;
@end

@implementation PrimObject
@synthesize szer,wys,mySubObject;
@end


In regards to being able to set X after dealloc, this is just because the freed memory hasn't been reused/overwritten yet, so the object still seems to exist.

Take the analogy of deleting a file. When you delete a file, the bytes still exist on the disk, until they get overwritten by another file. If you knew where the file was on the disk (that is you had a pointer to that part of the disk), you could still access that file as though it hadn't been deleted. But eventually that part of the disk would be reused/overwritten by another file, and you could not longer access successfully.

The same is happening here with the memory for the object. You have what's called a dangling pointer. The bytes of the object still exists in memory and so you're still able to use it. But if you tried to use that pointer later in the program after many other objects had been created, chances are that the memory would have been overwritten by another object, and your attempt to set X would either fail (if you're lucky) or corrupt whatever object was now occupying that memory (which more likely).

ksiedzulek
Apr 9, 2011, 08:05 AM
thx Jiminaus! I've thooght a lot about it and my conclusions are same as your explanation. thx a lot:)

gnasher729
Apr 9, 2011, 08:11 AM
thanks for quick reply!

...but i still don't get it:/. I can "forget" about using retaincount, but that's not explain to me what happen here(please look at red code)

You _must_ forget about retainCount. Instead you _must_ read and understand the "Memory Management Programming Guide.".

The principle is that every unit of code has to do the right thing as far as retain / release is concerned and must not worry about what other bits are doing.

You used an NSMutableArray. NSArrays, mutable or not, retain things you put into the array, and release them when the array itself is deallocated. So the array itself does the right thing and doesn't care about what other code is doing.

When you implement a property like "mySubObject", you as the programmer must do the right thing. That is, you must retain the object when it is stored, you must release an object that was stored there previously, and you must release the object when the owner object is deallocated. Easiest done by synthesizing a property with "retain", and adding a "release" for the object to your dealloc.

The second "[thisIsSubObject release]" creates a time bomb. Your code has allocated the object (alloc = retained once) and released it twice, once more than it should have. So that part of code is wrong. Other bits of code might be retaining the object, so any problems might not occur _yet_. But they will occur, for example when the NSMutableArray is dealloc'ed.

And a crash is _not guaranteed_ to happen. You should assume that any wrong code will not bite you when you are just playing with it, it will bite you when it really, really hurts. When you ask "why does my incorrect code work?", the answer is "because it wants to lull you into a false sense of security in order to cause maximum damage".

ksiedzulek
Apr 9, 2011, 09:40 AM
I think I start to feel it!:)

...but one more question, this time about autorelease pool.
here is a code (and question below):

#import <Foundation/Foundation.h>
@interface SubObject : NSObject
{
int x;
int y;
}
@property int x,y;
@end

@implementation SubObject
-(void) dealloc
{
NSLog(@"dealloc called");
[super dealloc];
}
@synthesize x,y;
@end

@interface PrimObject: NSObject
{
int szer;
int wys;
SubObject*mySubObject;
}
@property int szer,wys;
-(void) setMySubObject: (SubObject*) nobject;
-(SubObject*) mySubObject;
@end

@implementation PrimObject
@synthesize szer,wys;
-(void) dealloc
{
NSLog(@"dealloc called");
[super dealloc];
}


-(SubObject*) mySubObject
{
return mySubObject;
}

-(void) setMySubObject: (SubObject*) nobject
{
mySubObject=nobject;
}
@end





int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

PrimObject*Object=[PrimObject new];
SubObject*thisIsSubObject=[SubObject new];


NSMutableArray *myarray=[NSMutableArray arrayWithCapacity:10];
NSLog(@"1 retain number: %d", [thisIsSubObject retainCount]);

[myarray addObject:thisIsSubObject];
NSLog(@"2 retain number: %d", [thisIsSubObject retainCount]);

[Object setMySubObject: thisIsSubObject];
NSLog(@"3 retain number: %d", [thisIsSubObject retainCount]);

[thisIsSubObject release];// here retain number equal 1
NSLog(@"4 retain number: %d", [thisIsSubObject retainCount]);

[pool drain]; // why "thisIsSubObject" is released? I didn't sending autorelease message to thisIsSUbObject???


return 0;
}


output

2011-04-09 16:13:47.073 kjhnm,[3909:a0f] 1 retain number: 1
2011-04-09 16:13:47.076 kjhnm,[3909:a0f] 2 retain number: 2
2011-04-09 16:13:47.076 kjhnm,[3909:a0f] 3 retain number: 2
2011-04-09 16:13:47.077 kjhnm,[3909:a0f] 4 retain number: 1
2011-04-09 16:13:47.077 kjhnm,[3909:a0f] dealloc called

Why when I send drain message to pool, thisIsSubObject was released even though I didn't apply [thisIsSubObject autoreleased]?

Sydde
Apr 9, 2011, 10:17 AM
I think I start to feel it!:)

...but one more question, this time about autorelease pool.
here is a code (and question below):


int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSMutableArray *myarray=[NSMutableArray arrayWithCapacity:10];

[myarray addObject:thisIsSubObject];

[thisIsSubObject release];// here retain number equal 1

[pool drain]; // why "thisIsSubObject" is released? I didn't sending autorelease message to thisIsSUbObject???

return 0;
}


output

2011-04-09 16:13:47.073 kjhnm,[3909:a0f] 1 retain number: 1
2011-04-09 16:13:47.076 kjhnm,[3909:a0f] 2 retain number: 2
2011-04-09 16:13:47.076 kjhnm,[3909:a0f] 3 retain number: 2
2011-04-09 16:13:47.077 kjhnm,[3909:a0f] 4 retain number: 1
2011-04-09 16:13:47.077 kjhnm,[3909:a0f] dealloc called

Why when I send drain message to pool, thisIsSubObject was released even though I didn't apply [thisIsSubObject autoreleased]?

the method +arrayWithCapacity: allocates the array, then adds it to the autorelease pool. When you drain the pool, myarray gets released, which cause it to send release to everything it contains.

chown33
Apr 9, 2011, 11:05 AM
You _must_ forget about retainCount. Instead you _must_ read and understand the "Memory Management Programming Guide.".

Link to the guide:
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html