qml js库vs成员函数:无法调用null的方法'destroy'

qml js library vs member function: Cannot call method 'destroy' of null

纯JavaScript库中的函数有什么区别(标有“.pragma library”) 和我的代码上下文中 QML 中的成员函数?

import QtQuick 2.15
import QtQuick.Controls 2.15
import "."

ApplicationWindow {
    id: applicationWindow

    width: 400
    height: 400
    visible: true

    function showDialog(params) {
        const comp = Qt.createComponent("qrc:/MyPopup.qml");
        const err = comp.errorString();
        if (err)
            console.log("MyPopup component loading failed:", err);

        const dlg = comp.createObject(params.parent, params);
        console.assert(dlg !== null, "MyPopup object creation failed");
        console.log("opening");
        dlg.open();
    }

    Button {
        text: "Press me"
        anchors.centerIn: parent
        onClicked: {
            console.log("you press button");

            const params = {parent: applicationWindow.contentItem};
            //applicationWindow.showDialog(params); // !!! 1
            CreateDialog.showDialog2(params); // !!! 2
        }
    }
}

和我的弹出窗口 window:

import QtQuick 2.0
import QtQuick.Controls 2.15

Popup {
    id: view

    width: 300
    height: 300
    onClosed: {
        console.log("onClosed was called", view);
        view.destroy();
    }

    Text {
        anchors.centerIn: parent
        text: "Popup!!!"
    }
}

如果我使用变体 (1),如果我打开 Popup 然后按“Alt+F4”(在 Windows 上)或在 macOS 上按“Command+Q”,一切正常。

但是如果我将 'showDialog' 移动到 'CreateDialog.js' 文件中并在其顶部添加“.pragma library”, 然后在打开弹出窗口的应用程序退出期间我得到:

qrc:/MyPopup.qml:11: TypeError: Cannot call method 'destroy' of null

so Qt/QML 死对象的运行时调用信号。 发生了什么,是指在“纯javascript”情况下某处“挂起”的对话框, 所以 Qt 不能正确销毁它?

By default, JavaScript files imported from QML share their context with the QML component. If you remove the .pragma library from your JS file, the code behaves the same as if the JS code is run in your main QML file because they share the same context. So, why does .pragma library affect this? .pragma library creates a singleton-like JS file, and because any QML file can access it, its context is generically set to the root context 或某些顶级孤立上下文(不确定,无法从文档中确定),而不是调用它的文件的上下文。

来自上下文文档:

While QML objects instantiated in a context are not strictly owned by that context, their bindings are. If a context is destroyed, the property bindings of outstanding QML objects will stop evaluating.

我相信您看到的与第二句话相反,其中根上下文拥有弹出窗口的绑定,并在其销毁后调用其 onClosed 信号。

所以,您描述的两种情况:

第一个场景:

  1. 已创建弹出窗口,已分配 applicationWindow 父级,其创建上下文匹配 applicationWindow
  2. Window 关闭,applicationWindow 销毁开始
  3. 因为弹出窗口是 applicationWindow 的子窗口,所以它被销毁了
  4. applicationWindow 上下文被破坏,绑定停止计算
  5. 弹出关闭信号没有被调用,因为它的创建上下文被破坏了,没有抛出错误

第二种情况:

  1. 已创建弹出窗口,已分配 applicationWindow 父级,但其创建上下文是根上下文
  2. Window 关闭,applicationWindow 销毁开始
  3. 因为弹出窗口是 applicationWindow 的子窗口,所以它被销毁了
  4. applicationWindow上下文被破坏
  5. applicationWindow 销毁后,弹出关闭信号被调用,因为它的(根)创建上下文仍然存在,抛出错误,因为信号处理程序中的引用已被销毁

可能解决此问题的最佳方法是在管理对象创建的 JS 文件中不使用 .pramga library。否则,这不是一个特别令人担忧的错误,因为它足够聪明,知道引用已被破坏并且不会访问释放的内存。您可以通过在调用 view.destroy()

之前检查 if (view) 来消除错误