使用 destroy() 和异步函数避免分配错误
Avoid allocation errors with destroy() and async functions
以下是一些 GNOME 扩展的简单场景:
- 启用扩展。扩展名是 class 扩展
Clutter.Actor
.
- 它创建了一个名为
myActor
的 actor 并添加了它:
this.add_child(myActor)
.
- 然后它调用一个异步
耗时的函数
this._tcFunction()
最终做了一些事情
myActor
.
这是我 运行 遇到的问题:
我们在启用后立即禁用(运行 this.destroy()
)扩展。
禁用时,this.destroy()
运行s GObject 的 this.run_dispose()
到
收集垃圾。但是,如果this._tcFunction()
还没有完成运行ning,
它稍后会尝试访问 myActor
这可能已经
由 this.run_dispose()
.
释放
解决此问题的一种方法是在 this.destroy()
中定义一个布尔变量
destroy() {
this._destroying = true
// ...
this.run_dispose;
}
然后在 this._tcFunction()
中添加一个检查,例如
async _tcFunction() {
await this._timeConsumingStuff();
if (this._destroying === true) { return; }
myActor.show();
}
我的问题:有没有更好的方法来处理这些情况?也许 Gio.Cancellable()
? AFAIK,没有简单的方法来停止 javascript...
中的异步函数
首先要注意两点:
避免调用像 GObject.run_dispose()
这样的低级内存管理函数,因为在 C 库中有一些情况,这些对象被缓存以供重用,而不是 实际上 当你认为它们被处置时。也没有处理信号,其他对象可能需要通知。
避免重写像 Clutter.Actor.destroy()
这样触发处置的函数,而是使用 destroy 信号。 GObject 信号回调总是将发射对象作为第一个参数,在销毁回调中使用 that 是安全的。
我有几种方法可以解决这个问题,但这要视情况而定。如果异步函数是 GNOME 库异步函数,它可能确实有一个可取消的参数:
let cancellable = new Gio.Cancellable();
let actor = new Clutter.Actor();
actor.connect('destroy', () => cancellable.cancel());
Gio.File.new_for_path('foo.txt').load_contents_async(cancellable, (file, res) => {
try {
let result = file.load_contents_finish(res);
// This shouldn't be necessary if the operation succeeds (I think)
if (!cancellable.is_cancelled())
log(actor.width);
} catch (e) {
// We know it's not safe
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('it was cancelled');
// Probably safe, but let's check
else if (!cancellable.is_cancelled())
log(actor.width);
}
});
// The above function will begin but not finish before the
// cancellable is triggered
actor.destroy();
当然,您始终可以使用带有 Promise 的可取消对象,或者只是常规的 function/callback 模式:
new Promise((resolve, reject) => {
// Some operation
resolve();
}).then(result => {
// Check the cancellable
if (!cancellable.is_cancelled())
log(actor.width);
});
另一个选择是 null
出你的参考,因为你可以安全地检查:
let actor = new Clutter.Actor();
actor.connect('destroy', () => {
actor = null;
});
if (actor !== null)
log(actor.width);
以下是一些 GNOME 扩展的简单场景:
- 启用扩展。扩展名是 class 扩展
Clutter.Actor
. - 它创建了一个名为
myActor
的 actor 并添加了它:this.add_child(myActor)
. - 然后它调用一个异步
耗时的函数
this._tcFunction()
最终做了一些事情myActor
.
这是我 运行 遇到的问题:
我们在启用后立即禁用(运行
this.destroy()
)扩展。禁用时,
this.destroy()
运行s GObject 的this.run_dispose()
到 收集垃圾。但是,如果this._tcFunction()
还没有完成运行ning, 它稍后会尝试访问myActor
这可能已经 由this.run_dispose()
. 释放
解决此问题的一种方法是在 this.destroy()
destroy() {
this._destroying = true
// ...
this.run_dispose;
}
然后在 this._tcFunction()
中添加一个检查,例如
async _tcFunction() {
await this._timeConsumingStuff();
if (this._destroying === true) { return; }
myActor.show();
}
我的问题:有没有更好的方法来处理这些情况?也许 Gio.Cancellable()
? AFAIK,没有简单的方法来停止 javascript...
首先要注意两点:
避免调用像
GObject.run_dispose()
这样的低级内存管理函数,因为在 C 库中有一些情况,这些对象被缓存以供重用,而不是 实际上 当你认为它们被处置时。也没有处理信号,其他对象可能需要通知。避免重写像
Clutter.Actor.destroy()
这样触发处置的函数,而是使用 destroy 信号。 GObject 信号回调总是将发射对象作为第一个参数,在销毁回调中使用 that 是安全的。
我有几种方法可以解决这个问题,但这要视情况而定。如果异步函数是 GNOME 库异步函数,它可能确实有一个可取消的参数:
let cancellable = new Gio.Cancellable();
let actor = new Clutter.Actor();
actor.connect('destroy', () => cancellable.cancel());
Gio.File.new_for_path('foo.txt').load_contents_async(cancellable, (file, res) => {
try {
let result = file.load_contents_finish(res);
// This shouldn't be necessary if the operation succeeds (I think)
if (!cancellable.is_cancelled())
log(actor.width);
} catch (e) {
// We know it's not safe
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('it was cancelled');
// Probably safe, but let's check
else if (!cancellable.is_cancelled())
log(actor.width);
}
});
// The above function will begin but not finish before the
// cancellable is triggered
actor.destroy();
当然,您始终可以使用带有 Promise 的可取消对象,或者只是常规的 function/callback 模式:
new Promise((resolve, reject) => {
// Some operation
resolve();
}).then(result => {
// Check the cancellable
if (!cancellable.is_cancelled())
log(actor.width);
});
另一个选择是 null
出你的参考,因为你可以安全地检查:
let actor = new Clutter.Actor();
actor.connect('destroy', () => {
actor = null;
});
if (actor !== null)
log(actor.width);