NSAutoreleasePool and static data member constructors

  • Hi,

    I have a C++/Objective-C application which has some constructors for
    static data members accessing autorelased Objective-C objects and I
    get messages like this in the console:
    * _NSAutoreleaseNoPool(): Object 0xd06e70 of class NSCFString
    autoreleased with no pool in place - just leaking

    Obviously, I need to setup an autorelase pool before all the static
    member construction. Is there a way to do this?

    TIA

    Eyal Redler
    ======================================
    RedleX - Makers of Mellel
    www.mellel.com
    <eyal...>
    <eyredler...>
    Work: 972-3-5226849
    Mobile: 972-542-585545
  • > I have a C++/Objective-C application which has some constructors for
    > static data members accessing autorelased Objective-C objects and I
    > get messages like this in the console:
    > * _NSAutoreleaseNoPool(): Object 0xd06e70 of class NSCFString
    > autoreleased with no pool in place - just leaking
    >
    > Obviously, I need to setup an autorelase pool before all the static
    > member construction. Is there a way to do this?

    No.
    Constructors for C++ static data members are all called before main
    ().  You could create an NSAutorleasePool within a static data
    member's constructor, but the ANSI/C++ standard does not define any
    order for static data member constructors to be called, and in fact,
    every time you link your application, the order will likely change.

    My advice to you is to not mix Objective-C and C++ in the way you are
    attempting.  Keep the separation clear.  If possible, do not use any
    non-pointer instances of C++ classes as instance variables of
    Objective-C classes or visa versa.  The implementation of C++ classes
    should access Objective-C objects by pointer only (obviously), and
    the reverse is also true.  Objective-C methods should access C++
    instances by pointer only.

    GIVEN THE FOLLOWING:
    @class    Bar;

    class Foo
    {
        Bar        *bar;

        Foo() {this->bar = nil;};
        virtual ~Foo() {[bar release];};
        virtual GetBar() {return bar;};
        virtual SetBar(Bar *aBar) {[aBar retain];[bar release]; bar =
    aBar;};
    };

    GOODE:
    @interface Bar : NSObject
    {
        Foo        *foo;
    }

    @end

    BAD:
    @interface Bar : NSObject
    {
        Foo        foo;  // constructor will be called
    }

    @end

    GIVEN THE FOLLOWING:
    @class    Bar;

    class Foo
    {
        Bar        *bar;

        Foo() {this->bar = [[Bar alloc] init];};
        virtual ~Foo() {[bar release];};
        virtual GetBar() {return bar;};
        virtual SetBar(Bar *aBar) {[aBar retain];[bar release]; bar =
    aBar;};
    };

    GOODE:
    static Foo    *foo = NULL;

    int main(int argc, const char *argv[])
    {
        NSAutoreleasePool    *outerPool = [[NSAutoreleasePool alloc] init];
        foo = new Foo;
        [outerPool release];
    }

    BAD:
    static Foo    foo;  // Note: this is generally bad when constructors
    have side effects in straight C++ too
  • On Oct 10, 2007, at 15:20, Erik Buck wrote:

    >> I have a C++/Objective-C application which has some constructors for
    >> static data members accessing autorelased Objective-C objects and I
    >> get messages like this in the console:
    >> * _NSAutoreleaseNoPool(): Object 0xd06e70 of class NSCFString
    >> autoreleased with no pool in place - just leaking
    >>
    >> Obviously, I need to setup an autorelase pool before all the static
    >> member construction. Is there a way to do this?
    >
    > No.
    > Constructors for C++ static data members are all called before main
    > ().  You could create an NSAutorleasePool within a static data
    > member's constructor, but the ANSI/C++ standard does not define any
    > order for static data member constructors to be called, and in
    > fact, every time you link your application, the order will likely
    > change.
    >
    > My advice to you is to not mix Objective-C and C++ in the way you
    > are attempting.  Keep the separation clear.  If possible, do not
    > use any non-pointer instances of C++ classes as instance variables
    > of Objective-C classes or visa versa.  The implementation of C++
    > classes should access Objective-C objects by pointer only
    > (obviously), and the reverse is also true.  Objective-C methods
    > should access C++ instances by pointer only.

    Thanks Eric, I am not using non pointer C++ objects inside objective
    c objects but there are some C++ objects which access services that
    are implemented in Objective-C and are auto-releasing objective C
    objects.

    I was hoping to find some sort of "pre startup" opportunity when the
    executable is loaded in order to create the initial auto-release pool
    there, isn't there some routine that gets called to initialize the
    executable?

    Avoiding using static non pointer objects would be the best solution,
    I guess, but I'm porting code from windows and doing this would be
    quite radical.
  • On 10/10/07, Erik Buck <erik.buck...> wrote:
    >> I have a C++/Objective-C application which has some constructors for
    >> static data members accessing autorelased Objective-C objects and I
    >> get messages like this in the console:
    >> * _NSAutoreleaseNoPool(): Object 0xd06e70 of class NSCFString
    >> autoreleased with no pool in place - just leaking
    >>
    >> Obviously, I need to setup an autorelase pool before all the static
    >> member construction. Is there a way to do this?
    >
    > No.
    > Constructors for C++ static data members are all called before main
    > ().  You could create an NSAutorleasePool within a static data
    > member's constructor, but the ANSI/C++ standard does not define any
    > order for static data member constructors to be called, and in fact,
    > every time you link your application, the order will likely change.
    >
    > My advice to you is to not mix Objective-C and C++ in the way you are
    > attempting.  Keep the separation clear.  If possible, do not use any
    > non-pointer instances of C++ classes as instance variables of
    > Objective-C classes or visa versa.  The implementation of C++ classes
    > should access Objective-C objects by pointer only (obviously), and
    > the reverse is also true.  Objective-C methods should access C++
    > instances by pointer only.

    In general, I agree. However, I'd argue that when using C++ objects as
    Objective-C instance variables they should fall into one of these
    categories:
    1) "value" types. (i.e. Types that present an interface similar to
    built-in types std::string, big num types, etc.)
    2) Containers (i.e. std::vector, std::set, etc.)
    3) Smart pointer types (shared_ptr, auto_ptr, etc.)

    but never raw pointers. Raw pointers to C++ objects should not be used
    as Objective-C instance variables. Additionally, except for simple,
    transient Obj-C objects, the C++ objects should be "owned" by the
    Objective-C objects, not the other way around.

    --
    Clark S. Cox III
    <clarkcox3...>
  • Am 10.10.2007 um 16:28 schrieb Clark Cox:
    > but never raw pointers. Raw pointers to C++ objects should not be used
    > as Objective-C instance variables. Additionally, except for simple,
    > transient Obj-C objects, the C++ objects should be "owned" by the
    > Objective-C objects, not the other way around.

      Errr... I'm not sure whether that was a 10.3-only limitation, but I
    distinctly remember that Objective C did not call C++ constructors
    for its instance variables. The compiler option to activate this has
    been around for a while, but AFAIK the C++ runtime currently shipping
    with 10.4 doesn't support that option yet. So, I don't think you can
    use C++ smart pointers and the likes as instance variables. You /have
    to/ use pointers and new and delete them properly.

      For static and global variables, one really can not rely on
    initialization order (except for constant initializer values), and
    for that reason one shouldn't create any more complex object in an
    initializer for one of these variables. This applies not only to
    mixed-language settings, but also to any "pure" C++ code. What one
    should really do is

    a) use lazy initialization upon first use (go through an accessor to
    get at a singleton, not use the global directly)
    b) be darned careful to avoid circular dependencies

    If b is not an option, one should at least do the initialization
    after main() has been entered. Anything else just isn't safe or
    reliable, nor deterministic enough. Once you've done that, you have a
    function that gets called, and in that function you can set up and
    tear down an autorelease pool for that one call. And as with other
    uses of ObjC, any objects you need to keep around yourself outside
    this pool should be retained.

    It is not a good idea to try and create a "global autorelease pool"
    outside main, or for that matter even inside main, because this is
    essentially the same as a leak. Any objects added to this pool will
    stay around and won't be released until your application quits. All
    you do is shut up the console message, but you're not plugging the
    actual leak.

    Also, watch out for exceptions when integrating C++ and Objective C.
    Neither C++ nor ObjC like it when you throw and exception of either
    type through code of the other type, just like you shouldn't throw an
    exception through OS code. This may also be difficult, as the
    standard CarbonEventHandlers installed on Cocoa windows in a Carbon
    app don't install exception handlers before they call into Cocoa, so
    you'll have to fix that yourself.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • C++ objects as part of ObjC objects works OK in 10.4 (they are
    properly constructed/destructed), if you enable a compiler flag.
    It's 10.3 where it won't work.

    On Oct 10, 2007, at 12:39 PM, Uli Kusterer wrote:

    > Am 10.10.2007 um 16:28 schrieb Clark Cox:
    >> but never raw pointers. Raw pointers to C++ objects should not be
    >> used
    >> as Objective-C instance variables. Additionally, except for simple,
    >> transient Obj-C objects, the C++ objects should be "owned" by the
    >> Objective-C objects, not the other way around.
    >
    > Errr... I'm not sure whether that was a 10.3-only limitation, but
    > I distinctly remember that Objective C did not call C++
    > constructors for its instance variables. The compiler option to
    > activate this has been around for a while, but AFAIK the C++
    > runtime currently shipping with 10.4 doesn't support that option
    > yet. So, I don't think you can use C++ smart pointers and the likes
    > as instance variables. You /have to/ use pointers and new and
    > delete them properly.
    >
    > For static and global variables, one really can not rely on
    > initialization order (except for constant initializer values), and
    > for that reason one shouldn't create any more complex object in an
    > initializer for one of these variables. This applies not only to
    > mixed-language settings, but also to any "pure" C++ code. What one
    > should really do is
    >
    > a) use lazy initialization upon first use (go through an accessor
    > to get at a singleton, not use the global directly)
    > b) be darned careful to avoid circular dependencies
    >
    > If b is not an option, one should at least do the initialization
    > after main() has been entered. Anything else just isn't safe or
    > reliable, nor deterministic enough. Once you've done that, you have
    > a function that gets called, and in that function you can set up
    > and tear down an autorelease pool for that one call. And as with
    > other uses of ObjC, any objects you need to keep around yourself
    > outside this pool should be retained.
    >
    > It is not a good idea to try and create a "global autorelease pool"
    > outside main, or for that matter even inside main, because this is
    > essentially the same as a leak. Any objects added to this pool will
    > stay around and won't be released until your application quits. All
    > you do is shut up the console message, but you're not plugging the
    > actual leak.
    >
    > Also, watch out for exceptions when integrating C++ and Objective
    > C. Neither C++ nor ObjC like it when you throw and exception of
    > either type through code of the other type, just like you shouldn't
    > throw an exception through OS code. This may also be difficult, as
    > the standard CarbonEventHandlers installed on Cocoa windows in a
    > Carbon app don't install exception handlers before they call into
    > Cocoa, so you'll have to fix that yourself.
    >
    > Cheers,
    > -- M. Uli Kusterer
    > http://www.zathras.de
  • Am 10.10.2007 um 21:42 schrieb John Stiles:
    > C++ objects as part of ObjC objects works OK in 10.4 (they are
    > properly constructed/destructed), if you enable a compiler flag.
    > It's 10.3 where it won't work.

    John,

      yes, I just found the 10.4 release notes that state this:

    http://developer.apple.com/releasenotes/Cocoa/RN-ObjectiveC/index.html

    There's some order caveats, and only default constructors can be
    called, but at least that works now. Unless you need to be 10.3-
    compatible, of course :-)

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • Am 10.10.2007 um 21:42 schrieb John Stiles:
    >> a) use lazy initialization upon first use (go through an accessor
    >> to get at a singleton, not use the global directly)
    >> b) be darned careful to avoid circular dependencies
    >>
    >> If b is not an option, one should at least do the initialization
    >> after main() has been entered. Anything else just isn't safe or
    >> reliable, nor deterministic enough. Once you've done that, you
    >> have a function that gets called, and in that function you can set
    >> up and tear down an autorelease pool for that one call. And as
    >> with other uses of ObjC, any objects you need to keep around
    >> yourself outside this pool should be retained.

      Arrg... that should be: "If A is not an option"... what do we learn
    from that? Never swap two items in a list without checking whether
    that screws up references to them :-/

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    > Am 10.10.2007 um 16:28 schrieb Clark Cox:
    >> but never raw pointers. Raw pointers to C++ objects should not be used
    >> as Objective-C instance variables. Additionally, except for simple,
    >> transient Obj-C objects, the C++ objects should be "owned" by the
    >> Objective-C objects, not the other way around.
    >
    > Errr... I'm not sure whether that was a 10.3-only limitation, but I
    > distinctly remember that Objective C did not call C++ constructors
    > for its instance variables. The compiler option to activate this has
    > been around for a while, but AFAIK the C++ runtime currently shipping
    > with 10.4 doesn't support that option yet.

    10.4 most certainly supports this. (My last employer's flagship
    project was a large medical application written in Objective-C++ that
    ran on Tiger). Of course, we controlled the hardware and OS as well as
    the software itself, so we didn't have to worry about running on older
    OSes.

    > So, I don't think you can
    > use C++ smart pointers and the likes as instance variables. You /have
    > to/ use pointers and new and delete them properly.

    Smart pointers take care of this for you.

    The rest of your e-mail is spot on:
    Global autorelease pool or pool created before main() - bad idea
    Throwing exceptions across language boundaries - bad idea

    --
    Clark S. Cox III
    <clarkcox3...>
  • Am 11.10.2007 um 00:19 schrieb Clark Cox:
    > On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    >> So, I don't think you can
    >> use C++ smart pointers and the likes as instance variables. You /have
    >> to/ use pointers and new and delete them properly.
    >
    > Smart pointers take care of this for you.

      Only if their constructors are called. But as we found out, they
    are on 10.4, but if you deploy on 10.3 you'll want to use regular
    pointers and new and delete your C++ objects.

    > The rest of your e-mail is spot on:
    > Global autorelease pool or pool created before main() - bad idea
    > Throwing exceptions across language boundaries - bad idea

      You forgot: Implicitly or explicitly relying on initialization
    order of globals or class-static variables - bad idea

      This is really a matter too few C++ programmers are aware of: If
    you have two object-type globals, and one uses the other from its
    constructor, this is *a programming error*. You cannot know whether
    your target object has already been constructed at the time the
    constructor uses it.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    > Am 11.10.2007 um 00:19 schrieb Clark Cox:
    >> On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    >>> So, I don't think you can
    >>> use C++ smart pointers and the likes as instance variables. You /have
    >>> to/ use pointers and new and delete them properly.
    >>
    >> Smart pointers take care of this for you.
    >
    > Only if their constructors are called. But as we found out, they
    > are on 10.4, but if you deploy on 10.3 you'll want to use regular
    > pointers and new and delete your C++ objects.
    >
    >> The rest of your e-mail is spot on:
    >> Global autorelease pool or pool created before main() - bad idea
    >> Throwing exceptions across language boundaries - bad idea
    >
    > You forgot: Implicitly or explicitly relying on initialization
    > order of globals or class-static variables - bad idea
    >
    > This is really a matter too few C++ programmers are aware of: If
    > you have two object-type globals, and one uses the other from its
    > constructor, this is *a programming error*.

    While I am currently away from my mac (and therefore my copy of the
    standard) I do believe that the order of construction is well-defined
    if the two objects are defined in the *same translation unit*. Though
    I can't say that I've ever relied on that (nor do I plan to) :)

    But in the general case, I believe you are correct.

    > You cannot know whether
    > your target object has already been constructed at the time the
    > constructor uses it.
    >
    > Cheers,
    > -- M. Uli Kusterer
    > http://www.zathras.de
    >
    >
    >
    >

    --
    Clark S. Cox III
    <clarkcox3...>
  • On Oct 10, 2007, at 5:27 PM, Clark Cox wrote:

    > On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    >> Am 11.10.2007 um 00:19 schrieb Clark Cox:
    >>> On 10/10/07, Uli Kusterer <witness.of.teachtext...> wrote:
    >>>> So, I don't think you can
    >>>> use C++ smart pointers and the likes as instance variables. You /
    >>>> have
    >>>> to/ use pointers and new and delete them properly.
    >>>
    >>> Smart pointers take care of this for you.
    >>
    >> Only if their constructors are called. But as we found out, they
    >> are on 10.4, but if you deploy on 10.3 you'll want to use regular
    >> pointers and new and delete your C++ objects.
    >>
    >>> The rest of your e-mail is spot on:
    >>> Global autorelease pool or pool created before main() - bad idea
    >>> Throwing exceptions across language boundaries - bad idea
    >>
    >> You forgot: Implicitly or explicitly relying on initialization
    >> order of globals or class-static variables - bad idea
    >>
    >> This is really a matter too few C++ programmers are aware of: If
    >> you have two object-type globals, and one uses the other from its
    >> constructor, this is *a programming error*.
    >
    > While I am currently away from my mac (and therefore my copy of the
    > standard) I do believe that the order of construction is well-defined
    > if the two objects are defined in the *same translation unit*. Though
    > I can't say that I've ever relied on that (nor do I plan to) :)

    This is correct, and it is safe to rely upon. The standard dictates
    that global variables in a single translation unit are constructed
    "in order," from the top of the translation unit down. And of course
    the destructors are invoked in reverse order, from the bottom up.

    Between translation units, this way lies madness. Among the C++ savvy
    coders I know, this is well understood. It's better to have global
    objects which initialize themselves correctly on first use. We
    actually have some classes dedicated to cleaning up objects in the
    proper order; each global object specifies what things it relies on
    (if any), and this turns into a directed acyclic graph which
    determines shutdown order. It works well in practice but it's
    difficult to educate people on how to use it. (It is also good
    because it can immediately assert if there is a cyclic dependency.)

    > But in the general case, I believe you are correct.
    >
    >> You cannot know whether
    >> your target object has already been constructed at the time the
    >> constructor uses it.
    >>
    >> Cheers,
    >> -- M. Uli Kusterer
    >> http://www.zathras.de
    >>
    >>
    >>
    >>
    >
    >
    > --
    > Clark S. Cox III
    > <clarkcox3...>
  • On Oct 10, 2007, at 9:49 PM, John Stiles wrote:

    >
    > On Oct 10, 2007, at 5:27 PM, Clark Cox wrote:
    >
    >> While I am currently away from my mac (and therefore my copy of the
    >> standard) I do believe that the order of construction is well-defined
    >> if the two objects are defined in the *same translation unit*. Though
    >> I can't say that I've ever relied on that (nor do I plan to) :)
    >
    > This is correct, and it is safe to rely upon. The standard dictates
    > that global variables in a single translation unit are constructed
    > "in order," from the top of the translation unit down. And of course
    > the destructors are invoked in reverse order, from the bottom up.
    >
    > Between translation units, this way lies madness. Among the C++
    > savvy coders I know, this is well understood.

    I hope by well-understood, you mean that it is well-understood that
    the standard does not guarantee the order of initialization between
    translation units. The relevant portion of the standard seems to be
    3.6.2 [basic.start.init]. To quote from the standard (bah, my copy of
    the standard has copy protection, this is quite possibly the dumbest
    thing I've ever seen in a document),
    > The storage for objects with static storage duration shall be zero-
    > initialized before any other initialization takes places. Zero-
    > initialization and initialization with a constant expression are
    > collectively called static initialization; all other initialization
    > is dynamic initialization. Objects of POD types with static storage
    > duration initialized with constant expressions shall be initialized
    > before any dynamic initialization takes place. Objects with static
    > storage duration defined in namespace scope in the same translation
    > unit and dynamically initialized shall be initialized in the order
    > in which their definition appears in the translation unit.

    (Little did they seem to realize that I can type the text since I can
    read it...)

    --
    Steve Checkoway
previous month october 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 31        
Go to today