Question

As we know, programming objective-c in X-code used to mean handling manual reference counting of objects. Following this, garbage collection was introduced and this has now been superseded by automatic reference counting (ARC).

In the past, I've had an application developed with GC which could load libraries using the function call dlopen. This required that the loading library matched that of the host application and also used GC. If the library was built with ARC, dlopen would fail. Naturally, this incompatibility is to be expected.

The System Preferences application is a good example here. It uses garbage collection: -

otool -oV /Applications/System\ Preferences.app/Contents/MacOS/System\ Preferences | tail -3
Contents of (__DATA,__objc_imageinfo) section
version 0
flags 0x2 OBJC_IMAGE_SUPPORTS_GC

Up until 10.7 so did the preference panes which it loads. However, in 10.8 the System Preferences application still uses GC, but some of the panes which it loads do not: -

otool -oV /System/Library/PreferencePanes/Network.prefPane/Contents/MacOS/Network | tail -3
Contents of (__DATA,__objc_imageinfo) section
  version 0
    flags 0x0

So, what's going on here? How does the garbage collection mechanism of the System Preferences application handle the ARC-compiled panes? Do they just leak the memory, or is there some sort of conversion going on underneath?

Was it helpful?

Solution

ARC does not change the rule you're familiar with, garbage collection must still be enabled or disabled for the entire process.

To understand how System Preferences works, take a closer look at the flags from __objc_imageinfo. Here are the Clang definitions for those flags:

enum ImageInfoFlags {
  eImageInfo_FixAndContinue      = (1 << 0), // This flag is no longer set by clang.
  eImageInfo_GarbageCollected    = (1 << 1),
  eImageInfo_GCOnly              = (1 << 2),
  eImageInfo_OptimizedByDyld     = (1 << 3), // This flag is set by the dyld shared cache.

  // A flag indicating that the module has no instances of a @synthesize of a
  // superclass variable. <rdar://problem/6803242>
  eImageInfo_CorrectedSynthesize = (1 << 4), // This flag is no longer set by clang.
  eImageInfo_ImageIsSimulated    = (1 << 5)
};

The 10.8+ version of System Preferences has the garbage collected flag set but not the GC-only flag. This means that it was built with -fobjc-gc and includes code to support both garbage collection and retain/release. As a result it can be run with GC either enabled or disabled via the OBJC_DISABLE_GC environment variable. Looking at the Info.plist for System Preferences this is exactly what it does:

<key>LSEnvironment</key>
<dict>
    <key>OBJC_DISABLE_GC</key>
    <string>YES</string>
</dict>

With this combination System Preferences launches with garbage collection disabled, and can load preference panes without GC support and those built with both GC and retain/release support via -fobjc-gc. If the user tries to open a preference pane that requires garbage collection (built with -fobjc-gc-only) System Preferences informs the user that it needs to restart to load that pane. If permission is granted the app relaunches itself with OBJC_DISABLE_GC=NO. At this point it can load preference panes that require GC support and those built with both GC and retain/release support. If the user subsequently tries to load a preference pane without GC support the app will need to relaunch again.

So there's no GC and non-GC magic here, just a backwards-compatibility mechanism made possible by the System Preferences UI not requiring that all preference panes be loaded at the same time.

OTHER TIPS

ARC is actually backward compatible with libraries and frameworks compiled under non-ARC, so mixing ARC code with non-ARC code is not that unusual. The way it works is by using a flag at compile time to switch ARC on/off:

-fno-objc-arc
-fobjc-arc

For example, you can set the target to build default as ARC, and for the code that isn't ARC you'll be able to switch to a non-ARC compiler during that particular build phase.

Obviously there's much more to it than just what I've mentioned, but at least you know it's very possible to mix non-ARC with ARC.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top