Using, IDisposable & Finalizers
The Disposable Pattern, centered around the IDisposable
interface, can be used to add deterministic disposal of resources that are not managed by the platform's memory allocation system (be it the garbage collector or reference counting). This typically includes non-memory resources such as file or network handles, database connections, or operating-system-level objects such as locks, window handles, or the like. On the Island-backed platforms and on Cocoa it could also include manually allocated memory, such as from calls to malloc()
.
The IDisposable
interface is available on an platforms, and defines a single required method, Dispose
. The implied contract is that when allocating any class that implements IDisposable
, the Dispose
method must be called (explicitly or implicitly, more on that later) when the class is done being used.
After the call to Dispose
, the instance should be considered disposed, and no further calls to its members should be performed (unless otherwise documented as safe).
The implementation of Dispose
will take care of releasing any resources held by the class that might need explicit disposal.
The Using
Pattern & IDisposable
A common pattern for disposing short-lived (i.e. within the scope of a single method) objects is to the using
(or __using
, in Swift and try
in Java) statement.
A using
statement combines the declaration of a local variable that's limited in scope to the statement(s) contained within the using
with an automatic call to Dispose
at the end.
It serves two purposes: for one, because the local variable is limited in scope, accidental calls to it after disposal are prevented. For another, using
automatically checks if the instance in question actually implements IDisposable
, at runtime, and if so calls it in a way that is protected from exceptions:
using s := new FileSteam(...) do begin
// work with the stream
end;
using (s = new FileSteam(...))
{
// work with the stream
}
__using s = FileSteam(...) {
// work with the stream
}
try (var s = new FileSteam(...)) {
// work with the stream
}
The using
statement roughly translates to the following manual code:
var s := new FileSteam(...);
try
// work with the stream
finally
(s as IDisposable):Dispose;
end;
var s = new FileSteam(...);
try
{
// work with the stream
}
finally
{
IDisposable(s)?.Dispose();
}
let s = FileSteam(...)
defer {
(s as? IDisposable)?.Dispose()
}
// work with the stream
var s = new FileSteam(...);
try
{
// work with the stream
}
finally
{
if (s is IDisposable)
IDisposable(s).Dispose();
}
s := FileSteam { ... }
defer func () {
disposable, isDisposable := i.(sDisposable)
if (isDisposable) {
disposable.Dispose();
}
}()
// work with the stream
Finalizers
Finalizers are a secondary fall-back mechanism to free resources held by an object, if Dispose
was not called properly (or if the class in question does not properly implement IDisposable
). Finalizers are called when the last reference to an object is released (under ARC), or when the object is claimed by the garbage collector (under GC).
Note that under GC, the presence of finalizers adds extra cost to the deallocation and may cause instances to stay in memory longer and until a later GC cleanup than they would normally be cleaned away under. So it is always preferable to properly dispose of instances using the IDisposable pattern.
On the Cocoa platforms, including Cocoa object model classes on Island, Finalizers map to the dealloc
method (or deinit
, in Swift terms).
Suppressing Finalizers from Dispose
On .NET and Island, it is possible for the Dispose
method to mark the current instance as disposed and forgo the overhead associated with a redundant call to the finalizer. This is achieved by calling the GC.SuppressFinalize
(.NET) or Utilities.SuppressFinalize
(Island) method.
Platfrom-specific Mappings
- On .NET,
IDisposable
is defined by the base .NET Framework library. - On the Island-backed platforms,
IDisposable
is defined in Island RTL. - On legacy Cocoa.
IDisposable
is defined in the Toffee Base Library. - On Java,
IDisposable
is defined in the Cooper Base Library as a reverse-mapped interface to the standardAutoCloseable
interface.
See Also
- Finalizers in Oxygene
IDisposable
(.NET)IDisposable
(Island)IDisposable
(Cocoa)IDisposable
(Java)using
Statements in Oxygene__using
Statements in SwiftObject.Finalize
Method (.NET)- Understanding when to use a Finalizer in your .NET class (.NET)
AutoCloable
and how to use it (Java)