Memory Allocation Question with details

Discussion in 'iOS Programming' started by linuxaos, May 17, 2010.

  1. linuxaos macrumors newbie

    Joined:
    May 17, 2010
    #1
    Hello everyone. I have a test program which just allocates one object and copies it. I get a leak though and I thought those with more experience might help me. This is all command line, BTW.

    Here is the program, "simple.m":

    Code:
    [COLOR="Blue"]  1 //
      2 //
      3 #import <Foundation/Foundation.h>
      4 
      5 int main(int argc, char *argv[])
      6 {
      7     NSString *s1, *s2;
      8     s1=nil;
      9     s2=nil;
     10 
     11     NSLog(@"s1 retainCount: %u\n\n", [s1 retainCount]);
     12     NSLog(@"s2 retainCount: %u\n\n", [s2 retainCount]);
     13     //
     14     s1 = [[NSString alloc] initWithString:@"Hello There"];
     15     NSLog(@"s1: %@\n\n", s1);
     16     NSLog(@"s1 retainCount: %u\n\n", [s1 retainCount]);
     17     //
     18     s2 = [s1 stringByAppendingString:@" Computer"];
     19     NSLog(@"s2: %@\n\n", s2);
     20     NSLog(@"s1 retainCount: %u\n\n", [s1 retainCount]);
     21     NSLog(@"s2 retainCount: %u\n\n", [s2 retainCount]);
     22     [s1 release];
     23     [s2 release];
     24 
     25     return 0;
     26 }[/COLOR]
    I get this output:

    Code:
    [COLOR="Red"]gcc -framework Foundation -o simple simple.m[/COLOR]
    2010-05-18 00:56:45.769 simple[49262:903] s1 retainCount: 0
    
    2010-05-18 00:56:45.771 simple[49262:903] s2 retainCount: 0
    
    2010-05-18 00:56:45.771 simple[49262:903] s1: Hello There
    
    2010-05-18 00:56:45.771 simple[49262:903] s1 retainCount: 4294967295
    
    2010-05-18 00:56:45.772 simple[49262:903] ***
        __NSAutoreleaseNoPool(): Object 0x1001105d0 of class NSCFString
        autoreleased with no pool in place - just leaking
    2010-05-18 00:56:45.772 simple[49262:903] s2: Hello There Computer
    
    2010-05-18 00:56:45.772 simple[49262:903] s1 retainCount: 4294967295
    
    2010-05-18 00:56:45.773 simple[49262:903] s2 retainCount: 1
    So, my question is that it seems that there is some kind if inadvertent autorelease going on in line: 18

    Why? How?

    If this is a totally stupid question please forgive me.
     
  2. linuxaos thread starter macrumors newbie

    Joined:
    May 17, 2010
    #3
    Believe me I know this document (and memorized it) but it does not answer this particular question. Further, if I create a "NSAutoreleasePool" it will make the message go away but it will not tell me why there is an autorelease going on.

    This might seem like a simple (and possibly stupid question) however for an iPhone application it has serious consequences. I mean isn't this a commonplace code fragment:

    Code:
    		NSString *s;
    
    
    		s = [NSString stringWithString:@"First piece"];
    		s = [s stringByAppendingString:@" -- second piece"];
    
    What is wrong with that code? I can't figure it out.
     
  3. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #4
    First of all, the code snippet you gave in your last post and the way the code works in your original post are very different. In your last snippet, you have two autoreleased NSStrings, and in your original example, s1 is not autoreleased while s2 is. This means that you should not call release on s2 in your original example, because you have not retained it to begin with. It is necessary to release s1 since you created it (alloc/init) yourself. Your second snippet is technically OK since both strings are being autoreleased and you don't call release on anything.

    The reason you are getting the error about autoreleasing with no pool is because....well, something is being autoreleased (s2 when it is created in the stringByAppendingString method), but you never created an autorelease pool. You must explicitly create and release an autorelease pool if you are using Cocoa in a command line application. If you use XCode to create a Command Line Tool project with its Type set to Foundation, you will see that it gives you the code. You do not have to do this in a normal iPhone application as the OS takes care of it for you.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    Your memorization of the guide is faulty. You should reread the guide and pay close attention to the rules of ownership.

    Your originally posted code is wrong in several places.

    The section in the guide on "Autorelease Pools in Non-AppKit Programs" makes it clear that an autorelease pool is required. Your main() doesn't have one. Therefore, your main() is wrong for not having an active pool.


    In this code:
    Code:
    s = [NSString stringWithString:@"First piece"];
    s = [s stringByAppendingString:@" -- second piece"];
    
    You are not calling a method that starts with "new" or "alloc", or contains "copy". Therefore, you do not own the returned objects. If any object is returned that you don't own, then someone else owns it.

    The "someone else" might be another object, which is dispensing the returned object and retains ownership. Or the "someone else" might be an autorelease pool, which the dispensing object has transferred ownership to, after creating a returnable object on your behalf.

    You can't tell who the "someone else" is. All you know is whether you own the returned object or not. This is fundamental to memory management: Do you own the object?

    If you don't own the object yet you want it to survive so you can use it, then you must claim ownership by calling retain. If you don't do this, then there is no way of knowing when that object might disappear out from under you. That's what happens when you don't own something: it can be destroyed without your knowledge.

    Furthermore, if you don't own the returned object, then you MUST NOT release it. Your originally posted code did this with an object it didn't own. Therefore it's wrong for releasing an object it didn't own.


    Any class can autorelease any object, if and when it wants to relinquish ownership of that object, but defer the actual deallocation of that object until later. This is explained in the guide's section on autorelease pools, which I suggest you reread.

    Referring again to this code:
    Code:
    s = [NSString stringWithString:@"First piece"];
    s = [s stringByAppendingString:@" -- second piece"];
    
    Exactly who owns the strings being returned?

    If you've memorized the rules from the guide, then you know you're not the owner, because the method names don't start with "new" or "alloc", or contain "copy".

    So who owns the objects?
     
  5. linuxaos thread starter macrumors newbie

    Joined:
    May 17, 2010
    #6
    admanimal: Thank you for the very informative answer. From your post and other info, it seems that this is what's going on:

    In the original post, line 14 should be:
    Code:
     14     s1 = [[[NSString alloc] initWithString:@"Hello There"] autorelease];
    
    It doesn't have to be, actually the two versions of the line accomplish the same thing. However, getting to the warning message, line 18:
    Code:
     18     s2 = [s1 stringByAppendingString:@" Computer"];
    what going on here is: "stringByAppendingString" creates an autoreleased string. This the "autorelease" that the warning message was about.

    Why worry about such things? Well one of the iPhone books I am reading:"Beginning iPhone Development. Exploring the iPhone SDK. Dave Mark, Jeff LaMarche", warns against relying on autoreleased objects. Specifically, on page 40:
    Code:
    The memory allocated for an autoreleased object will stay allocated for some
    period of time after we’re done with it. On Mac OS X, with swap files and
    relatively large amounts of physical memory, the cost of using autoreleased
    objects is nominal, but on iPhone, these objects can have a detrimental
    effect on your application’s memory footprint.
    
    It is OK to use autorelease, but try to use it only when you really need to,
    not just to save typing a line or two of code.
    Actually the program, in my original post, was designed as a test of the autorelease mechanism and my attempt to understand exactly how these things work.

    Now, this begs the question: If I am within a view and one of my custom methods uses heavy NSString manipulation, should I create a custom autorelease pool?

    I don't see this as being used by iPhone programmers, except in threads.

    Any thoughts?
     
  6. linuxaos thread starter macrumors newbie

    Joined:
    May 17, 2010
    #7
    chown33, thank you for your reply. The problem I have with the guide is that it tends to make sense after you completely understand it. You know what I mean?

    My digital bookcase has no less than three iPhone Programing Books, two Objective-C books and about one hundred C books. And I also have Google.

    A lot of what's going on, inside the iPhone, needs to be hacked at in order to become transparent. Reading the guide+books is a good starting point but a lot of the information is dry unless you use it and see it for yourself. At least that's how my mind works.

    Also, for me, it is better when I hash out my thoughts in a forum, such as this one. It helps me think better when I am having a discussion.

    As far as this particular problem is concerned, I was getting all sorts of info on what's appropriate for autorelease pools, on the iPhone.

    The little program in my first post was only meant to be an test.

    Your post and the post of admanimal hels me get a good view on what's going on. Thank you!
     
  7. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    There's a ton a lot of removable redundancy in that line. Consider the more concise and still correct:
    Code:
    s1 = @"Hello There";
    The @"" notation is shorthand for a constant NSString*. That means it's already an object. Anything else you do with it isn't going to make it more of an object than it already is.

    See this for more tips:
    http://www.cocoadev.com/index.pl?NSString

    Also google for:
    objective-c nsstring constant


    Don't do premature optimization.

    First, exactly what constitutes "heavy" NSString manipulation? Thousands of operations? Millions? Exactly what manipulations?

    Second, it depends on when and where your method is being called. The runloop creates and then drains an autorelease pool when handling events. So there may already be a short-lived pool in place. Creating another one improves nothing.

    Third, not all string manipulation needs or creates autoreleased objects. For example, many operations on NSMutableString will modify the mutable object itself rather than creating autoreleased objects.

    Fourth, unless you have profiled your working code and have obtained evidence of a problem, attempting to solve it is premature. You should be concerned first with writing correct code. After it's correct, then you can concern yourself with fixing any performance issues it might have.

    If the code has no performance issues, either in temporary memory consumption or in speed, then there isn't a problem that needs to be fixed, so why fix it?
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    Yes, I do. That's why I suggest rereading it, even after you think you understand it. Maybe even especially when you think you understand it. I frequently reread references to make sure I haven't made mistakes, or to see if there's some insight I've missed or forgotten.

    It reminds me of something an instructor once told me:
    One way to really understand a subject is to try explaining it to someone else.

    That focused action of mentally creating an explanation, then articulating it in a cogent way, leads to a clearer understanding of the subject. This is also the basis of rubber duck debugging:
    http://en.wikipedia.org/wiki/Rubber_duck_debugging

    It's also why simply writing down a description of a problem for posting to a forum, including the actual code, can lead one to discovering the problem without ever posting it.
     
  9. linuxaos thread starter macrumors newbie

    Joined:
    May 17, 2010
    #10
    I was only being extremely verbose to show that the "autorelease" was there.

    The reason, for my paranoid memory allocation issues, is that my project is getting big (close to 10k lines) and I was just going over the code for memory leaks. I ran the code with the Leaks Performance Tool and I was getting many leak indicators. Some were stupid mistakes but some are still puzzling.

    So, I thought I'd start with some obvious examples, using NSString as a test, and take it from there. So this is all a part of preventive maintenance.

    Thanks to this simple post, I know a lot more about string manipulation details, from you and admanimal. And for that, I thank you. Seriously!

    How do you use the Leaks tool?
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    Have you tried using the Build and Analyze tool yet? (Make sure you do a Clean first).
     
  11. linuxaos thread starter macrumors newbie

    Joined:
    May 17, 2010
    #12
    N-I-C-E TOOL!!! Thanks dejo. Running it now. Should be done by the morning, EST :)
     

Share This Page