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



