Professional Web Applications Themes

bogus retain via NSEnumerator - Mac Programming

I have noticed a strange side-effect of using the NSEnumerator object returned by NSMutableArray's objectEnumerator method. When the last object is retrieved by the array, its retain count is incremented. The following example creates an array of 4 objects, and enumerates through it twice. The output is as follows (under OS X): object 0's retain count = 1 object 1's retain count = 1 object 2's retain count = 1 object 3's retain count = 2 object 0's retain count = 1 object 1's retain count = 1 object 2's retain count = 1 object 3's retain count = 3 ...

  1. #1

    Default bogus retain via NSEnumerator

    I have noticed a strange side-effect of using the NSEnumerator object returned
    by NSMutableArray's objectEnumerator method. When the last object is
    retrieved by the array, its retain count is incremented. The following example
    creates an array of 4 objects, and enumerates through it twice. The output
    is as follows (under OS X):

    object 0's retain count = 1
    object 1's retain count = 1
    object 2's retain count = 1
    object 3's retain count = 2
    object 0's retain count = 1
    object 1's retain count = 1
    object 2's retain count = 1
    object 3's retain count = 3

    Note the extra retain count on object 3.
    What's going on here?

    Note that this program behaves as expected under GNUstep on my Linux box.

    program built using
    gcc -g r.m -framework Foundation -o r


    #import <Foundation/Foundation.h>
    #import <stdio.h>

    interface Fred : NSObject {
    int num;
    }
    -(id)init;
    -(int)num;
    end

    static int num_ = 0;

    implementation Fred
    -(id)init {
    if ((self = [super init]) != nil) {
    num = num_++;
    }
    return self;
    }
    -(int) num {return num;}
    end

    int main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSMutableArray *a;
    NSEnumerator *e;
    id obj;
    int i;

    a = [[NSMutableArray alloc] initWithCapacity: 4];
    for (i = 0; i < 4; i++) {
    obj = [[Fred alloc] init];
    [a addObject: obj];
    [obj release];
    }

    e = [a objectEnumerator];
    while ((obj = [e nextObject]) != nil)
    printf("object %d's retain count = %d\n",
    (int) [obj num], (int) [obj retainCount]);

    e = [a objectEnumerator];
    while ((obj = [e nextObject]) != nil)
    printf("object %d's retain count = %d\n",
    (int) [obj num], (int) [obj retainCount]);

    [pool release];
    return 0;
    }
    Wayne Guest

  2. #2

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c Wayne Cochran <org> wrote: 

    The retain/release memory is so badly designed that it behaves different
    on various operating systems.

    The root class NSObject with its inferior Apple designed memory management,
    is an obstacle to portability.

    David Guest

  3. #3

    Default Re: bogus retain via NSEnumerator

    On Fri, 16 Apr 2004, Wayne Cochran wrote:
     

    This is not incorrect until you identify a leak. Note that if I retain an
    object sixteen times and autorelease it sixteen times, its retain count
    will show up as 17 or higher, even though the object will be correctly
    released and everything is being managed correctly, if inefficently.

    To see *why* this is happening, consider what happens with an enumerator.
    In order to make sure the enumerator functions correctly, it retains the
    collection that it enumerates to ensure the collection doesn't get
    deallocated before the enumerator is finished. What you're seeing looks
    like a performance optimization; once the enumerator has reached the last
    object, it no longer needs to keep the collection around, but it *does*
    need to keep that last object around, so it releases the collection and
    retains the object. Once the enumerator is released, it will release the
    object I'm sure, because otherwise there would be an enormous number of
    posts saying "NSEnumerator leaks objects!"

    Try running 'leaks' on your program after the autorelease pool is
    released, and I'm sure you'll find that everything is being deallocated
    correctly.
    Michael Guest

  4. #4

    Default Re: bogus retain via NSEnumerator

    Michael Ash <com> wrote in message news:<twistedsys.net>...
     

    This brings up a question -- what if you only need to enumerate through
    part of the container (e.g., searching for some item)? I am inferring that
    release messages are not sent to the container objects until the
    enumerator has exhausted all the elements.
    No examples I see sends a release message to the enumerator, since (I assume)
    it has been autoreleased already by NSArray's objectEnumerator method.
     

    That is indeed a strange behavior. I can not imagine a scenario where you
    only needed to hang on to the last object -- what does that buy you?
     

    Your faith in these classes is strong. Perhaps, over time, I will reach that
    level of trust.
     

    I'll investigate further. Thanks for the input.

    --w
    Wayne Guest

  5. #5

    Default Re: bogus retain via NSEnumerator

    David Stes <kabel.telenet.be> wrote in message news:<LS7gc.74513$telenet-ops.be>... 
    >
    > The retain/release memory is so badly designed that it behaves different
    > on various operating systems.[/ref]

    The Cocoa version of autorelease and the one doented in
    the O'Reilly "Objective-C Pocket Reference" differ greatly. I like the idea
    of autorelease sending a release message to the object as soon
    as the callee's stack frame is destroyed -- that makes a lot sense
    and is consistent with how local object's (i.e. stack
    allocated) are deallocated in C++.

    This whole idea of autorelease pools seems a little strange to me and seems
    as error prone as performing my own explicit memory management.
    But I am new to Cocoa's memory management scheme, so the jury
    is still out.
     

    Perhaps -- I withhold my opinion for now.

    --w
    Wayne Guest

  6. #6

    Default Re: bogus retain via NSEnumerator

    On Tue, 20 Apr 2004, Wayne Cochran wrote:
     
    >
    > This brings up a question -- what if you only need to enumerate through
    > part of the container (e.g., searching for some item)? I am inferring that
    > release messages are not sent to the container objects until the
    > enumerator has exhausted all the elements.[/ref]

    Assume for a moment that you were writing NSEnumerator; how would you do
    it?

    This is what I would do: when created, the enumerator retains the array it
    enumerates, so that bad things don't happen if you release the array
    before you finish enumerating. When the enumerator reaches the end of the
    array, or the enumerator is deallocated, whichever comes first, it
    releases the array it was hanging on to. What I described is just a bit of
    an optimization on top of this basic idea.
     

    Any object you get back from a method that's not "alloc", "copy", or
    "retain" (plus the variants like mutableCopy) is autoreleased.
     
    >
    > That is indeed a strange behavior. I can not imagine a scenario where you
    > only needed to hang on to the last object -- what does that buy you?[/ref]

    Imagine this:

    NSArray *array = [[NSArray alloc] init....];
    NSEnumerator *enumerator = [array objectEnumerator];
    [array release]; // we're done with it, but the enumerator isn't

    id obj;
    while((obj = [enumerator nextObject]))
    {
    ...do lots of work here...
    }

    Using the optimization I describe above, the array will get deallocated
    before the last iteration of this while loop begins. Deallocating unneeded
    memory earlier rather than later is always a good thing.
     
    >
    > Your faith in these classes is strong. Perhaps, over time, I will reach that
    > level of trust.[/ref]

    Run 'leaks' on any Cocoa program. You'll find a few leaks, but unless the
    programmer ed up something large, they'll all be one-time leaks that
    happen at program startup. There are a lot of programs that use Cocoa
    either directly or indirectly, and enumerators are extremely common; can
    you imagine the havoc if every time you used an enumerator, you leaked an
    object?

    It helps to understand the purpose and design of the retain/release
    system. The entire point of the system is to move decisions about memory
    management from the global level "Is it safe to free this memory? Does
    somebody have a pointer to it?" to the local level "Is it safe to
    'release' this memory? Do *I* have a pointer to it?" With some exceptions,
    it pulls this off pretty well. This is why you're not supposed to use the
    -retainCount message unless you're debugging something; relying on it in
    production code means you're making global decisions about object
    lifetimes, which you're not supposed to do.

    Apple lays down some basic rules for memory management and sticks with
    them very consistently. If you alloc, copy, or retain an object, it's your
    responsibility to send it exactly one release message for each alloc,
    copy, or retain to balance. If you don't, it's not your responsibility,
    either because you're being given an object that's cached or otherwise in
    permanent residence, or because you're being given an autoreleased object.
    Because of these rules, you shouldn't start worrying about extra retains
    unless you discover that there's no balancing release; it's perfectly
    legitimate, and happens fairly often, to retain an object behind
    somebody's back just to ensure that it stays around as long as it's
    needed.

    It's not as easy as a garbage collector, but it makes sense once you get
    used to it.
    Michael Guest

  7. #7

    Default Re: bogus retain via NSEnumerator

    In article <google.com>, Wayne
    Cochran <org> wrote:
     

    You are comparing Apple's and oranges! All (repeat: ALL) objects in
    Objective C are dynamically allocated; none are allocated on the stack.
    Even in C++, dynamically allocated objects created with new are not
    automatically destroyed just because a variable that REFERENCES the
    object goes out of scope.

    The autorelease mechanism works very well once you understand it. The
    advantage is that typically a method caller who gets a pointer to an
    object back from the call doesn't have to do anything with it in terms
    of retaining it or destroying it. In C++, you would have to
    explicitly destroy the reference.

    Spence

    --
    James P. Spencer
    Rochester, MN

    "Badges?? We don't need no stinkin badges!"
    James Guest

  8. #8

    Default Re: bogus retain via NSEnumerator

    Wayne Cochran wrote: 
    > >
    > > The retain/release memory is so badly designed that it behaves different
    > > on various operating systems.[/ref]
    >
    > The Cocoa version of autorelease and the one doented in
    > the O'Reilly "Objective-C Pocket Reference" differ greatly. I like the idea
    > of autorelease sending a release message to the object as soon
    > as the callee's stack frame is destroyed -- that makes a lot sense
    > and is consistent with how local object's (i.e. stack
    > allocated) are deallocated in C++.[/ref]

    You're missing the purpose of autorelease. It's there specifically for
    the case where you *don't* want the object to go away immediately,
    although you are done with it yourself. Sending -autorelease delays the
    release, allowing others to -retain the object if they need to.

    Consider for example, the -stringByAppendingString: method of NSString.
    -stringByAppendingString: allocates a new string, but leaves it up to
    the sender to decide whether it should live beyond the current iteration
    of the run loop. If I want to build up a path by appending a bunch of
    strings, I don't need to retain any of the intermediate strings I get
    along the way: they're autoreleased, and if I don't want them to
    persist, I can just let them go away when the autorelease pool is released.
     

    In practice, the use of autorelease by the framework means that much of
    the time you won't need to bother with releasing temporary objects at all.

    -jcr
    John Guest

  9. #9

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c Wayne Cochran <org> wrote: 

    It's good to be aware of the fact that autorelease is not the way it normally
    is in Objective-C.

    Other (regular) Objective-C systems such as POC:

    http://users.pandora.be/stes/compiler.html

    do not use this bad memory management.

    David Guest

  10. #10

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c John C. Randolph <idiom.com> wrote: 

    The poster is not missing anything. Your abusive Apple-employee-language
    is completeley misplaced here.
     

    Yes, like in enumerating 2,000,000 objects and having 2,000,000 objects
    in the autoreleasepool so that memory is released only after 2,000,000
    iterations instead of immediately when the object is no longer used.

    David Guest

  11. #11

    Default Re: bogus retain via NSEnumerator

    David Stes <kabel.telenet.be> writes: 

    But unless you released the container class then the objects *are* still
    being used by the container. If you *have* released the container then you
    cant enumerate it! This seem like a nonsense argument to me.

    If you just want to run through the objecst in the container and use each one
    once, releasing it when done, then you are effectively taking them out of
    the container. If you actually do this then they will be released when you
    expect - i.e. each time round the loop.

    -bat.
    Pete Guest

  12. #12

    Default Re: bogus retain via NSEnumerator

    Pete French wrote:
     

    Welcome to David Stes' world. It's a place where NeXT never existed, POC is
    the only Objective-C implementation, up is down, black is white, and dogs
    and cats are friendly.

    He's delusional. Ignore him, maybe he'll just go away.

    sherm--

    --
    Cocoa programming in Perl: http://camelbones.sourceforge.net
    Hire me! My resume: http://www.dot-app.org
    Sherm Guest

  13. #13

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c Sherm Pendley <org> wrote: 

    POC (http://users.pandora.be/stes/compiler.html) at least doesn't use the
    crippled Apple memory management.

    The Apple memory management is especially inferior for stuff like
    enumerators, because you are forced to keep a counter of how many times you
    are autoreleasing inside a loop. The reason is that "modulo" a certain
    number of loop iterations, you have to free the pool yourself or it will
    consume all memory. In fact, it may be that you have plenty of memory left,
    but just that the stupid autoreleasepool is using all of it ...

    David Guest

  14. #14

    Default Re: bogus retain via NSEnumerator

    In article <D7Chc.81731$telenet-ops.be>,
    David Stes <kabel.telenet.be> wrote:
     
    >
    > POC (http://users.pandora.be/stes/compiler.html) at least doesn't use the
    > crippled Apple memory management.
    >[/ref]

    Instead it's crippled with not being thread safe, lack of support for
    categories, exception handling that doesn't actually implement exception
    handling (local error handling only and even that has other major errors
    like a hard-coded depth for nesting of those error handlers),
    non-standard extensions, and an author that thinks that Color should be
    a subclass of String (as should a bar or pie-wedge in a chart).
    Glenn Guest

  15. #15

    Default Re: bogus retain via NSEnumerator

    David Stes wrote: 
    >
    > The poster is not missing anything. Your abusive Apple-employee-language
    > is completeley misplaced here.[/ref]

    Abusive? If saying that someone is missing a point is abusive, then how
    would you characterize your own behavior in this newsgroup?
     
    >
    > Yes, like in enumerating 2,000,000 objects and having 2,000,000 objects
    > in the autoreleasepool so that memory is released only after 2,000,000
    > iterations instead of immediately when the object is no longer used.[/ref]

    I see that once again, you're inventing a ridiculous usage scenario.

    -jcr
    John Guest

  16. #16

    Default Re: bogus retain via NSEnumerator

    David Stes wrote: 
    >
    > POC (http://users.pandora.be/stes/compiler.html) at least doesn't use the
    > crippled Apple memory management.[/ref]

    No, it has a memory management system that can't cope with circular
    references. That's *so* much better, isn't it?
     

    If you ever bothered to find out what you were talking about, it would
    be one of the signs of the apocalypse. I've *never* had to do what you
    describe above.

    -jcr
    John Guest

  17. #17

    Default Re: bogus retain via NSEnumerator

    David Stes wrote: 
    >
    > It's good to be aware of the fact that autorelease is not the way it normally
    > is in Objective-C.[/ref]

    Your definition of the word "normally" is rather bizarre. As you
    already know, nearly all Obj-C code that exists is based on the Cocoa
    framework. You may wish this were not the case, but it is.

    -jcr
    John Guest

  18. #18

    Default Re: bogus retain via NSEnumerator

    They say insanity is when you repeatedly try the same thing and expect
    different results. Glenn and JCR, don't be insane. You'll just have the
    same argument as always; if anybody wants to see the argument, they can
    use Google Groups, we don't need to repeat it live.

    Michael Guest

  19. #19

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c John C. Randolph <idiom.com> wrote: 
    >
    > I've *never* had to do what you describe above.[/ref]

    But do you test for this condition, in your programs ?

    "Defensive programming" is about assuming that there may actually be many
    elements in a collection, so if you enumerate over them (for instance in a
    generic message) that you're not creating a bottleneck by autoreleasing tons
    of objects.
    David Guest

  20. #20

    Default Re: bogus retain via NSEnumerator

    In comp.lang.objective-c John C. Randolph <idiom.com> wrote: 

    If you ever wrote a piece of real software, you'd know that portability is
    important in the real world; regardless of what you may wish.

    It's not acceptable that a bad memory management of a lousy root class
    results in different behavior on Linux and on your Apple OS; it's not the
    fault of Linux, it's the fault of the absence of decent design in the autor
    release stuff.

    Normal Objective-C doesn't use NSObject and its memory management; this
    memory management is really very specific to the "NS" class, and NS does not
    stand for normal Objective-C.

    The NS prefix is specifically to indicate that it is a particular
    non-standard vendor specific class.

    David Guest

Page 1 of 2 12 LastLast

Similar Threads

  1. Bogus Code
    By haberdashery in forum Macromedia Dynamic HTML
    Replies: 2
    Last Post: September 20th, 06:08 PM
  2. CFMX 7 Getting Bogus DB Error
    By mooresm in forum Coldfusion Database Access
    Replies: 1
    Last Post: June 14th, 02:22 PM
  3. /var/mail BOGUS files
    By Administrateur de systemes in forum Linux / Unix Administration
    Replies: 3
    Last Post: August 17th, 04:48 AM
  4. Bogus SID hanging around
    By Dave in forum Windows Setup, Administration & Security
    Replies: 2
    Last Post: August 10th, 05:52 PM
  5. This is such a bogus article
    By George Hester in forum ASP.NET General
    Replies: 1
    Last Post: July 31st, 12:37 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139