Re: Understanding objc_assign_strongCast
Re: Understanding objc_assign_strongCast
- Subject: Re: Understanding objc_assign_strongCast
- From: Ben Trumbull <email@hidden>
- Date: Fri, 8 Feb 2008 01:01:24 -0800
On Feb 6, 2008, at 11:50 PM, David Elliott wrote:
Thank you for your response. It is most helpful to know that
CFRetain/CFRelease are the most appropriate option for maintaining
strong references from non-GC objects and that
objc_assign_strongCast has zero effect unless the pointer itself is
located in a GC-managed heap. I've observed first-hand that it has
zero effect in global memory and zero effect in non-GC heaps and
from my understanding ought not to be needed on the stack. Did I
miss any other notable types of memory?
There is a separate objc_assign_global(). You *can* put GC objects
into globals (assuming they are id or __strong types).
objc_assign_ivar() is for assigning to an ivar of an object (something
with an Objective-C Class). There are some additional details in the
GC programming guide, as well as the Leopard (10.5) release notes for
Objective-C and Garbage Collection (some GC stuff is nestled away
under the ObjC runtime)
But that's mostly for entertainment purposes. CFRetain and CFRelease
work well to manage "external references" (stuff holding a GC object
from non-GC memory) I've never needed to use any of the objc_assign*
functions directly for any strong references.
The only remaining question I have pertains to compatibility with
prior OS X versions. Clearly CFRetain/CFRelease work on Leopard.
And as far as I know code with GC-write barriers can only work on
Leopard, even when it is compiled to support both RR and GC, due to
the simple fact that the objc_assign_* series of functions do not
exist on older versions of OS X.
GC only works on Leopard, but several of the objc_assign_ functions
are available on Tiger (as their RR no-ops). I *believe* you can
compile mixed mode code for Leopard and Tiger so long as you do not
use any __weak references, but I'm not certain. I'd recommend trying
a small sample project on Leopard and testing the binary on Tiger.
Keep in mind I am writing a library (wxCocoa) that must work in both
RR and GC mode. Therefore my usage of CFRetain/CFRelease will be
balanced appropriately so as to function at runtime correctly
regardless of mode.
That is possible, although I haven't tried adding in the twist of
running on Tiger and Leopard. Nearly all the system frameworks on
Leopard run in both RR and GC mode.
However, if compiling for pure-RR mode is it still reasonable to use
CFRetain/CFRelease in lieu of retain/release or will this cause me
problems when targeting older versions of OS X? Do I need to
condition CFRetain/CFRelease on garbage collection being potentially
supported or is it safe to just judiciously replace retain/release
with CFRetain/CFRelease and expect the same code to run on prior
versions of OS X?
There are a few factors at work here, so typically one does not use
CFRetain/CFRelease in lieu of retain/release wholesale.
(1) CFRetain and CFRelease crash on NULL so you cannot dispatch to
them blindly, as you can with -retain and -release. You have to check
for nil.
(2) You don't have to replace all your use of -retain/release with
CFRetain/CFRelease. You only need it when you want to assign into non-
GC memory or otherwise hold an external reference upon a block of GC
memory.
(3) You must balance a call to -retain/+alloc with -release/-
autorelease. You must balance a call to CFRetain/CFCreate... with a
call to CFRelease. You cannot balance -retain with CFRelease. You
cannot balance a call to CFCreate... with -release (this does work
under RR, but breaks dual mode or GC code)
(4) -retain, -release, -autorelease are no-ops under GC.
Rules 3 &4 combine into an interesting pattern:
id foo = [[NSObject alloc] init]; // RR count 1, GC external
reference count 0 because +alloc does not force an er under GC
if (foo) CFRetain((void*)foo); // RR count 2, GC er count 1
[foo release]; // RR count 1, GC er count 1 because -release is a no-
op under GC
At this point 'foo' has the same hard reference count under both RR
and GC. It can be freed equally well under either mode with
if (foo) CFRelease((void*)foo): // RR count 0; GC er count 0
// some point later either -dealloc or -finalize, if any, will get
called
You don't need this if 'foo' can be constructed by a CFCreate...
function since CF functions create objects that have an er count of 1
as they must be balanced by CFRelease.
This pattern is useful to deal with a problem encountered by dual
memory mode code. -finalize is called in a completely indeterminate,
random order. You absolutely cannot message another object in -
finalize unless you can guarantee it is alive. That is really hard to
know unless you put a CFRetain on it yourself, and CFRelease it in -
finalize. This includes your own ivars! Your ivars can be finalized
before you!
Now even if you do have a finalize method, most of your ivars won't
need to be messaged. Most messages to ivars in -dealloc are -release,
which you don't need at all in -finalize method. So this is a special
case. In hundreds of classes, with plenty of ivars apiece, I've only
used it 10 or 11 times.
The problem is an issue with dual mode code, because it's much easier
for GC only code to be structured with fewer (ideally zero) finalize
methods. finalize methods can be quite expensive so avoiding them is
best.
Writing a dual mode framework is not trivial.
You could override the new operator to allocate scanned collectible
memory. I don't have any experience writing Objective-C++ code under
GC, so I'm not sure it's any easier than the CFRetain approach.
- Ben
On Feb 6, 2008, at 8:23 PM, Ben Trumbull wrote:
Dave,
objc_assign_strongCast() will issue a write barrier, informing GC
that the destination value has changed. But if the only references
to this pointer are in unscanned (not GC) memory, than the GC
system will think it's dead as no references to that pointer exist
in scanned (GC live) memory.
The C++ new operator allocates from malloc(), just as before.
malloc() memory is not GC scanned. It's probably easiest to
instead use CFRetain and balance it with CFRelease in delete/etc.
In fact, I've written some pretty hairy hybrid memory model code,
and have yet to find a use for calling objc_assign_strongCast()
myself.
The GC programming guide talks about using CFRetain/CFRelease in
the section on Core Foundation objects. The primary difference
with C++ objects is that C++'s new is from unscanned malloc memory
where as CF types (with the default allocator) are allocated from
scanned memory.
--
-Ben
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden