Finalizable class
Marker interface for objects which should not be finalized too soon.
Any local variable with a static type that includes Finalizable
is guaranteed to be alive until execution exits the code block where
the variable is in scope.
A type includes Finalizable
if either
- the type is a non-
Never
subtype ofFinalizable
, or - the type is
T?
orFutureOr<T>
whereT
includesFinalizable
.
In other words, while an object is referenced by such a variable, it is guaranteed to not be considered unreachable, and the variable itself is considered alive for the entire duration of its scope, even after it is last referenced.
Without this marker interface on the variable's type, a variable's
value might be garbage collected before the surrounding scope has
been completely executed, as long as the variable is definitely not
referenced again. That can, in turn, trigger a NativeFinalizer
to perform a callback. When the variable's type includes Finalizable,
The NativeFinalizer
callback is prevented from running until
the current code using that variable is complete.
For example, finalizable
is kept alive during the execution of
someNativeCall
:
void myFunction() {
final finalizable = MyFinalizable(Pointer.fromAddress(0));
someNativeCall(finalizable.nativeResource);
}
void someNativeCall(Pointer nativeResource) {
// ..
}
class MyFinalizable implements Finalizable {
final Pointer nativeResource;
MyFinalizable(this.nativeResource);
}
Methods on a class implementing Finalizable
keep the this
object alive
for the duration of the method execution. The this
value is treated
like a local variable.
For example, this
is kept alive during the execution of someNativeCall
in myFunction
:
class MyFinalizable implements Finalizable {
final Pointer nativeResource;
MyFinalizable(this.nativeResource);
void myFunction() {
someNativeCall(nativeResource);
}
}
void someNativeCall(Pointer nativeResource) {
// ..
}
It is good practise to implement logic involving finalizables as methods on the class that implements Finalizable.
If a closure is created inside the block scope declaring the variable, and that closure contains any reference to the variable, the variable stays alive as long as the closure object does, or as long as the body of such a closure is executing.
For example, finalizable
is kept alive by the closure object and until the
end of the closure body:
void doSomething() {
final resourceAction = myFunction();
resourceAction(); // `finalizable` is alive until this call returns.
}
void Function() myFunction() {
final finalizable = MyFinalizable(Pointer.fromAddress(0));
return () {
someNativeCall(finalizable.nativeResource);
};
}
void someNativeCall(Pointer nativeResource) {
// ..
}
class MyFinalizable implements Finalizable {
final Pointer nativeResource;
MyFinalizable(this.nativeResource);
}
Only captured variables are kept alive by closures, not all variables.
For example, finalizable
is not kept alive by the returned closure object:
void Function() myFunction() {
final finalizable = MyFinalizable(Pointer.fromAddress(0));
final nativeResource = finalizable.nativeResource;
return () {
someNativeCall(nativeResource);
};
}
void someNativeCall(Pointer nativeResource) {
// ..
}
class MyFinalizable implements Finalizable {
final Pointer nativeResource;
MyFinalizable(this.nativeResource);
}
It's likely an error if a resource extracted from a finalizable object escapes the scope of the finalizable variable it's taken from.
The behavior of Finalizable
variables applies to asynchronous
functions too. Such variables are kept alive as long as any
code may still execute inside the scope that declared the variable,
or in a closure capturing the variable,
even if there are asynchronous delays during that execution.
For example, finalizable
is kept alive during the await someAsyncCall()
:
Future<void> myFunction() async {
final finalizable = MyFinalizable();
await someAsyncCall();
}
Future<void> someAsyncCall() async {
// ..
}
class MyFinalizable implements Finalizable {
// ..
}
Also in asynchronous code it's likely an error if a resource extracted from
a finalizable object escapes the scope of the finalizable variable it's
taken from. If you have to extract a resource from a Finalizable
, you
should ensure the scope in which Finalizable is defined outlives the
resource by await
ing any asynchronous code that uses the resource.
For example, this
is kept alive until resource
is not used anymore in
useAsync1
, but not in useAsync2
and useAsync3
:
class MyFinalizable {
final Pointer<Int8> resource;
MyFinalizable(this.resource);
Future<int> useAsync1() async {
return await useResource(resource);
}
Future<int> useAsync2() async {
return useResource(resource);
}
Future<int> useAsync3() {
return useResource(resource);
}
}
/// Does not use [resource] after the returned future completes.
Future<int> useResource(Pointer<Int8> resource) async {
return resource.value;
}
It is possible for an asynchronous function to stall at an
await
, such that the runtime system can see that there is no possible
way for that await
to complete. In that case, no code after the
await
will ever execute, including finally
blocks, and the
variable may be considered dead along with everything else.
If you're not going to keep a variable alive yourself, make sure to pass the finalizable object to other functions instead of just its resource.
For example, finalizable
is not kept alive by myFunction
after it has
run to the end of its scope, while someAsyncCall
could still continue
execution. However, finalizable
is kept alive by someAsyncCall
itself:
void myFunction() {
final finalizable = MyFinalizable();
someAsyncCall(finalizable);
}
Future<void> someAsyncCall(MyFinalizable finalizable) async {
// ..
}
class MyFinalizable implements Finalizable {
// ..
}
- Annotations
-
- @Since('2.17')
Properties
- hashCode → int
-
The hash code for this object.
read-onlyinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
read-onlyinherited
Methods
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited