您的位置:首页 > 移动开发 > Objective-C

Object::connect函数的学习

2016-07-18 00:05 381 查看
从Qobject(QObject.h)源码中可以看到QObject::connect的定义是这样的:

static bool connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *member, Qt::ConnectionType =
#ifdef qdoc
Qt::AutoConnection
#else
#ifdef QT3_SUPPORT
Qt::AutoCompatConnection
#else
Qt::AutoConnection
#endif
#endif
);
inline bool connect(const QObject *sender, const char *signal,
const char *member, Qt::ConnectionType type =
#ifdef qdoc
Qt::AutoConnection
#else
#ifdef QT3_SUPPORT
Qt::AutoCompatConnection
#else
Qt::AutoConnection
#endif
#endif
) const;


其中第二个connect的实现其实只有一句话:

{ return connect(asender, asignal, this, amember, atype); }


所以对于connect函数的学习其实就是研究第一个connect函数。

我们在使用connect函数的时候一般是这样调用的:

 
connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));


这里用到了两个宏:SIGNAL() 和SLOT();通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。

在qobjectdefs.h中可以看到SIGNAL() 和SLOT()的宏定义:

#ifndef QT_NO_DEBUG
# define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__)
# define METHOD(a)   qFlagLocation("0"#a QLOCATION)
# define SLOT(a)     qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)
#else
# define METHOD(a)   "0"#a
# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a
#endif


所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。

比如:SIGNAL(read())展开后就是"2read()";同理SLOT(read())展开后就是"1read()"。

connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));
//实际上就是connect(sender,“2signal()”,receiver,“1slot())”;


搞明白了实际的参数就可以来看connect的真正实现过程了,在QObject.cpp文件中可以找到connect的实现代码。

bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;
}

if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return false;
}
QByteArray tmp_signal_name;

if (!check_signal_macro(sender, signal, "connect", "bind"))
return false;
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
int signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;

signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
}

QByteArray tmp_method_name;
int membcode = extract_code(method);

if (!check_method_code(membcode, receiver, method, "connect"))
return false;
const char *method_arg = method;
++method; // skip code

const QMetaObject *rmeta = receiver->metaObject();
int method_index = -1;
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
if (method_index < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
}

if (method_index < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
if (!QMetaObject::checkConnectArgs(signal, method)) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n        %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return false;
}

int *types = 0;
if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
return false;

QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
const_cast<QObject*>(sender)->connectNotify(signal - 1);
return true;


上面是去除了debug代码的connect实现。

const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;


判断连接是否已经建立。

QInternal::ConnectCallback在qglobal.cpp中实现。

bool QInternal::activateCallbacks(Callback cb, void **parameters)
{
Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");

QInternal_CallBackTable *cbt = global_callback_table();
if (cbt && cb < cbt->callbacks.size()) {
QList<qInternalCallback> callbacks = cbt->callbacks[cb];
bool ret = false;
for (int i=0; i<callbacks.size(); ++i)
ret |= (callbacks.at(i))(parameters);
return ret;
}
return false;
}


QInternal_CallBackTable 定义为(qglobal.cpp)

struct QInternal_CallBackTable {
QVector<QList<qInternalCallback> > callbacks;
};


qInternalCallback定义为(qnamespace.h)

typedef bool (*qInternalCallback)(void **);这是一个函数指针 返回值是bool,只有一个参数为void**。这个指针在调用registerCallback加入列表。
if (!check_signal_macro(sender, signal, "connect", "bind"))
return false;


判断signal是否合法。

在QObject.cpp文件中可以找到check_signal_macro的实现

static bool check_signal_macro(const QObject *sender, const char *signal,
const char *func, const char *op)
{
int sigcode = extract_code(signal);
if (sigcode != QSIGNAL_CODE) {
if (sigcode == QSLOT_CODE)
qWarning("Object::%s: Attempt to %s non-signal %s::%s",
func, op, sender->metaObject()->className(), signal+1);
else
qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s",
func, op, sender->metaObject()->className(), signal);
return false;
}
return true;
}


extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。

static int extract_code(const char *member)
{
// extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE
return (((int)(*member) - '0') & 0x3);
}


这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。

#ifdef QT3_SUPPORT
#define METHOD_CODE   0                        // member type codes
#define SLOT_CODE     1
#define SIGNAL_CODE   2
#endif


这个定义与之前的SIGNAL和SLOT的定义是对应的。

const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
int signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;

signal_index = smeta->indexOfSignal(signal);
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
}


获取signal的索引。

metaObject()是在moc_name.cpp文件中生成的。

return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;


其中staticMetaObject也是在moc文件中定义的

const QMetaObject MainWindow::staticMetaObject = {
{ &QMainWindow::staticMetaObject, qt_meta_stringdata_MainWindow,
qt_meta_data_MainWindow, 0 }
};


qt_meta_stringdata_MainWindow(具体名字和类名有关)就是staticconstchar[]类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。

qt_meta_data_MainWindow(具体名字和类名有关)是staticconstuint[]类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。

enum MethodFlags  {
AccessPrivate = 0x00,
AccessProtected = 0x01,
AccessPublic = 0x02,
AccessMask = 0x03, //mask

MethodMethod = 0x00,
MethodSignal = 0x04,
MethodSlot = 0x08,
MethodConstructor = 0x0c,
MethodTypeMask = 0x0c,

MethodCompatibility = 0x10,
MethodCloned = 0x20,
MethodScriptable = 0x40
};


indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。

QByteArray tmp_method_name;
int membcode = extract_code(method);

if (!check_method_code(membcode, receiver, method, "connect"))
return false;
const char *method_arg = method;
++method; // skip code

const QMetaObject *rmeta = receiver->metaObject();
int method_index = -1;
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
if (method_index < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
}

if (method_index < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}


校验method并且查找它的索引。过程与signal类似。

if (!QMetaObject::checkConnectArgs(signal, method)) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n        %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return false;
}


判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少(method与signal前几个参数一致)的时候返回true,其它返回false。

int *types = 0;
if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
return false;


如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个

static conststruct { constchar * typeName;int type;} types[];在这里记录了全部类型和名称如({"void",QMetaType::Void});Void在Qmetatype.h中定义。

QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);


调用QMetaObject的connect函数,再次不详细写出。

const_cast<QObject*>(sender)->connectNotify(signal - 1);


最后调用虚函数connectNotify表示connect已经执行完成。

关于QObject::connect中函数参数

1: 首先要链接的两个类必须继承与QObject,同时添加 Q_OBJECT;

[u]2:在Qt中QObject::connect中填写的signal和slot函数,一定要填写参数类型;
[/u]

[u][u]3: QObject::connect中的signal
和 slot 函数一定要有参数类型, 但是,不可以有参数:

[/u][/u]



4:在参数部分,如定义了:typedef unsigned char       BYTE;

//signal:

void  CClass1::setBuf( BYTE * );

//pulic slots:

void CClass2::setBuf(  unsigned char * )

{

    MessageBoxQ( "消息响应" );

}

这样,通常可以用BYTE替换unsigned char ;

但是在QObject::connect(  pClass1, SIGNAL( setBuf( BYTE * )  ),  pClass2, SLOT( setBuf( unsigned char * ) )  );

编译时不会有问题的,但是消息无法响应,

修改方法:

void CClass2::setBuf(  unsigned char * ) 为 void CClass2::setBuf(  BYTE*  )

QObject::connect(  pClass1, SIGNAL( setBuf( BYTE * )  ),  pClass2, SLOT( setBuf( BYTE * ) )  );

可能错误的原因:

SIGNAL 和 SLOT是将其内容转为字符串,

所以,如果消息传递前 函数和参数 是按照字符串严格比较话,那么 “BYTE” 和“unsigned char” 就不同了;

具体原因需要分析源码;
5: 关于  QObject::connect函数声明: QMetaObject::Connectionconnect(const QObject * sender, const char * signal, const QObject * receiver,
const char * method, Qt::ConnectionType type = Qt::AutoConnection)


[u][u]6:connect中的常量参数,引用参数,一般参数;
[/u][/u]

[u][/u]

1:  SIGNAL函数, 常量引用参数, SLOT函数的参数一般链接也应该是常量引用参数;如:

signals:
void resultReady( const QString & s);

public slots:

void slot_string( const QString & str )
{
// str = "123";
qDebug()<<str;
}

connect( this, SIGNAL(resultReady(const QString&)), this, SLOT(slot_string(const QString&)) , Qt::DirectConnection);

因为上述是const参数,所以slot中无法修改参数数据;如果想修改数据有两个方法:

(1): 赋值一个新的变量应用;

(2): 修改slot函数参数:

void slot_string( QString str )
{
str = "123";
qDebug()<<str;
}

connect( this, SIGNAL(resultReady(const QString&)), this, SLOT(slot_string(QString)) , Qt::DirectConnection);

注:大部分的Qt的SIGNAL函数都是常量引用参数;

2: SIGNAL函数, 引用参数, SLOT函数的参数也可以是 引用参数 或一般参数;

3:SIGNAL函数和SLOT函数都是一般参数;

说明:

1:这里主要讲的是区别就是在connect的时候,SGNAL ,SLOT的参数在引用于非引用对效率的影响和参数数据的修改;

2:在线程间参数传递的时候,SGNAL和SLOT位于两个不同的线程,如果connect的链接类型是Qt::QueuedConnection非阻塞运行,SLOT的参数是一般参数,那么SLOT函数的参数就是一个数据copy,

在线程emit SGNAL后,如果线程中作为从参数的数据丢失或改变,但是SLOT函数用因为有数据备份,所以SLOT函数中的参数数据不会受到影响;

[u]7:关于connect函数:connect函数后多种重载方法,常用的有:
[/u]



1:QMetaObject::Connection QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection) [static]

for example:

QLabel *label = new QLabel;

QScrollBar *scrollBar = new QScrollBar;

QObject::connect( scrollBar, SIGNAL(valueChanged(int)),     label,  SLOT(setNum(int)) );

2:

QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * receiver, PointerToMemberFunction method, Qt::ConnectionType type) [static]

Example:

QLabel *label = new QLabel;

QLineEdit *lineEdit = new QLineEdit;

QObject::connect( lineEdit, &QLineEdit::textChanged,   label,  &QLabel::setText );

[u][u]
[/u][/u]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: