作者:Q-Kevin @ http://www.qkevin.com

当我们写下一下emit signal代码的时候,与这个signal相连接的slot就会被调用,那么这个调用是如何发生的呢?让我们来逐一解开其中的谜团。

让我们来看一段例子代码:

class ZMytestObj : public QObject
{
    Q_OBJECT
signals:
    void sigMenuClicked();
    void sigBtnClicked();
};

MOC编译器在做完预处理之后的代码如下:

// SIGNAL 0
void ZMytestObj::sigMenuClicked()
{
    QMetaObject::activate(this, &staticMetaObject, 0, 0);
}

// SIGNAL 1
void ZMytestObj::sigBtnClicked()
{
    QMetaObject::activate(this, &staticMetaObject, 1, 0);
}

哈哈,看到了把,每一个signal都会被转换为一个与之相对应的成员函数。也就是说,当我们写下这样一行代码:
emit sigBtnClicked();
当程序运行到这里的时候,实际上就是调用了void ZMytestObj::sigBtnClicked() 这个函数。

大家注意比较这两个函数的函数体,
void ZMytestObj::sigMenuClicked()  void ZMytestObj::sigBtnClicked(),
它们唯一的区别就是调用 QMetaObject::activate函数时给出的参数不同,一个是0,一个是1,它们的含义是什么呢?它们表示是这个类中的第几个signal被发送出来了,回头再去看头文件就会发现它们就是在这个类定义中,signal定义出现的顺序,这个参数可是非常重要的,它直接决定了进入这个函数体之后所发生的事情。

当执行流程进入到QMetaObject::activate函数中后,会先从connectionLists这个变量中取出与这个signal相对应的connection list,它根据的就是刚才所传入进来的signal index。这个connectionlist中保存了所有和这个signal相链接的slot的信息,每一对connection(即:signal 和 slot的连接)是这个list中的一项。

在每个一具体的链接记录中,还保存了这个链接的类型,是自动链接类型,还是队列链接类型,或者是阻塞链接类型,不同的类型处理方法还不一样的。这里,我们就只说一下直接调用的类型。

对于直接链接的类型,先找到接收这个signal的对象的指针,然后是处理这个signal的slot的index,已经是否有需要处理的参数,然后就使用这些信息去调用receiver的qt_metcall 方法。

在qt_metcall方法中就简单了,根据slot的index,一个大switch语句,调用相应的slot函数就OK了。

======================================================================
声明:
《Inside Qt Series》专栏文章是(http://www.qkevin.com)原创技术文章。
本系列专栏文章可随意转载,但必须保留本段声明和每一篇文章的原始地址。
作者保留版权,未经作者同意,不得用于任何商业用途

《Inside Qt Series》专栏文章总索引: http://www.qkevin.com/qt
本文原始地址:http://www.qkevin.com/archives/89

10-24 11:44