不应过早最终化的对象的标记接口。
任何静态类型中包含 Finalizable
的局部变量都保证在执行退出变量作用域的代码块之前始终有效。
一个类型包含 Finalizable
的条件是:
- 该类型是
Finalizable
的非Never
子类型,或者 - 该类型是
T?
或FutureOr<T>
,其中T
包含Finalizable
。
换句话说,在变量引用对象的情况下,可以保证不会认为它是不可达的,即使在最后引用之后,变量自身在其作用域的整个持续期间都认为是活的。
如果没有这个标记接口在变量的类型上,只要变量肯定不再被引用,它的值可能会在完全执行周围作用域之前被垃圾回收。这反过来,可能会触发 NativeFinalizer
来执行回调。当变量的类型包含 Finalizable,则 NativeFinalizer
回调直到使用该变量的当前代码完成才会运行。
例如,在执行 someNativeCall
时保持 finalizable
生存。
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);
}
实现了 Finalizable
的类的类方法在方法执行期间保持 this
对象存活。this
的值被当作局部变量处理。
例如,在 myFunction
中执行 someNativeCall
时保持 this
生存。
class MyFinalizable implements Finalizable {
final Pointer nativeResource;
MyFinalizable(this.nativeResource);
void myFunction() {
someNativeCall(nativeResource);
}
}
void someNativeCall(Pointer nativeResource) {
// ..
}
将涉及最终化的逻辑作为实现 Finalizable 的类上的方法来实践是一个好习惯。
如果在一个声明变量的块作用域内部创建了闭包,并且该闭包包含对该变量的任何引用,则变量会一直存活,直到闭包对象存活,或者直到该闭包体的执行完成。
例如,通过闭包对象和闭包体的末尾保持 finalizable
生存。
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);
}
仅捕获变量通过闭包存活,而不是所有变量。
例如,没有通过返回的闭包对象保持 finalizable
存活。
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);
}
如果从最终化对象中提取的资源逃脱了它所取的最终化变量的作用域,那么可能是一个错误。
Finalizable
变量的行为也适用于异步函数。这些变量在其声明的范围或捕获该变量的闭包中仍然存活,即使在该执行期间有异步延迟。
例如,在执行 await someAsyncCall()
时保持 finalizable
生存。
Future<void> myFunction() async {
final finalizable = MyFinalizable();
await someAsyncCall();
}
Future<void> someAsyncCall() async {
// ..
}
class MyFinalizable implements Finalizable {
// ..
}
在异步代码中,如果从最终化对象中提取的资源逃脱了它所取的最终化变量的作用域,那么可能是一个错误。如果您必须从 Finalizable
中提取资源,您应该通过 await
任何使用该资源的异步代码来确保在最终化定义的范围中超越资源。
例如,在 useAsync1
中,直到不再使用 resource
,才保持 this
生存,但在 useAsync2
和 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;
}
异步函数可能在一个 await
点上 挂起,这样运行时系统可以看到该 await
无法完成。在这种情况下,包括 finally
块在内,在 await
之后将不会执行任何代码,变量可能被认为与其他一切一样是死的。
如果您不打算自己保持变量的生存,请确保通过其他函数传递最终化对象,而不是仅仅传递其资源。
例如,finalizable
在myFunction
运行到其作用域的末尾后不会保持活跃状态,而someAsyncCall
仍然可以继续执行。然而,finalizable
本身是由someAsyncCall
保持活跃的。
void myFunction() {
final finalizable = MyFinalizable();
someAsyncCall(finalizable);
}
Future<void> someAsyncCall(MyFinalizable finalizable) async {
// ..
}
class MyFinalizable implements Finalizable {
// ..
}
- 注解
-
- @Since('2.17')
属性
- hashCode → int
- 此对象的哈希码。no setterinherited
- runtimeType → Type
- 对象的运行时类型表示。no setterinherited
方法
-
noSuchMethod(
Invocation invocation) → dynamic - 当访问不存在的方法或属性时调用。inherited
-
toString(
) → String - 此对象的字符串表示形式。inherited
运算符
-
operator ==(
Object other) → bool - 等号运算符。inherited