The following sections discuss design patterns that are applicable in Cocoa applications that use reference counting but which do not translate well to a garbage collected environment.
Reference counting
dealloc
Enumerations
Resource wrapper objects
Leaked but not lost objects
Delegate references
Memory zones
If you use garbage collection, the methods that are used to implement the manual reference counting system (retain
, release
, dealloc
, autorelease
, and retainCount
) have no effect—the Objective-C messenger short circuits their dispatch. As a result, overriding release
and dealloc
is not supported when garbage collection is enabled—this makes obsolete some object caching patterns.
Note, however, that CFRetain
and CFRelease
do still have an effect in Core Foundation objects. See also “Adopting Garbage Collection.”
When you use “classic” memory management, you typically implement a dealloc
method that performs “clean-up” operations such as releasing instance variables, unregistering the object from a notification center, and closing resources. In a garbage-collected application, the analog of the dealloc
method is finalize
.
In a garbage-collected application, there is obviously no need to release instance variables, however you should ideally ensure that other resources are closed prior to an object’s destruction. For more details, see “Implementing a finalize Method.”
Although there are conceptual similarities between dealloc
and finalize
, there are some constraints on the implementation of finalize
that do not apply to dealloc
. In particular, you must ensure that there are no ordering issues.
Occasionally, within a completely captive subgraph, significant work is done in dealloc
methods as they do recursive releases and subsequent deallocations. Many applications that use reference counting make use of the deterministic ordering of object deallocation. If one object A retains another object B, A can guarantee that during its dealloc
method the B is valid (object B’s dealloc
method has not been called) and so send B messages and otherwise interact with it.
If you use garbage collection, it is possible for A and B to become invalid at the same time. Moreover, there is no ordering of the invocation of objects’ finalize
methods. If object A has a strong reference to object B, and object A and object B are both reclaimed during a given collection cycle, then there is no guarantee that object A’s finalize
method will be invoked first. Object A cannot therefore make any assumptions about the state of object B in its finalize
method. Or, conversely, object B must be prepared to be messaged after its finalize
method is invoked.
Since finalize
messages may be sent in any order, existing code that relies on side effects during dealloc
methods will need to introduce new methods to achieve a similar graph walking effect.
If you use weak collections, the count of the collection may change during an iteration loop. This will obviously lead to problems if you iterate over the contents of the collection directly using a for
loop. On the other hand, enumeration objects can cause resurrection of the collection or its objects if all are found to be garbage at the same time—this is particularly likely to occur if you use a pattern where you have a collection of helper objects and on finalization they perform cleanup work (see “Avoiding Resurrection”).
To avoid these problems, you should use the NSFastEnumeration
protocol (see Fast Enumeration) to iterate over the contents of a collection.
A common pattern is to associate an object with an external resource—for example, a file descriptor—that needs "management" or other state that the object coordinates, often across several threads. The typical implementation is to use a non-retaining CFDictionary coupled with a global lock at the lookup and deallocation stages. This pattern does not work when you use garbage collection because there is a timing window during finalization where the object is no longer reachable from a root, yet is still in the dictionary and can be resurrected.
The solution is to use an NSMapTable
object. A map table can hold keys, values, or both weakly, and when the objects are discovered unreachable the table is immediately cleared of such entries before any finalization is performed. This prevents resurrection of the object being finalized. For resources created and destroyed within the application, such as file descriptors, this is an adequate solution.
Cocoa used to have several classes of object (fonts and images) where a global table of strong keys held weak value references to the objects. The object would remove itself from the global table on dealloc
. But it would also be the case that there would be some universally known objects that never went away, and the pattern was to allocate these at startup using [[alloc ] init]
and simply place them in the weak table. The reference count for these objects would never decrease and so they would live indefinitely. Under garbage collection, in the absence of a strong reference these universal objects are collected. The solution is to use [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]
on these objects before placing them in the weak table.
If you do not use garbage collection, references to delegates are typically “weak” (in that the delegate is not retained). This is to avoid retain cycle problems. With garbage collection, retain cycles do not pose a problem, so there is no need to declare references to delegates as __weak
.
You cannot allocate objects in separate zones—all Cocoa objects must be allocated in a single managed heap. If your application is running in garbage collection mode, the zone parameter in NSAllocateObject
is ignored. With garbage collection enabled, [NSObject allocWithZone:zone]
calls NSAllocateObject(cls, extra, zone)
, which in turn calls objc_allocate_object(cls, extra)
.
You can allocate memory such that it is scanned using NSAllocateCollectable
or NSReallocateCollectable
.
© 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-11-19)