首页 > 解决方案 > 使用 destroy() 和异步函数避免分配错误

问题描述

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

  1. 启用扩展。扩展是一个扩展类 Clutter.Actor
  2. 它创建一个名为的actormyActor并添加它: this.add_child(myActor).
  3. 然后它调用一个异步耗时的函数this._tcFunction(),该函数最终对myActor.

这是我遇到问题的地方:

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

  2. 禁用时,this.destroy()运行 GObjectthis.run_dispose()来收集垃圾。但是,如果this._tcFunction()还没有完成运行,它稍后会尝试访问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中的异步函数......

标签: javascriptgnome-shellgnome-shell-extensionsgjs

解决方案


首先,需要注意两点:

  1. 避免调用低级内存管理函数,例如GObject.run_dispose()在 C 库中存在这样的情况:这些对象被缓存以供重用,而实际上并没有在您认为它们被释放时被释放。也没有 dispose 信号,其他对象可能需要通知。

  2. 避免覆盖触发处置之类的函数,Clutter.Actor.destroy()而是使用销毁信号。GObject 信号回调总是将发射对象作为第一个参数,并且在销毁回调中使用它是安全的。

有几种方法我可以考虑解决这个问题,但这取决于具体情况。如果异步函数是 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 的可取消对象,或者仅使用常规函数/回调模式:

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);

推荐阅读