6. Qt 信号与信号槽 (7)-QMetaObject:: activate

本文详细剖析了Qt框架中QMetaObject的activate方法,该方法负责处理对象间的信号与槽的激活过程。内容涉及信号触发时的阻塞检查、连接列表的获取、线程安全的信号激活以及不同类型的连接处理(如立即调用或放入事件队列)。通过对源码的解读,揭示了Qt中事件传递的底层机制。
摘要由CSDN通过智能技术生成

最后调用callFunction() 或者metaCall()

在这里插入图片描述

void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
{
 // 这里得到的是QObject的数据,首先判断是否为阻塞设置
    if (sender->d_func()->blockSig)
        return;
 // 得到全局链表
    QConnectionList * const list = ::connectionList();
    if (!list)
        return;
    QReadLocker locker(&list->lock);
    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        locker.unlock();
        qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,
                                                         argv ? argv : empty_argv);
        locker.relock();
    }
 // 在sender的哈希表中得到sender的连接
    QConnectionList::Hash::const_iterator it = list->sendersHash.find(sender);
    const QConnectionList::Hash::const_iterator end = list->sendersHash.constEnd();
    if (it == end) {
        if (qt_signal_spy_callback_set.signal_end_callback != 0) {
            locker.unlock();
            qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
            locker.relock();
        }
        return;
    }
    QThread * const currentThread = QThread::currentThread();
    const int currentQThreadId = currentThread ? QThreadData::get(currentThread)->id : -1;
 // 记录sender连接的索引
    QVarLengthArray<int> connections;
    for (; it != end && it.key() == sender; ++it) {
        connections.append(it.value());
  // 打上使用标记,因为可能是放在队列中
        list->connections[it.value()].inUse = 1;
    }
    for (int i = 0; i < connections.size(); ++i) {
        const int at = connections.constData()[connections.size() - (i + 1)];
        QConnectionList * const list = ::connectionList();
  // 得到连接
        QConnection &c = list->connections[at];
        c.inUse = 0;
        if (!c.receiver || (c.signal < from_signal_index || c.signal > to_signal_index))
            continue;
  // 判断是否放到队列中
        // determine if this connection should be sent immediately or
        // put into the event queue
        if ((c.type == Qt::AutoConnection
             && (currentQThreadId != sender->d_func()->thread
                 || c.receiver->d_func()->thread != sender->d_func()->thread))
            || (c.type == Qt::QueuedConnection)) {
            ::queued_activate(sender, c, argv);
            continue;
        }
  // 为receiver设置当前发送者
        const int method = c.method;
        QObject * const previousSender = c.receiver->d_func()->currentSender;
        c.receiver->d_func()->currentSender = sender;
        list->lock.unlock();
        if (qt_signal_spy_callback_set.slot_begin_callback != 0)
            qt_signal_spy_callback_set.slot_begin_callback(c.receiver, method, argv ? argv : empty_argv);
#if defined(QT_NO_EXCEPTIONS)
        c.receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
        try {
   // 调用receiver的方法
            c.receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
        } catch (...) {
            list->lock.lockForRead();
            if (c.receiver)
                c.receiver->d_func()->currentSender = previousSender;
            throw;
        }
#endif
        if (qt_signal_spy_callback_set.slot_end_callback != 0)
            qt_signal_spy_callback_set.slot_end_callback(c.receiver, method);
        list->lock.lockForRead();
        if (c.receiver)
            c.receiver->d_func()->currentSender = previousSender;
    }
    if (qt_signal_spy_callback_set.signal_end_callback != 0) {
        locker.unlock();
        qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
        locker.relock();
    }
}


void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
    int signal_index = signalOffset + local_signal_index;

    /* 我们所做的第一件事,是快速检查一个 64 位的位蒙版 bit-mask。如果为 0,
     * 我们就知道没有连接到该信号的东西,可以迅速返回,
     * 这意味着,发送一个没有与槽连接的信号是相当迅速的。*/
    if (!sender->d_func()->isSignalConnected(signal_index))
        return; // nothing connected to these signals, and no spy

    /* ... 跳过调试信息和 QML 调用,以及一些合理性检查 ... */

    /* 使用互斥锁,因为 connectionList 中的所有操作都是线程安全的 */
    QMutexLocker locker(signalSlotLock(sender));

    /* 获取该信号的 ConnectionList。此处做了一些简化。真实的代码还为列表添加了引用计数和一些合理性检查 */
    QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
    const QObjectPrivate::ConnectionList *list =
        &connectionLists->at(signal_index);

    QObjectPrivate::Connection *c = list->first;
    if (!c) continue;
    // 我们需要最后一次检查,确保在信号发出的过程中添加的信号不会在本次发出过程被触发。
    QObjectPrivate::Connection *last = list->last;

    /* 遍历槽 */
    do {
        if (!c->receiver)
            continue;

        QObject * const receiver = c->receiver;
        const bool receiverInSameThread = QThread::currentThreadId() == receiver->d_func()->threadData->threadId;

        // 确定该连接应该立即发出,还是放入事件队列
        if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
            || (c->connectionType == Qt::QueuedConnection)) {
            /* 从根本上说,就是复制参数,发出事件 */
            queued_activate(sender, signal_index, c, argv);
            continue;
        } else if (c->connectionType == Qt::BlockingQueuedConnection) {
            /* ... 跳过 ... */
            continue;
        }

        /* 助手结构体,设置 sender()(并且在超出作用域之后重新设回 */
        QConnectionSenderSwitcher sw;
        if (receiverInSameThread)
            sw.switchSender(receiver, sender, signal_index);

        const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
        const int method_relative = c->method_relative;
        if (c->isSlotObject) {
            /* ... 跳过 ...  Qt5 风格的指向函数指针的连接 */
        } else if (callFunction && c->method_offset metaObject()->methodOffset()) {
            /* 如果存在 callFunction(指向由 moc 生成的 qt_static_metacall 的指针,
             * 调用该函数。还需要检查已保存的 metodOffset 是否依旧可用
             * (因为我们可能在析构函数中调用) */
            locker.unlock(); // 实际调用时不能持有锁
            callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);
            locker.relock();
        } else {
            /* 动态对象 */
            const int method = method_relative + c->method_offset;
            locker.unlock();
            metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
            locker.relock();
        }

        // 检查该对象是否被槽析构
        if (connectionLists->orphaned) break;
    } while (c != last && (c = c->nextConnectionList) != 0);
}
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

thefist11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值