使用 destroy() 和异步函数避免分配错误

Avoid allocation errors with destroy() and async functions

以下是一些 GNOME 扩展的简单场景:

  1. 启用扩展。扩展名是 class 扩展 Clutter.Actor.
  2. 它创建了一个名为 myActor 的 actor 并添加了它: this.add_child(myActor).
  3. 然后它调用一个异步 耗时的函数 this._tcFunction() 最终做了一些事情 myActor.

这是我 运行 遇到的问题:

  1. 我们在启用后立即禁用(运行 this.destroy())扩展。

  2. 禁用时,this.destroy() 运行s GObject 的 this.run_dispose() 到 收集垃圾。但是,如果this._tcFunction()还没有完成运行ning, 它稍后会尝试访问 myActor 这可能已经 由 this.run_dispose().

  3. 释放

解决此问题的一种方法是在 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...

中的异步函数

首先要注意两点:

  1. 避免调用像 GObject.run_dispose() 这样的低级内存管理函数,因为在 C 库中有一些情况,这些对象被缓存以供重用,而不是 实际上 当你认为它们被处置时。也没有处理信号,其他对象可能需要通知。

  2. 避免重写像 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);