• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Understanding objc_assign_strongCast
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


References: 
 >re: Understanding objc_assign_strongCast (From: Ben Trumbull <email@hidden>)
 >Re: Understanding objc_assign_strongCast (From: David Elliott <email@hidden>)

  • Prev by Date: Re: Command-option-8
  • Next by Date: Re: CoreData Predicate Question - Retriving child objects
  • Previous by thread: Re: Understanding objc_assign_strongCast
  • Next by thread: Re: Understanding objc_assign_strongCast
  • Index(es):
    • Date
    • Thread