Skip to content

Finalizer<T> abstract final

abstract final class Finalizer<T>

Annotations: @Since.new("2.17")

A finalizer which can be attached to Dart objects.

A finalizer can create attachments between the finalizer and any number of Dart values, by calling attach with the value, along with a finalization token and an optional attach key, which are part of the attachment.

When a Dart value becomes inaccessible to the program, any finalizer that currently has an attachment to the value may have its callback function called with the attachment's finalization token.

Example:

dart
class Database {
  // Keeps the finalizer itself reachable, otherwise it might be disposed
  // before the finalizer callback gets a chance to run.
  static final Finalizer<DBConnection> _finalizer =
      Finalizer((connection) => connection.close());

  final DBConnection _connection;

  Database._fromConnection(this._connection);

  factory Database.connect() {
    // Wraps the connection in a nice user API,
    // *and* closes connection if the user forgets to.
    final connection = DBConnection.connect();
    final wrapper = Database._fromConnection(connection);
    // Calls finalizer callback when `wrapper` is no longer reachable.
    _finalizer.attach(wrapper, connection, detach: wrapper);
    return wrapper;
  }

  void close() {
    // User requested close.
    _connection.close();
    // Detach from finalizer, no longer needed.
    _finalizer.detach(this);
  }

  // Some useful methods.
}

This example has an example of an external resource that needs clean-up. The finalizer is used to clean up an external connection when the user of the API no longer has access to that connection. The example uses the same object as attached object and detach key, which is a useful approach when each attached object can be detached individually. Being a detachment key doesn't keep an object alive.

No promises are made that the callback will ever be called. The only thing that is guaranteed is that if a finalizer's callback is called with a specific finalization token as argument, then at least one value with an attachment to the finalizer that has that finalization token, is no longer accessible to the program.

If the finalizer itself becomes unreachable, it's allowed to be garbage collected and then it won't trigger any further callbacks. Always make sure to keep the finalizer itself reachable while it's needed.

If multiple finalizers are attached to a single object, or the same finalizer is attached multiple times to an object, and that object becomes inaccessible to the program, then any number (including zero) of those attachments may trigger their associated finalizer's callback. It will not necessarily be all or none of them.

Finalization callbacks will happen as events. They will not happen during execution of other code, and not as a microtask, but as high-level events similar to timer events.

Finalization callbacks must not throw.

When running on the Dart native runtime, and the callback is a native function rather than a Dart function, use dart:ffi's NativeFinalizer instead.

Constructors

Finalizer() factory

factory Finalizer(void Function(T) callback)

Creates a finalizer with the given finalization callback.

The callback is bound to the current zone when the Finalizer is created, and will run in that zone when called.

Implementation
dart
external factory Finalizer(void Function(T) callback);

Properties

hashCode no setter inherited

int get hashCode

The hash code for this object.

A hash code is a single integer which represents the state of the object that affects operator == comparisons.

All objects have hash codes. The default hash code implemented by Object represents only the identity of the object, the same way as the default operator == implementation only considers objects equal if they are identical (see identityHashCode).

If operator == is overridden to use the object state instead, the hash code must also be changed to represent that state, otherwise the object cannot be used in hash based data structures like the default Set and Map implementations.

Hash codes must be the same for objects that are equal to each other according to operator ==. The hash code of an object should only change if the object changes in a way that affects equality. There are no further requirements for the hash codes. They need not be consistent between executions of the same program and there are no distribution guarantees.

Objects that are not equal are allowed to have the same hash code. It is even technically allowed that all instances have the same hash code, but if clashes happen too often, it may reduce the efficiency of hash-based data structures like HashSet or HashMap.

If a subclass overrides hashCode, it should override the operator == operator as well to maintain consistency.

Inherited from Object.

Implementation
dart
external int get hashCode;

runtimeType no setter inherited

Type get runtimeType

A representation of the runtime type of the object.

Inherited from Object.

Implementation
dart
external Type get runtimeType;

Methods

attach()

void attach(Object value, T finalizationToken, {Object? detach})

Attaches this finalizer to value.

When value is no longer accessible to the program, while still having an attachment to this finalizer, the callback of this finalizer may be called with finalizationToken as argument. The callback may be called at most once per active attachment, ones which have not been detached by calling Finalizer.detach.

The detach value is only used by the finalizer to identify the current attachment. If a non-null detach value is provided, the same (identical) value can be passed to Finalizer.detach to remove the attachment. If no (non-null) value is provided, the attachment cannot be removed again.

The value and detach arguments do not count towards those objects being accessible to the program. Both must be objects supported as an Expando key. They may be the same object.

Example:

dart
class Database {
  // Keeps the finalizer itself reachable, otherwise it might be disposed
  // before the finalizer callback gets a chance to run.
  static final Finalizer<DBConnection> _finalizer =
      Finalizer((connection) => connection.close());

  factory Database.connect() {
    // Wraps the connection in a nice user API,
    // *and* closes connection if the user forgets to.
    final connection = DBConnection.connect();
    final wrapper = Database._fromConnection();
    // Calls finalizer callback when `wrapper` is no longer reachable.
    _finalizer.attach(wrapper, connection, detach: wrapper);
    return wrapper;
  }

  Database._fromConnection();

  // Some useful methods.
}

Multiple objects may be attached using the same finalization token, and the finalizer can be attached multiple times to the same object with different, or the same, finalization token.

Implementation
dart
void attach(Object value, T finalizationToken, {Object? detach});

detach()

void detach(Object detach)

Detaches this finalizer from values attached with detach.

Each attachment between this finalizer and a value, which was created by calling attach with the detach object as detach argument, is removed.

If the finalizer was attached multiple times to the same value with different detachment keys, only those attachments which used detach are removed.

After detaching, an attachment won't cause any callbacks to happen if the object become inaccessible.

Example:

dart
class Database {
  // Keeps the finalizer itself reachable, otherwise it might be disposed
  // before the finalizer callback gets a chance to run.
  static final Finalizer<DBConnection> _finalizer =
      Finalizer((connection) => connection.close());

  final DBConnection _connection;

  Database._fromConnection(this._connection);

  void close() {
    // User requested close.
    _connection.close();
    // Detach from finalizer, no longer needed.
    // Was attached using this object as `detach` token.
    _finalizer.detach(this);
  }

  // Some useful methods.
}
Implementation
dart
void detach(Object detach);

noSuchMethod() inherited

dynamic noSuchMethod(Invocation invocation)

Invoked when a nonexistent method or property is accessed.

A dynamic member invocation can attempt to call a member which doesn't exist on the receiving object. Example:

dart
dynamic object = 1;
object.add(42); // Statically allowed, run-time error

This invalid code will invoke the noSuchMethod method of the integer 1 with an Invocation representing the .add(42) call and arguments (which then throws).

Classes can override noSuchMethod to provide custom behavior for such invalid dynamic invocations.

A class with a non-default noSuchMethod invocation can also omit implementations for members of its interface. Example:

dart
class MockList<T> implements List<T> {
  noSuchMethod(Invocation invocation) {
    log(invocation);
    super.noSuchMethod(invocation); // Will throw.
  }
}
void main() {
  MockList().add(42);
}

This code has no compile-time warnings or errors even though the MockList class has no concrete implementation of any of the List interface methods. Calls to List methods are forwarded to noSuchMethod, so this code will log an invocation similar to Invocation.method(#add, [42]) and then throw.

If a value is returned from noSuchMethod, it becomes the result of the original invocation. If the value is not of a type that can be returned by the original invocation, a type error occurs at the invocation.

The default behavior is to throw a NoSuchMethodError.

Inherited from Object.

Implementation
dart
@pragma("vm:entry-point")
@pragma("wasm:entry-point")
external dynamic noSuchMethod(Invocation invocation);

toString() inherited

String toString()

A string representation of this object.

Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string representation.

Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.

Inherited from Object.

Implementation
dart
external String toString();

Operators

operator ==() inherited

bool operator ==(Object other)

The equality operator.

The default behavior for all Objects is to return true if and only if this object and other are the same object.

Override this method to specify a different equality relation on a class. The overriding method must still be an equivalence relation. That is, it must be:

  • Total: It must return a boolean for all arguments. It should never throw.

  • Reflexive: For all objects o, o == o must be true.

  • Symmetric: For all objects o1 and o2, o1 == o2 and o2 == o1 must either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

If a subclass overrides the equality operator, it should override the hashCode method as well to maintain consistency.

Inherited from Object.

Implementation
dart
external bool operator ==(Object other);