Memory leak by calling method which returns object..?

Discussion in 'Mac Programming' started by TurboLag, Dec 2, 2008.

  1. TurboLag macrumors member

    Joined:
    Feb 24, 2004
    #1
    I am curious, if you call a method which returns an object, but you dont store it anywhere, does it still exist on the stack forever, creating a memory leak?

    Ex.

    - (NSString *)getString; //method prototype

    [myObject getString];
     
  2. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #2
    There is no way to return an Objective-C object. You can only return a _pointer_ to an Objective-C object. Whether the object behind the pointer continues to exist depends on its retain count. You'll have to read the Cocoa documentation about when to release or not release objects. According to Cocoa guidelines, a method named "getString" will return an existing object without increasing its retain count (or an autoreleased object), so you need to retain it if you want to keep it, or do nothing if you don't want to keep it.
     
  3. TurboLag thread starter macrumors member

    Joined:
    Feb 24, 2004
    #3
    Ah, yes your right, only the pointer gets passed.

    I guess what I am asking is, is the memory required for the pointer (4/8 bytes) leaked when it gets pushed on the stack, and nothing pops it?
     
  4. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #4
    When a method returns a pointer, you need a place to store it. That means the calling method needs to have a pointer already defined to store the address that is returned. If you use the same pointer over and over again, you may be leaking memory for another reason since you won't have a way to send release to an object you no longer have a reference to. However, if you never send retain to these objects, you should be fine.

    Code:
    NSString *x = nil;
    x = [MyObject getString];
    NSLog(x);
    x = [MyObject getString];
    NSLog(x);
    x = [MyObject getString];
    
    x is on the stack, using up some number of bytes based on the platform. You've assigned to it 3 times, but it's always using the same memory to store the value you've assigned. What's on the other end of that pointer is a totally different story. gnasher729 pointed out that by convention getString would return something without increasing that object's retain count, and simply assigning its pointer to a variable will not do anything to change that.

    It may help to think of it like this. You have declared an int in a function. That int is on the stack. You assign 5 to this int, then 7, then 9, then 34032. That int isn't using extra memory on the heap (or stack) because you assigned things to it. A pointer will work the same way.

    -Lee
     
  5. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #5
    How on earth are you getting into a situation where nothing pops the stack?
     
  6. TurboLag thread starter macrumors member

    Joined:
    Feb 24, 2004
    #6
    I dont think Im making myself clear.

    Lets say there is a method which does something (which I need to happen), and then passes a pointer to an object, which I dont care about.

    - (NSString *)method{
    //do something here
    return @"a pointer to this string is pushed onto stack";
    }

    // in my code...
    [self method];
    //I call the method, it does what it should, then passes back something to //which I dont care for. Does the pointer still remain on the stack, causing a //leak?
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    If you don't do anything with what is returned, nothing is placed on the stack or the heap (where would it go?). The memory address that is returned is what the expression:
    Code:
    [self method]; 
    evaluates to, but since this expression isn't used as the right hand value of an assignment, etc. it just "goes away". Here is a simple C program:
    Code:
    int getInt(void);
    
    int main(int argc, char *argv[]) {
      getInt();
    }
    
    int getInt(){
      return 5;
    }
    
    Here is the assembly that is produced:
    Code:
            .file   "testret.c"
            .text
    .globl main
            .type   main, @function
    main:
            leal    4(%esp), %ecx
            andl    $-16, %esp
            pushl   -4(%ecx)
            pushl   %ebp
            movl    %esp, %ebp
            pushl   %ecx
            subl    $4, %esp
            call    getInt
            addl    $4, %esp
            popl    %ecx
            popl    %ebp
            leal    -4(%ecx), %esp
            ret
            .size   main, .-main
    .globl getInt
            .type   getInt, @function
    getInt:
            pushl   %ebp
            movl    %esp, %ebp
            movl    $5, %eax
            popl    %ebp
            ret
            .size   getInt, .-getInt
    
    Nothing is done back in main with %eax after the call to getInt. If another function was called, %eax would just be reused (hard to think of a stack in this case, since registers are being used, but the result is the same) as needed.

    In your example, you use an NSString literal as the return value. NSString literals are auto-released as far as I know, so in your example you should be fine memory-wise. Are you seeing behavior that makes you think otherwise?

    Based on your comment, i think your discussion about the stack is in regard to how the return value is passed back to the caller. This is implementation specific, but you don't need to worry about leaking memory by calling non-void functions and discarding their return values.

    -Lee
     
  8. TurboLag thread starter macrumors member

    Joined:
    Feb 24, 2004
    #8
    Great answer, lee1210.

    It is out of curiosity. I just wanted to know if it creates a leak, as there are a few occasions where I call a method, and ignore its return.
    Thanks!
     
  9. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #9
    You have to be careful if you are calling things that return a "new" Object, with a retain count. This seems odd, but if you did:
    Code:
    NSString *myString = @"My string";
    [myString copy];
    [myString copy];
    [myString copy];
    
    you'd have a leak on your hands. You are safe to discard return values if they are primitives, or if they return pointers to objects that you don't own. Methods with names containing:
    alloc
    copy
    copyWithZone
    new

    Return something that you own, so if you discard their return values you will be leaking. Not because you're not doing something special with the pointer returned, but because you've discarded this pointer, so you will have no way to send release to these objects.

    -Lee
     
  10. Soulstorm macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #10
    One thing you may have forgotten is that Cocoa provides autorelease pools that release autorelased objects when the Cocoa runtime decides it. In methods where you return pointers to objects, it is wiser to return autoreleased objects. Like this:

    HTML:
    - (NSString *)foo(){
    	NSString *str = [[NSString alloc]initWithString:@"hello world!"];
    	return [str autorelease];
    }
    That way the string you created will be returned, used by you, and when it's no longer needed, it will be thrown away the next time Cocoa decides to release its internal autorelease pools. So, you won't need to worry about deallocating a non-needed object.
     

Share This Page