resetting ivars safely

  • HI All,

    I am trying to find a way to safely reset or reinitialize the
    instance variables of a singleton object. A "word parser" behaves
    correctly the first couple of times it is used, but then self-
    destructs. Everything goes fine the first time through, but on the
    second or third attempt to reset the wordCandidates ivar, an EXC-BAD-
    ACCESS signal is generated just prior to releasing the ivar in its
    setter (using the release, then copy paradigm). Since the retain
    count prior to releasing is 1, I am mystified.

    Here is the relevant code. Thanks much....

    Daniel

    + (WordParser *) sharedWordParser
    {
    if (wordParser == nil) {
      wordParser = [[WordParser alloc] init];
    }
    return wordParser;
    }

    - (id) init
    {
    if (self = [super init]) {
      [self setSyllables:      [[NSMutableArray alloc] init]];
      [self setWordCandidates: [[NSMutableArray alloc] init]];
    }
    return self;
    }

    // triggered when a GUI reset button is pressed
    - (void) reset
    {
    [self setSyllables: nil]; // identically structured ivar has no
    problem for some reason
    [self setWordCandidates: nil]; // PROBLEM OCCURS HERE
    [self setCurrentCandidate: nil];
    [self setHead: nil];
    [self setTail: nil];
    [self init];
    }

    // setter for wordCandidates ivar
    - (void) setWordCandidates: (NSMutableArray *) wc
    {
    if (wc != wordCandidates) {
      printf("wordCandidates retain count prior to release is %i.\n",
    [wordCandidates retainCount]); // equal to 1
      [wordCandidates release]; // EXC-BAD-ACCESS COMES HERE
      wordCandidates = [wc mutableCopy];
    }
    }
  • On 9/12/07, Daniel Child <wchild...> wrote:
    > HI All,
    >
    > I am trying to find a way to safely reset or reinitialize the
    > instance variables of a singleton object. A "word parser" behaves
    > correctly the first couple of times it is used, but then self-
    > destructs. Everything goes fine the first time through, but on the
    > second or third attempt to reset the wordCandidates ivar, an EXC-BAD-
    > ACCESS signal is generated just prior to releasing the ivar in its
    > setter (using the release, then copy paradigm). Since the retain
    > count prior to releasing is 1, I am mystified.
    >
    > Here is the relevant code. Thanks much....
    >
    > // setter for wordCandidates ivar
    > - (void) setWordCandidates: (NSMutableArray *) wc
    > {
    > if (wc != wordCandidates) {
    > printf("wordCandidates retain count prior to release is %i.\n",
    > [wordCandidates retainCount]); // equal to 1
    > [wordCandidates release]; // EXC-BAD-ACCESS COMES HERE
    > wordCandidates = [wc mutableCopy];
    > }
    > }

    What do you see in the run log if you replace the printf above with
    the following line (is it a mutable array with objects that you
    expect, etc.)...

    NSLog(@"wordCandidates=%@", wordCandidates);

    Also are you sure the bad access is at the exact message send or does
    it happen while the target object is handling that message (in other
    words while the NSMutableArray is releasing the objects it contains,
    assuming your release triggers deallocation of the array).

    -Shawn
  • On Sep 12, 2007, at 1:59 PM, Daniel Child wrote:

    > I am trying to find a way to safely reset or reinitialize the
    > instance variables of a singleton object.

    If you feel a need to do this, should it really be a singleton?

    Very few objects should be singletons.  Even for true singletons, it's
    almost always better to make them normal, separately-instantiable
    objects -- it makes writing the unit tests that specify their behavior
    much, much easier -- and only have a "default" or "shared" instance as
    a convenience.

      -- Chris
  • On Sep 12, 2007, at 1:59 PM, Daniel Child wrote:

    > Here is the relevant code. Thanks much....

    Since your class manages a couple of mutable collections, I'd just set
    those up in -init, release them in -dealloc, and otherwise manipulate
    the collections themselves in the accessor methods.

    I also wouldn't provide an accessor to the underlying mutable array,
    only an immutable array.  Instead, I would write the appropriate KVC
    accessor methods (see <Foundation/NSKeyValueCoding.h> for details) and
    either call those directly or use -mutableArrayValueForKey: on the
    property to manipulate it from my own code.  Doing so will ensure the
    proper KVO messages are posted whenever the property is changed, which
    is important for bindings.  These methods will also be invoked
    directly by bindings (since they use -mutableArrayValueForKey:) making
    them more efficient.

    Using the KVC accessor methods also lets the compiler help you with
    type safety, assuming that your "wordCandidates" collection holds
    "Word" objects.  A tool like Accessorizer can make it really easy to
    generate these rather than write them by hand.

    Below is how I would write that code.  I'm assuming a *non*-singleton
    WordParser here, and only showing enough to give an example of what
    I'd do.

      -- Chris

    @class Word; // forward declaration

    @interface WordParser : NSObject {
    @private
        NSMutableArray *wordCandidates;
    }

    - (NSArray *)wordCandidates;
    - (void)setWordCandidates:(NSArray *)value;
    - (unsigned)countOfWordCandidates;
    - (Word *)objectInWordCandidatesAtIndex:(unsigned)index;
    - (void)insertObject:(Word *)object inWordCandidatesAtIndex:
    (unsigned)index;
    - (void)removeObjectInWordCandidatesAtIndex:(unsigned)index;

    @end

    @implementation WordParser

    - (id)init {
        if (self = [super init]) {
            // using a random value, pick a sensible default for your
    data set
            wordCandidates = [[NSMutableArray alloc] initWithCapacity:16];
        }

        return self;
    }

    - (void)dealloc {
        [wordCandidates release];

        [super dealloc];
    }

    - (NSArray *)wordCandidates {
        return wordCandidates;
    }

    - (void)setWordCandidates:(NSArray *)value {
        [wordCandidates setArray:value];
    }

    - (unsigned)countOfWordCandidates {
        return [wordCandidates count];
    }

    - (Word *)objectInWordCandidatesAtIndex:(unsigned)index {
        return [wordCandidates objectAtIndex:index];
    }

    - (void)insertObject:(Word *)object inWordCandidatesAtIndex:
    (unsigned)index {
        [wordCandidates insertObject:object atIndex:index];
    }

    - (void)removeObjectFromWordCandidatesAtIndex:(unsigned)index {
        [wordCandidates removeObjectAtIndex:index];
    }

    @end
  • On 9/12/07, Chris Hanson <cmh...> wrote:
    > On Sep 12, 2007, at 1:59 PM, Daniel Child wrote:
    >
    >> Here is the relevant code. Thanks much....
    >
    > Since your class manages a couple of mutable collections, I'd just set
    > those up in -init, release them in -dealloc, and otherwise manipulate
    > the collections themselves in the accessor methods.
    >
    > I also wouldn't provide an accessor to the underlying mutable array,
    > only an immutable array.  Instead, I would write the appropriate KVC
    > accessor methods (see <Foundation/NSKeyValueCoding.h> for details) and
    > either call those directly or use -mutableArrayValueForKey: on the
    > property to manipulate it from my own code.  Doing so will ensure the
    > proper KVO messages are posted whenever the property is changed, which
    > is important for bindings.  These methods will also be invoked
    > directly by bindings (since they use -mutableArrayValueForKey:) making
    > them more efficient.

    I encourage folks to NOT start pushing KVO, KVC, etc. on folks that
    are new to Cocoa development. ...especially in the abstract since we
    have little visibility into the intent of the Classes involved so the
    support of KVO/KVC may make little sense.

    -Shawn
  • On 13/09/2007, at 6:59 AM, Daniel Child wrote:

    > HI All,
    >
    > I am trying to find a way to safely reset or reinitialize the
    > instance variables of a singleton object. A "word parser" behaves
    > correctly the first couple of times it is used, but then self-
    > destructs. Everything goes fine the first time through, but on the
    > second or third attempt to reset the wordCandidates ivar, an EXC-
    > BAD-ACCESS signal is generated just prior to releasing the ivar in
    > its setter (using the release, then copy paradigm). Since the
    > retain count prior to releasing is 1, I am mystified.

    I suspect that you've probably got some memory management issues ---
    some extra releases somewhere else. You're missing some release/
    autorelease messages in your init method which don't look like the
    cause of the problem but suggest it's worth you having another look
    at the memory management documentation.

    I wouldn't read too much into the fact that the retain count is
    reported as 1 since the objective C implementation doesn't store the
    retain count with the object. It stores the retain count in a global
    hash if it's more than 1. The absence of the object in the hash means
    it will cause it to report 1 but that could be because your object
    has been deallocated. I'd turn on zombies and see what happens. It's
    possible that the problem lies with your singleton having been over-
    released.

    - Chris
  • The first time, retain count is 1, and the objects are all there (4
    objects in the array).
    Later on, the retain count is 0, and there are no objects.

    But doesn't that mean there is a problem with the setters paradigm of
    "release then copy"?

    On Sep 12, 2007, at 5:20 PM, Shawn Erickson wrote:

    > On 9/12/07, Daniel Child <wchild...> wrote:
    >> HI All,
    >>
    >> I am trying to find a way to safely reset or reinitialize the
    >> instance variables of a singleton object. A "word parser" behaves
    >> correctly the first couple of times it is used, but then self-
    >> destructs. Everything goes fine the first time through, but on the
    >> second or third attempt to reset the wordCandidates ivar, an EXC-BAD-
    >> ACCESS signal is generated just prior to releasing the ivar in its
    >> setter (using the release, then copy paradigm). Since the retain
    >> count prior to releasing is 1, I am mystified.
    >>
    >> Here is the relevant code. Thanks much....
    >>
    >> // setter for wordCandidates ivar
    >> - (void) setWordCandidates: (NSMutableArray *) wc
    >> {
    >> if (wc != wordCandidates) {
    >> printf("wordCandidates retain count prior to
    >> release is %i.\n",
    >> [wordCandidates retainCount]); // equal to 1
    >> [wordCandidates release]; // EXC-BAD-ACCESS COMES
    >> HERE
    >> wordCandidates = [wc mutableCopy];
    >> }
    >> }
    >
    > What do you see in the run log if you replace the printf above with
    > the following line (is it a mutable array with objects that you
    > expect, etc.)...
    >
    > NSLog(@"wordCandidates=%@", wordCandidates);
    >
    > Also are you sure the bad access is at the exact message send or does
    > it happen while the target object is handling that message (in other
    > words while the NSMutableArray is releasing the objects it contains,
    > assuming your release triggers deallocation of the array).
    >
    > -Shawn
  • It makes sense for it to be a singleton. It will eventually be
    interfacing with a component. And there is no need for "lots of
    them." The same "machine" processes streams of words one set at a
    time. No need to create a new machine for each set of data. I am
    simply clearing fields that store input or that store output
    temporarily.

    On Sep 12, 2007, at 5:48 PM, Chris Hanson wrote:

    > On Sep 12, 2007, at 1:59 PM, Daniel Child wrote:
    >
    >> I am trying to find a way to safely reset or reinitialize the
    >> instance variables of a singleton object.
    >
    > If you feel a need to do this, should it really be a singleton?
    >
    > Very few objects should be singletons.  Even for true singletons,
    > it's almost always better to make them normal, separately-
    > instantiable objects -- it makes writing the unit tests that
    > specify their behavior much, much easier -- and only have a
    > "default" or "shared" instance as a convenience.
    >
    > -- Chris
    >
  • On 9/12/07, Daniel Child <wchild...> wrote:
    > The first time, retain count is 1, and the objects are all there (4
    > objects in the array).
    > Later on, the retain count is 0, and there are no objects.
    >
    > But doesn't that mean there is a problem with the setters paradigm of
    > "release then copy"?

    In the code you posted I don't see anything that would explain the
    issue you report at least not the way you have reported.

    Note you should switch to use [NSMutableArray array] instead of
    [[NSMutableArray alloc] init] in your classes init method (the
    setSyllables: and setWordCandidates:) lines. Review the memory
    documentation to understand why (hint you are leaking those initial
    instances)

    -Shawn
  • Actually, I originally HAD [NSMutableArray array], because I thought
    it would be good to have them autoreleased. But when I ran into EXC-
    BAD-ACCESS, I thought perhaps I was losing them in an autorelease
    that came too early. Since each time I reset, I release the array,
    each alloc is matched with a release, at least as far as I can see.

    Here's the sequence:

    create word parser (one time only)
    init word parser, and in so doing, init the wordCandidate field as
    follows:
    [self setWordCandidates: [[NSMutableArray alloc] init]]; // ONE ALLOC
    after done looking at the word candidates, reset the parser so I can
    process new input, i.e.
    [self setWordCandidates: nil];
    which of course calls
    - (void) setWordCandidates: (NSMutableArray *) wc
    {
    if (wc != wordCandidates) {
      NSLog(@"wordCandidates == %@\n", wordCandidates);
      printf("wordCandidates retain count prior to release is %i.\n",
    [wordCandidates retainCount]);
      [wordCandidates release]; // ONE RELEASE
      wordCandidates = [wc mutableCopy];
    }
    }

    The release and allocs are balanced, as far as I can tell.

    the only thing I can think is that the wordCandidates holds
    NSStrings. You never really "set" the array, but rather add
    candidates as they are calculated. Here's that method.

    /
    ************************************************************************
    *********
      ** addCandidate
      **
      ** adds a candidate to the array of wlrdCandidates

    ************************************************************************
    *********/
    - (void) addCandidate: (NSString *) c
    {
    [[self wordCandidates] addObject: c]; // wordCandidates is an
    NSMutableArray
    // ALSO TRIED [wordCandidates addObject: c];
    // DO I NEED TO COPY OR RETAIN HERE?
    }

    I wonder if this is where the problem is, but since there were four
    objects registered at the time wc release is called, I discounted
    it.....

    On Sep 12, 2007, at 9:28 PM, Shawn Erickson wrote:

    > On 9/12/07, Daniel Child <wchild...> wrote:
    >> The first time, retain count is 1, and the objects are all there (4
    >> objects in the array).
    >> Later on, the retain count is 0, and there are no objects.
    >>
    >> But doesn't that mean there is a problem with the setters paradigm of
    >> "release then copy"?
    >
    > In the code you posted I don't see anything that would explain the
    > issue you report at least not the way you have reported.
    >
    > Note you should switch to use [NSMutableArray array] instead of
    > [[NSMutableArray alloc] init] in your classes init method (the
    > setSyllables: and setWordCandidates:) lines. Review the memory
    > documentation to understand why (hint you are leaking those initial
    > instances)
    >
    > -Shawn
  • On 9/12/07, Daniel Child <wchild...> wrote:
    > Actually, I originally HAD [NSMutableArray array], because I thought it
    > would be good to have them autoreleased. But when I ran into EXC-BAD-ACCESS,
    > I thought perhaps I was losing them in an autorelease that came too early.
    > Since each time I reset, I release the array, each alloc is matched with a
    > release, at least as far as I can see.
    >
    > Here's the sequence:

    Lets flatten this out...

    {
        NSMutableArray* mutableArray = [[NSMutableArray alloc] init]; //
    implicit retain because of alloc
      // you now have a mutable array instance that this code block "owns"

        [self setWordCandidates:mutableArray];

        // The implicit retain of mutableArray is not balanced with a
    release or autorelease
        // this code is NOT following proper Cocoa memory management as a result
        // This is a bug that should be fixed since it results in leaked
    array instances
    }

    - (void) setWordCandidates: (NSMutableArray *) wc
    {
        if (wc != wordCandidates) {
            // wc doesn't reference the same objects as wordCandidates so...

            [wordCandidates release];
            // The above releases "ownership" of the object that
    wordCandidates currently
            // references. If wordCandidates is nil this line does
    nothing. Note wordCandidates
            // will be nil when your singleton object is first alloc/inited.

            // The above line balances the implicit retain resulting in
    the line below
            // (in a future call to this method).

            wordCandidates = [wc mutableCopy]; // implicit retain because of "copy"
            // The above copies wc resulting in a new object that you now
    "own" and makes
            // wordCandidates reference that new object. Note if wc is nil
    then wordCandidates
            // will be set to nil as well.
        }
    }

    - (void) addCandidate:(NSString *) c
    {
    [[self wordCandidates] addObject: c];
          // The above will add the NSString instance referenced by c to the
          // mutable array referenced by wordCandidates. If wordCandidates is nil
          // the line does nothing. Note when an object is added to an array the
          // array will retain the object so that it will continue to
    exist while in the array.
    }

    Again nothing in the code you have posted above will result in the
    issue I think you are reporting.

    If you cannot figure this out using the debugger, logging and review
    of the memory management documents then you will need to post a
    complete example that demonstrates the issue you are hitting (ideally
    one that will compile and run showing the error).

    Also at the moment it is cloudy on exactly what issue(s) you are
    hitting given all of the talk about things you have tried and the
    various symptoms you reported.

    We want to help but we don't have enough of picture to understand what
    you are seeing...

    -Shawn
  • On 13/09/2007, at 8:27 AM, Chris Suter wrote:

    > I wouldn't read too much into the fact that the retain count is
    > reported as 1 since the objective C implementation doesn't store
    > the retain count with the object. It stores the retain count in a
    > global hash if it's more than 1. The absence of the object in the
    > hash means it will cause it to report 1 but that could be because
    > your object has been deallocated. I'd turn on zombies and see what
    > happens. It's possible that the problem lies with your singleton
    > having been over-released.

    I just realised that what I said about the retain count isn't
    strictly true in this instance since we're talking about
    NSMutableArray which will be using CoreFoundation. For
    CoreFoundation, the retain count is stored with the object provided
    it's less than 0x8000.

    - Chris
  • On 13.09.2007, at 03:12, Daniel Child wrote:
    > It makes sense for it to be a singleton. It will eventually be
    > interfacing with a component. And there is no need for "lots of
    > them." The same "machine" processes streams of words one set at a
    > time. No need to create a new machine for each set of data. I am
    > simply clearing fields that store input or that store output
    > temporarily.

      Don't confuse objects in an OOP language with real-world, physical
    objects: An object in OOP is simply a combination of data (=state)
    and actions that modify this data. Hence, if you find you need to
    replace 90% of the data, you might as well tear down and recreate the
    object. That way, you don't duplicate clean-up code and risk it going
    out of sync, and even if you have a -cleanUp method that's called
    both by -reset and -dealloc, it's still two separate code paths that
    may diverge by accident.

      If you have some hardware you're interfacing to, it may help to
    have separate objects for state and actual hardware control. All
    state objects would share the same hardware controller, but would
    nonetheless be separate. However, one can also over-engineer things,
    and you didn't give enough information to say for certain in your
    case. *In general*, you don't have objects that can be reset in OOP,
    and instead you create new objects (the performance implications are
    minimal, and if you find your case is different, *after profiling*,
    then you can always refactor the design).

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On 12.09.2007, at 22:59, Daniel Child wrote:
    > + (WordParser *) sharedWordParser
    > {
    > if (wordParser == nil) {
    > wordParser = [[WordParser alloc] init];
    > }
    > return wordParser;
    > }

      I presume wordParser is some sort of global variable? I'd recommend
    giving it a name like gWordParser (or sWordParser if it's static,
    which will make it invisible to people outside your file).

    > - (id) init
    > {
    > if (self = [super init]) {
    > [self setSyllables:      [[NSMutableArray alloc] init]];
    > [self setWordCandidates: [[NSMutableArray alloc] init]];
    > }
    > return self;
    > }

      You're leaking here. Setters and getters (except those referring to
    an "owner" or "delegate" (which usually is the owner)) generally
    retain their objects themselves, so nobody is releasing these arrays
    that you own because you allocate them using alloc/init.

    > // setter for wordCandidates ivar
    > - (void) setWordCandidates: (NSMutableArray *) wc
    > {
    > if (wc != wordCandidates) {
    > printf("wordCandidates retain count prior to release is %i.\n",
    > [wordCandidates retainCount]); // equal to 1
    > [wordCandidates release]; // EXC-BAD-ACCESS COMES HERE
    > wordCandidates = [wc mutableCopy];
    > }
    > }

    If wordCandidates retain count prior to release is 1, that means
    you're over-releasing somewhere. On the second call, due to your
    leak, in the constructor, the retain count of the object should be 2:
    1 for the alloc/init, 1 for the retain that setWordCandidates: does.
    Find the other places where you are releasing (or autoreleasing)
    wordCandidates, and you should have your potential problem spots.
    Post those, and we may be able to tell you which of them is wrong.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On Sep 13, 2007, at 5:13 AM, Uli Kusterer wrote:

    > On 12.09.2007, at 22:59, Daniel Child wrote:
    >> + (WordParser *) sharedWordParser
    >> {
    >> if (wordParser == nil) {
    >> wordParser = [[WordParser alloc] init];
    >> }
    >> return wordParser;
    >> }
    >
    > I presume wordParser is some sort of global variable? I'd
    > recommend giving it a name like gWordParser (or sWordParser if it's
    > static, which will make it invisible to people outside your file).
    Yes. OK, I'll do that.

    >> - (id) init
    >> {
    >> if (self = [super init]) {
    >> [self setSyllables:      [[NSMutableArray alloc] init]];
    >> [self setWordCandidates: [[NSMutableArray alloc] init]];
    >> }
    >> return self;
    >> }
    >
    > You're leaking here. Setters and getters (except those referring
    > to an "owner" or "delegate" (which usually is the owner)) generally
    > retain their objects themselves, so nobody is releasing these
    > arrays that you own because you allocate them using alloc/init.
    I now understand my mistake. I thought the "alloc" above was balanced
    by the release in the setter, but the setter also copies, so that's
    not the case. Got it.

    It seems to me there are two approaches.

    A. Use a convenience constructor....
    [NSMutableArray array] OR
    [NSMutableArray arrayWithCapacity:]
    But do I have to worry that it will get autoreleased prematurely?

    B. Use alloc init autorelease [[NSMutableArray alloc] init]
    autorelease].
    Same question: could it get released too soon?

    C. Use allloc + init and release elsewhere. But I'm not sure there is
    an elsewhere that works. Is that correct?

    >> // setter for wordCandidates ivar
    >> - (void) setWordCandidates: (NSMutableArray *) wc
    >> {
    >> if (wc != wordCandidates) {
    >> printf("wordCandidates retain count prior to release is %i.\n",
    >> [wordCandidates retainCount]); // equal to 1
    >> [wordCandidates release]; // EXC-BAD-ACCESS COMES HERE
    >> wordCandidates = [wc mutableCopy];
    >> }
    >> }
    >
    > If wordCandidates retain count prior to release is 1, that means
    > you're over-releasing somewhere. On the second call, due to your
    > leak, in the constructor, the retain count of the object should be
    > 2: 1 for the alloc/init, 1 for the retain that setWordCandidates:
    > does.
    Actually, I'm using release ... copy, but I guess it amounts to the
    same thing.

    > Find the other places where you are releasing (or autoreleasing)
    > wordCandidates, and you should have your potential problem spots.
    > Post those, and we may be able to tell you which of them is wrong.
    Will check. Thanks.
  • --- Daniel Child <wchild...> wrote:

    > A. Use a convenience constructor....
    > [NSMutableArray array] OR
    > [NSMutableArray arrayWithCapacity:]
    > But do I have to worry that it will get autoreleased
    > prematurely?
    >
    > B. Use alloc init autorelease [[NSMutableArray
    > alloc] init]
    > autorelease].
    > Same question: could it get released too soon?

    You should read the Cocoa memory management
    documentation.

    http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Concep
    ts/ObjectOwnership.html#//apple_ref/doc/uid/20000043-BEHDEDDB


    Cheers,
    Chuck


    ____________________________________________________________________________________
    Boardwalk for $500? In 2007? Ha! Play Monopoly Here and Now (it's updated for today's economy) at Yahoo! Games.
    http://get.games.yahoo.com/proddesc?gamekey=monopolyherenow
  • Thanks for the suggestion. I did. It is helpful on accessors and the
    basic notion of ownership. ("X created or copied it so X needs to
    release it.") But I don't find that documentation terribly clear on
    some of the scenarios I have been dealing with. My two main scenarios
    requiring attention to memory have been

    - reinitializing or resetting instance variables
    - adding objects to a collection, which itself is an instance variable

    There must be better articles out there.....

    On Sep 13, 2007, at 12:41 PM, Charles Steinman wrote:

    > You should read the Cocoa memory management
    > documentation.
    >
    > http://developer.apple.com/documentation/Cocoa/Conceptual/
    > MemoryMgmt/Concepts/ObjectOwnership.html#//apple_ref/doc/uid/
    > 20000043-BEHDEDDB
  • On 9/13/07, Daniel Child <wchild...> wrote:
    > Thanks for the suggestion. I did. It is helpful on accessors and the
    > basic notion of ownership. ("X created or copied it so X needs to
    > release it.") But I don't find that documentation terribly clear on
    > some of the scenarios I have been dealing with. My two main scenarios
    > requiring attention to memory have been
    >
    > - reinitializing or resetting instance variables

    This is an arbitrary qualification of yours that doesn't affect the
    memory management "rules".

    > - adding objects to a collection, which itself is an instance variable

    Again this is an arbitrary qualification of yours that doesn't affect
    the memory management "rules".

    -Shawn
  • I should note that it doesn't seem to have clicked how simple the
    memory management rules really are. It may help to just focus on the
    simple rules that are stated on the following page.

    <file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/MemoryMgmt/index.html#//apple_ref/doc/uid/10000011i>

    In particular the two I quoted below (these two are often all you need
    to consider)...

    ---

    This is the fundamental rule:

    You take ownership of an object if you create it using a method whose
    name begins with "alloc" or "new" or contains "copy" (for example,
    alloc, newObject, or mutableCopy), or if you send it a retain message.
    You are responsible for relinquishing ownership of objects you own
    using release or autorelease. Any other time you receive an object,
    you must not release it.

    The following rules derive from the fundamental rule, or cope with edge cases:

    As a corollary of the fundamental rule, if you need to store a
    received object as a property in an instance variable, you must retain
    or copy it. (This is not true for weak references, described at "Weak
    References to Objects", but these are typically rare.)

    ---

    Also... the only real difference between applying the "rules" to an
    instance variable (ivar) and a local variable (lvar) is that you need
    to consider life time / scope of that reference. Instance variables
    exist for the life of the object that they are part of while local
    variables only exist for the life of the block they are in.

    - ivars need to follow the "rules" when assignments are made to them
    and when the object, that the ivar is part of, is created/destroyed
    (e.g. init and dealloc).

    - lvars need to follow the "rules" when assignments are made to them
    and when the block they are in comes into exists or goes away.

    Finally make sure you understand that an object typed (NSArray*, id,
    etc.) lvar or ivar only _points_ at an instance of object and the
    object it points at is in no way tied to the pointers that point at
    it. Related to this is the fact that a _pointer_ to an object isn't
    useful unless it actually points at an object (for example ivars are
    set to zero, aka nil, when the object they are part of is first
    allocated). This is why in "init" methods you would normally create
    the object instances that you need to have around during the life of
    your object.

    -Shawn
  • On 9/13/07, Daniel Child <<email_removed>> wrote:
    > Thanks for the suggestion. I did. It is helpful on accessors and the
    > basic notion of ownership. ("X created or copied it so X needs to
    > release it.") But I don't find that documentation terribly clear on
    > some of the scenarios I have been dealing with. My two main scenarios
    > requiring attention to memory have been
    >
    > - reinitializing or resetting instance variables

      Implement accessors.  Put memory management logic exclusively in accessors.  Implement reinitialization by calling the accessors to set instance varaible.  Don't touch the instance varaibles directly accept in the accessors.

      > - adding objects to a collection, which itself is an instance variable

      The collection classes obey Cocoa memory management conventions, so you shouldn't care what the collections do with memory management.  If you follow the conventions, you will be fine.

      Finally, autoreleased objects are not released or deallocated at random times.  They are released when the most nested autorelease pool that contains the objects is itself deallocated.  he AppKit manages a top level autorelease pool that is allocated and released by the event loop.
  • --- Daniel Child <wchild...> wrote:

    > Thanks for the suggestion. I did. It is helpful on
    > accessors and the
    > basic notion of ownership. ("X created or copied it
    > so X needs to
    > release it.")

    I would suggest you look it over again. That document
    answers your question about whether you have to worry
    about autoreleased objects being freed prematurely.
    I'm not trying to be rude -- it just seems like you
    aren't completely clear on how it works.

    Cheers,
    Chuck


    ____________________________________________________________________________________
    Be a better Heartthrob. Get better relationship answers from someone who knows. Yahoo! Answers - Check it out.
    http://answers.yahoo.com/dir/?link=list&sid=396545433
  • On 13.09.2007, at 17:24, Daniel Child wrote:
    > It seems to me there are two approaches.
    >
    > A. Use a convenience constructor....
    > [NSMutableArray array] OR
    > [NSMutableArray arrayWithCapacity:]
    > But do I have to worry that it will get autoreleased prematurely?

      Read up on the memory contract. Unless you create an autorelease
    pool in your method, there's only one outside your function, so
    that's the earliest time at which any of these objects can be
    released [1].

    > B. Use alloc init autorelease [[NSMutableArray alloc] init]
    > autorelease].
    > Same question: could it get released too soon?

      You're autoreleasing it. Are you creating and releasing a pool? No?
    Then it is guaranteed to stay around for the duration of your method
    call, because the first pool can be outside your method, at the
    earliest.

    > C. Use allloc + init and release elsewhere. But I'm not sure there
    > is an elsewhere that works. Is that correct?

      Well, since your setter retains the object, you could do (pseudocode):

    myFoo = [[NSFoo alloc] init]
    [self setFrobnitz: myFoo]
    [myFoo release]

    and while your function shouldn't use the object afterwards
    ("release" means "I'm done using this object" -- unless you know you
    hold another reference to this object, e.g. since you retained your
    object twice, you shouldn't use the object after a release).

    > Actually, I'm using release ... copy, but I guess it amounts to the
    > same thing.

      Yes. copy creates an object that you own, retain acquires ownership
    of an object, so for this discussion they're exchangeable. But you're
    right, to be more exact, I should have simply said that the setter
    acquires ownership.

    1) After all, these are convenience constructors, not methods that
    give you an object owned by the receiver, so you don't have to worry
    about destroying the receiver. If they weren't convenience
    constructors, like -objectAtIndex:, the object could go away earlier,
    when you release the array that contains the object. But this is not
    the case here.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On 13.09.2007, at 18:15, Daniel Child wrote:
    > This option seems to work, and the "word parser" (kind of a
    > misnomer) now works indefinitely without crashing.

      If it's a misnomer, rename it. You'll be writing your code once,
    but you'll be re-reading it over and over again during debugging.
    Trust me, you'll want your code to be as readable as possible.
    Anything else will just make you eventually throw away solid and
    tested code because it's become too un-manageable to fix that one
    tiny bug. And that's just wasteful.

    > I can go through an indefinite number of cycles of "get candidates"
    > "reset" without any hiccups.

      Still, check your retains and releases. If you got a BAD_ACCESS,
    that means you had an object that went out of scope where you didn't
    expect it to. Such memory bugs are hard to track down, because
    disposed memory doesn't get cleared, so the object may look
    completely valid a moment after it's been released. Your new code
    probably just caused things to move around in RAM in a way that the
    released objects didn't get overwritten as quickly. The issue has
    just been hidden, but it's unlikely that it is actually gone.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
previous month september 2007 next month
MTWTFSS
          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
Go to today