一、简介
Qt meta-object系统基于三个方面:
1 QObject提供一个基类,方便派生类使用meta-object系统的功能。
2 Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性,信号,槽
3 Meta Object编译器(MOC),为每个QObject派生类生成代码, 以支持meta-object功能
QObject定义了从一个QObject对象访问meta-object功能的接口,
Q_OBJECT宏用来告诉编译器该类需要激活meta- object功能,编译器在扫描一个源文件时,
如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成
该类对应 MetaObject类以及对QObject的函数override。
QObject和QMetaObject:
QMeatObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除
了类型信息外,还包含QT中特有的signal&slot信息。
QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个
方法是virtual方法。如上文所说,如果一个类的声明中包含了 Q_OBJECT宏,编译器
会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()
方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用
metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不
会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()
返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其
实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该
声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
1、基本信息
const char * className () const;
const QMetaObject * superClass () const
2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。
int classInfoCount () const
int classInfoOffset () const
QMetaClassInfo classInfo ( int index ) const
int indexOfClassInfo ( const char * name ) const
3、contructor:提供该类的构造方法信息
QMetaMethod constructor ( int index ) const
int constructorCount () const
int indexOfConstructor ( const char * constructor ) const
4、enum:描述该类声明体中所包含的枚举类型信息
QMetaEnum enumerator ( int index ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int indexOfSlot ( const char * slot ) const
6、property:类型的属性信息
QMetaProperty property ( int index ) const
int propertyCount () const
int propertyOffset () const
int indexOfProperty ( const char * name ) const
QMetaProperty userProperty () const //返回类中设置了USER flag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。
比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。
Qt这么设计的原因应该是避免meta信息的臃肿。
二、meta数据和数据结构
如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成meta信息,这个信息将保存在moc文件中。
QMetaObject包含唯一的数据成员如下(见头文件qobjectdefs.h)
struct QMetaObject
{
private:
struct { // private data
const QMetaObject *superdata; //父类QMetaObject实例的指针
const char *stringdata; //一段字符串内存块,包含MetaObject信息之字符串信息
const uint *data; //一段二级制内存块,包含MetaObject信息之二进制信息
const void *extradata; //额外字段,暂未使用
} d;
}
QMetaObjectPrivate的数据定义:
QMetaObjectPrivate是QMetaObject的私有实现类,
其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,
比如classInfoCount、methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。
下文结合这两个内存块的结构再分析个字段的含义。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision
};
moc文件
qt_meta_data_TestObject::定义的正是QMetaObject::d.data指向的信息块;
qt_meta_stringdata_TestObject:定义的是QMetaObject::d.dataString指向的信息块;
const QMetaObject TestObject::staticMetaObject :定义TestObject类的MetaObject
实例,从中可以看出QMetaObject各个字段是如何被赋值的;
const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,
返回上述的MetaObject实例指针。
TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或
访问property,动态方法调用属性访问正是依赖于这个方法。
TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,
signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。
meta信息在moc文件中以静态数据的形式被定义,其排列有点类似可执行文件中静态数据信息的排布。
三、QMetaObject接口实现
QMetaObject::className()
inline const char *QMetaObject::className() const
{ return d.stringdata; }
从前一篇可知,d.stringdata就是那块字符串数据,包含若干c字符串(以'/0')结尾。
这个字符串序列的第一个字符串,正是类名。
QMetaObject::superClass()
inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }
QMetaObject::classInfoCount()
int QMetaObject::classInfoCount() const
{
int n = priv(d.data)->classInfoCount;
const QMetaObject *m = d.superdata;
while (m) {
n += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return n;
}
从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。
函数priv是一个简单inline函数:
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
由前一篇可知,d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。
QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision
};
QMetaObject::classInfoOffset ()
int classInfoOffset () const
{
int offset = 0;
const QMetaObject *m = d.superdata;
while (m) {
offset += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return offset;
}
该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量。
QMetaObject::classInfo (int index)
QMetaClassInfo classInfo ( int index ) const
{
int i = index;
i -= classInfoOffset();
if (i < 0 && d.superdata)
return d.superdata->classInfo(index);
QMetaClassInfo result;
if (i >= 0 && i < priv(d.data)->classInfoCount) {
result.mobj = this;
result.handle = priv(d.data)->classInfoData + 2*i;
}
return result;
}
这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;
每条classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData + 2*i”这个表达式的值
就是第i个classinfo的信息在d.data中的偏移。
QMetaObject::indexOfClassInfo ()
按照继承层次,从下往上寻找名字为name的classinfo。
参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息
的第一个32位值。该值是字符信息块d.stringdata中的索引值。
因此m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。
int constructorCount () const
QMetaMethod constructor ( int index ) const
int indexOfConstructor ( const char * constructor ) const
int enumeratorCount () const
int enumeratorOffset () const
QMetaEnum enumerator ( int index ) const
int indexOfEnumerator ( const char * name ) const
int methodCount () const
int methodOffset () const
QMetaMethod method ( int index ) const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal)
可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。
int indexOfSlot ( const char * slot ) const 略;
int propertyCount () const 略;
int propertyOffset () const 略;
int indexOfProperty ( const char * name ) const 略;
QMetaProperty property ( int index ) const
该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。
QMetaProperty userProperty () const
从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。
四、meta call
所谓meta call就是通过object的meta system的支持来动态调用object的方法,
metacall也是signal&slot的机制的基石。
QMetaObject::invokeMethod()
bool invokeMethod (
QObject * obj ,
const char * member ,
Qt::ConnectionType type ,
QGenericReturnArgument ret ,
QGenericArgument val0 = QGenericArgument( 0 ),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()
)
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,
type参数表明该调用时同步的还是异步的。ret是一个通用的用来存储返回值的类型,
后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。
参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,
这是运行时的行为,参数类型转换是编译时的行为。
然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个QMetaMethod值,
再将调用委托给QMetaMethod::invoke方法。
QMetaMethod::invoke
代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,
再依据当前线程和被调对象所属线程来调整connnection type;
如果是directconnection,直接调用
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param),
param是将所有参数值指针排列组成的指针数组。
如果不是directconnection,也即异步调用,
就通过一个post一个QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。
QMetaObject::metacall
如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject来调用,
这个条件实际上就是object就是QObject类型,而不是派生类型。否则调用object::qt_metacall。
QMetaCallEvent
object->qt_metacall
property的动态调用的实现方式一定和invocalbe method是一致的.
五、信号和槽
宏SLOT,SIGNAL
在qobjectdefs.h中有这样的定义:
# define METHOD(a) "0"#a
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
不过是在方法签名之前加了一个数字标记。
因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。
QObject::connect()
这段代码的做的事情就是将signal在sender的meta system中的signal索引找出,
以及接受者方法(signal或slot)在receiver的meta system中的索引找出来。
在委托QMetaObjectPrivate::connect()执行连接
QMetaObjectPrivate::connect()
这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。
然后创建一个QObjectPrivate::Connection结构,这个结构包含了sender,receiver,
接受方法的method_index,然后加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。
可以推测,当signal方法被调用时,一定会到这个结构中查找所有连接到该signal的connection,
connection保存了receiver和method index,由上一篇可知,可以很容易调用到receiver的对应method。
Qt meta-object系统基于三个方面:
1 QObject提供一个基类,方便派生类使用meta-object系统的功能。
2 Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性,信号,槽
3 Meta Object编译器(MOC),为每个QObject派生类生成代码, 以支持meta-object功能
QObject定义了从一个QObject对象访问meta-object功能的接口,
Q_OBJECT宏用来告诉编译器该类需要激活meta- object功能,编译器在扫描一个源文件时,
如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成
该类对应 MetaObject类以及对QObject的函数override。
QObject和QMetaObject:
QMeatObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除
了类型信息外,还包含QT中特有的signal&slot信息。
QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个
方法是virtual方法。如上文所说,如果一个类的声明中包含了 Q_OBJECT宏,编译器
会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()
方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用
metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不
会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()
返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其
实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该
声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
1、基本信息
const char * className () const;
const QMetaObject * superClass () const
2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。
int classInfoCount () const
int classInfoOffset () const
QMetaClassInfo classInfo ( int index ) const
int indexOfClassInfo ( const char * name ) const
3、contructor:提供该类的构造方法信息
QMetaMethod constructor ( int index ) const
int constructorCount () const
int indexOfConstructor ( const char * constructor ) const
4、enum:描述该类声明体中所包含的枚举类型信息
QMetaEnum enumerator ( int index ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int indexOfSlot ( const char * slot ) const
6、property:类型的属性信息
QMetaProperty property ( int index ) const
int propertyCount () const
int propertyOffset () const
int indexOfProperty ( const char * name ) const
QMetaProperty userProperty () const //返回类中设置了USER flag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。
比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。
Qt这么设计的原因应该是避免meta信息的臃肿。
二、meta数据和数据结构
如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成meta信息,这个信息将保存在moc文件中。
QMetaObject包含唯一的数据成员如下(见头文件qobjectdefs.h)
struct QMetaObject
{
private:
struct { // private data
const QMetaObject *superdata; //父类QMetaObject实例的指针
const char *stringdata; //一段字符串内存块,包含MetaObject信息之字符串信息
const uint *data; //一段二级制内存块,包含MetaObject信息之二进制信息
const void *extradata; //额外字段,暂未使用
} d;
}
QMetaObjectPrivate的数据定义:
QMetaObjectPrivate是QMetaObject的私有实现类,
其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,
比如classInfoCount、methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。
下文结合这两个内存块的结构再分析个字段的含义。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision
};
moc文件
qt_meta_data_TestObject::定义的正是QMetaObject::d.data指向的信息块;
qt_meta_stringdata_TestObject:定义的是QMetaObject::d.dataString指向的信息块;
const QMetaObject TestObject::staticMetaObject :定义TestObject类的MetaObject
实例,从中可以看出QMetaObject各个字段是如何被赋值的;
const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,
返回上述的MetaObject实例指针。
TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或
访问property,动态方法调用属性访问正是依赖于这个方法。
TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,
signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。
meta信息在moc文件中以静态数据的形式被定义,其排列有点类似可执行文件中静态数据信息的排布。
三、QMetaObject接口实现
QMetaObject::className()
inline const char *QMetaObject::className() const
{ return d.stringdata; }
从前一篇可知,d.stringdata就是那块字符串数据,包含若干c字符串(以'/0')结尾。
这个字符串序列的第一个字符串,正是类名。
QMetaObject::superClass()
inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }
QMetaObject::classInfoCount()
int QMetaObject::classInfoCount() const
{
int n = priv(d.data)->classInfoCount;
const QMetaObject *m = d.superdata;
while (m) {
n += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return n;
}
从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。
函数priv是一个简单inline函数:
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
由前一篇可知,d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。
QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision
};
QMetaObject::classInfoOffset ()
int classInfoOffset () const
{
int offset = 0;
const QMetaObject *m = d.superdata;
while (m) {
offset += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return offset;
}
该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量。
QMetaObject::classInfo (int index)
QMetaClassInfo classInfo ( int index ) const
{
int i = index;
i -= classInfoOffset();
if (i < 0 && d.superdata)
return d.superdata->classInfo(index);
QMetaClassInfo result;
if (i >= 0 && i < priv(d.data)->classInfoCount) {
result.mobj = this;
result.handle = priv(d.data)->classInfoData + 2*i;
}
return result;
}
这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;
每条classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData + 2*i”这个表达式的值
就是第i个classinfo的信息在d.data中的偏移。
QMetaObject::indexOfClassInfo ()
按照继承层次,从下往上寻找名字为name的classinfo。
参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息
的第一个32位值。该值是字符信息块d.stringdata中的索引值。
因此m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。
int constructorCount () const
QMetaMethod constructor ( int index ) const
int indexOfConstructor ( const char * constructor ) const
int enumeratorCount () const
int enumeratorOffset () const
QMetaEnum enumerator ( int index ) const
int indexOfEnumerator ( const char * name ) const
int methodCount () const
int methodOffset () const
QMetaMethod method ( int index ) const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal)
可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。
int indexOfSlot ( const char * slot ) const 略;
int propertyCount () const 略;
int propertyOffset () const 略;
int indexOfProperty ( const char * name ) const 略;
QMetaProperty property ( int index ) const
该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。
QMetaProperty userProperty () const
从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。
四、meta call
所谓meta call就是通过object的meta system的支持来动态调用object的方法,
metacall也是signal&slot的机制的基石。
QMetaObject::invokeMethod()
bool invokeMethod (
QObject * obj ,
const char * member ,
Qt::ConnectionType type ,
QGenericReturnArgument ret ,
QGenericArgument val0 = QGenericArgument( 0 ),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()
)
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,
type参数表明该调用时同步的还是异步的。ret是一个通用的用来存储返回值的类型,
后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。
参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,
这是运行时的行为,参数类型转换是编译时的行为。
然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个QMetaMethod值,
再将调用委托给QMetaMethod::invoke方法。
QMetaMethod::invoke
代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,
再依据当前线程和被调对象所属线程来调整connnection type;
如果是directconnection,直接调用
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param),
param是将所有参数值指针排列组成的指针数组。
如果不是directconnection,也即异步调用,
就通过一个post一个QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。
QMetaObject::metacall
如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject来调用,
这个条件实际上就是object就是QObject类型,而不是派生类型。否则调用object::qt_metacall。
QMetaCallEvent
object->qt_metacall
property的动态调用的实现方式一定和invocalbe method是一致的.
五、信号和槽
宏SLOT,SIGNAL
在qobjectdefs.h中有这样的定义:
# define METHOD(a) "0"#a
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
不过是在方法签名之前加了一个数字标记。
因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。
QObject::connect()
这段代码的做的事情就是将signal在sender的meta system中的signal索引找出,
以及接受者方法(signal或slot)在receiver的meta system中的索引找出来。
在委托QMetaObjectPrivate::connect()执行连接
QMetaObjectPrivate::connect()
这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。
然后创建一个QObjectPrivate::Connection结构,这个结构包含了sender,receiver,
接受方法的method_index,然后加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。
可以推测,当signal方法被调用时,一定会到这个结构中查找所有连接到该signal的connection,
connection保存了receiver和method index,由上一篇可知,可以很容易调用到receiver的对应method。