Obj-c, RetainCount problem

Discussion in 'Mac Programming' started by ksiedzulek, Apr 8, 2011.

  1. macrumors newbie

    Joined:
    Jan 6, 2011
    #1
    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




    Code:
    #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:
    Code:
    -(void) dealloc
    {
    	NSLog(@"zadzialal dealloc");
    	[super dealloc];
    }
    
     
  2. macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #2
    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.
     
  3. macrumors 603

    Joined:
    Aug 9, 2009
    #3
    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.
     
  4. macrumors regular

    Joined:
    Nov 24, 2006
    Location:
    The Netherlands
    #4
    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
     
  5. macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #5
    Answers like these make me sad that Macrumors doesn't support voting up answers. Thanks for the laugh.
     
  6. macrumors 6502a

    Joined:
    Sep 30, 2001
    #6
    I love your answer.
     
  7. macrumors 6502a

    Joined:
    Dec 2, 2008
    #7
    I'm Dead!

     
  8. thread starter macrumors newbie

    Joined:
    Jan 6, 2011
    #8
    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):

    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];// [COLOR="Red"]here is dealloc called[/COLOR]
    	
    	[thisIsSubObject setX:7];// [COLOR="red"]so why I can still set this?[/COLOR]
    	
    	
    	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?
     
  9. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #9
    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.

    Code:
    @interface PrimObject: NSObject
    {
    	int szer;
    	int wys;
    	SubObject*mySubObject;
    }
    @property int szer,wys;
    [color=red]@property (retain) SubObject *mySubObject;[/color]
    @end
    
    @implementation PrimObject
    @synthesize szer,wys[color=red],mySubObject[/color];
    @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).
     
  10. thread starter macrumors newbie

    Joined:
    Jan 6, 2011
    #10
    thx Jiminaus! I've thooght a lot about it and my conclusions are same as your explanation. thx a lot:)
     
  11. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #11
    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".
     
  12. thread starter macrumors newbie

    Joined:
    Jan 6, 2011
    #12
    I think I start to feel it!:)

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

    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;
    -(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];// [COLOR="red"]here retain number equal 1[/COLOR]
    	NSLog(@"4 retain number: %d", [thisIsSubObject retainCount]);
    
    	[pool drain]; // [COLOR="Red"]why "thisIsSubObject" is released? I didn't sending autorelease message to thisIsSUbObject???[/COLOR]
    	
    	
        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]?
     
  13. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #13
    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.
     
  14. macrumors 603

    Joined:
    Aug 9, 2009
    #14

Share This Page