Add support for unregistering of custom meta types.
This patch addresses a specific Qml problem, where the meta types list will grow indefinitely when unloading and reloading Qml components over and over (in an failed effort to save memory). The implementation is not specific to Qml though, but will cater to all use-cases where registered types may not live until the application's termination. Change-Id: Ic0224dcd19aeb559715ef088b22a30509be2456b Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
This commit is contained in:
parent
472216da2d
commit
91dfab223a
@ -872,13 +872,17 @@ static inline int qMetaTypeStaticType(const char *typeName, int length)
|
|||||||
\internal
|
\internal
|
||||||
Similar to QMetaType::type(), but only looks in the custom set of
|
Similar to QMetaType::type(), but only looks in the custom set of
|
||||||
types, and doesn't lock the mutex.
|
types, and doesn't lock the mutex.
|
||||||
|
The extra \a firstInvalidIndex parameter is an easy way to avoid
|
||||||
|
iterating over customTypes() a second time in registerNormalizedType().
|
||||||
*/
|
*/
|
||||||
static int qMetaTypeCustomType_unlocked(const char *typeName, int length)
|
static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = 0)
|
||||||
{
|
{
|
||||||
const QVector<QCustomTypeInfo> * const ct = customTypes();
|
const QVector<QCustomTypeInfo> * const ct = customTypes();
|
||||||
if (!ct)
|
if (!ct)
|
||||||
return QMetaType::UnknownType;
|
return QMetaType::UnknownType;
|
||||||
|
|
||||||
|
if (firstInvalidIndex)
|
||||||
|
*firstInvalidIndex = -1;
|
||||||
for (int v = 0; v < ct->count(); ++v) {
|
for (int v = 0; v < ct->count(); ++v) {
|
||||||
const QCustomTypeInfo &customInfo = ct->at(v);
|
const QCustomTypeInfo &customInfo = ct->at(v);
|
||||||
if ((length == customInfo.typeName.size())
|
if ((length == customInfo.typeName.size())
|
||||||
@ -887,6 +891,8 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length)
|
|||||||
return customInfo.alias;
|
return customInfo.alias;
|
||||||
return v + QMetaType::User;
|
return v + QMetaType::User;
|
||||||
}
|
}
|
||||||
|
if (firstInvalidIndex && (*firstInvalidIndex < 0) && customInfo.typeName.isEmpty())
|
||||||
|
*firstInvalidIndex = v;
|
||||||
}
|
}
|
||||||
return QMetaType::UnknownType;
|
return QMetaType::UnknownType;
|
||||||
}
|
}
|
||||||
@ -905,6 +911,39 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
|
|||||||
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct, 0, TypeFlags(), 0);
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct, 0, TypeFlags(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
\since 5.5
|
||||||
|
|
||||||
|
Unregisters the user type with the given \a typeId and all its aliases.
|
||||||
|
Returns \c true if the type was unregistered or \c false otherwise.
|
||||||
|
|
||||||
|
This function was added for QML to be able to deregister types after
|
||||||
|
they are unloaded to prevent an infinite increase in custom types for
|
||||||
|
applications that are unloading/reloading components often.
|
||||||
|
*/
|
||||||
|
bool QMetaType::unregisterType(int type)
|
||||||
|
{
|
||||||
|
QWriteLocker locker(customTypesLock());
|
||||||
|
QVector<QCustomTypeInfo> *ct = customTypes();
|
||||||
|
|
||||||
|
// check if user type
|
||||||
|
if ((type < User) || ((type - User) >= ct->size()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// only types without Q_DECLARE_METATYPE can be unregistered
|
||||||
|
if (ct->data()[type - User].flags & WasDeclaredAsMetaType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// invalidate type and all its alias entries
|
||||||
|
for (int v = 0; v < ct->count(); ++v) {
|
||||||
|
if (((v + User) == type) || (ct->at(v).alias == type))
|
||||||
|
ct->data()[v].typeName.clear();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
\since 5.0
|
\since 5.0
|
||||||
@ -977,8 +1016,10 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName,
|
|||||||
int previousFlags = 0;
|
int previousFlags = 0;
|
||||||
if (idx == UnknownType) {
|
if (idx == UnknownType) {
|
||||||
QWriteLocker locker(customTypesLock());
|
QWriteLocker locker(customTypesLock());
|
||||||
|
int posInVector = -1;
|
||||||
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
||||||
normalizedTypeName.size());
|
normalizedTypeName.size(),
|
||||||
|
&posInVector);
|
||||||
if (idx == UnknownType) {
|
if (idx == UnknownType) {
|
||||||
QCustomTypeInfo inf;
|
QCustomTypeInfo inf;
|
||||||
inf.typeName = normalizedTypeName;
|
inf.typeName = normalizedTypeName;
|
||||||
@ -992,8 +1033,13 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName,
|
|||||||
inf.size = size;
|
inf.size = size;
|
||||||
inf.flags = flags;
|
inf.flags = flags;
|
||||||
inf.metaObject = metaObject;
|
inf.metaObject = metaObject;
|
||||||
idx = ct->size() + User;
|
if (posInVector == -1) {
|
||||||
ct->append(inf);
|
idx = ct->size() + User;
|
||||||
|
ct->append(inf);
|
||||||
|
} else {
|
||||||
|
idx = posInVector + User;
|
||||||
|
ct->data()[posInVector] = inf;
|
||||||
|
}
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1075,14 +1121,19 @@ int QMetaType::registerNormalizedTypedef(const NS(QByteArray) &normalizedTypeNam
|
|||||||
|
|
||||||
if (idx == UnknownType) {
|
if (idx == UnknownType) {
|
||||||
QWriteLocker locker(customTypesLock());
|
QWriteLocker locker(customTypesLock());
|
||||||
|
int posInVector = -1;
|
||||||
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
||||||
normalizedTypeName.size());
|
normalizedTypeName.size(),
|
||||||
|
&posInVector);
|
||||||
|
|
||||||
if (idx == UnknownType) {
|
if (idx == UnknownType) {
|
||||||
QCustomTypeInfo inf;
|
QCustomTypeInfo inf;
|
||||||
inf.typeName = normalizedTypeName;
|
inf.typeName = normalizedTypeName;
|
||||||
inf.alias = aliasId;
|
inf.alias = aliasId;
|
||||||
ct->append(inf);
|
if (posInVector == -1)
|
||||||
|
ct->append(inf);
|
||||||
|
else
|
||||||
|
ct->data()[posInVector] = inf;
|
||||||
return aliasId;
|
return aliasId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,6 +484,7 @@ public:
|
|||||||
int size,
|
int size,
|
||||||
QMetaType::TypeFlags flags,
|
QMetaType::TypeFlags flags,
|
||||||
const QMetaObject *metaObject);
|
const QMetaObject *metaObject);
|
||||||
|
static bool unregisterType(int type);
|
||||||
static int registerNormalizedType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName, Deleter deleter,
|
static int registerNormalizedType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName, Deleter deleter,
|
||||||
Creator creator,
|
Creator creator,
|
||||||
Destructor destructor,
|
Destructor destructor,
|
||||||
|
@ -1254,6 +1254,54 @@ void tst_QMetaType::registerType()
|
|||||||
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
|
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
|
||||||
|
|
||||||
QCOMPARE(QMetaType::type("MyFoo"), fooId);
|
QCOMPARE(QMetaType::type("MyFoo"), fooId);
|
||||||
|
|
||||||
|
// cannot unregister built-in types
|
||||||
|
QVERIFY(!QMetaType::unregisterType(QMetaType::QString));
|
||||||
|
QCOMPARE(QMetaType::type("QString"), int(QMetaType::QString));
|
||||||
|
QCOMPARE(QMetaType::type("MyString"), int(QMetaType::QString));
|
||||||
|
|
||||||
|
// cannot unregister declared types
|
||||||
|
QVERIFY(!QMetaType::unregisterType(fooId));
|
||||||
|
QCOMPARE(QMetaType::type("TestSpace::Foo"), fooId);
|
||||||
|
QCOMPARE(QMetaType::type("MyFoo"), fooId);
|
||||||
|
|
||||||
|
// test unregistration of dynamic types (used by Qml)
|
||||||
|
int unregId = QMetaType::registerType("UnregisterMe",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct,
|
||||||
|
0, QMetaType::TypeFlags(), 0);
|
||||||
|
QCOMPARE(QMetaType::registerTypedef("UnregisterMeTypedef", unregId), unregId);
|
||||||
|
int unregId2 = QMetaType::registerType("UnregisterMe2",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct,
|
||||||
|
0, QMetaType::TypeFlags(), 0);
|
||||||
|
QVERIFY(unregId >= int(QMetaType::User));
|
||||||
|
QCOMPARE(unregId2, unregId + 2);
|
||||||
|
|
||||||
|
QVERIFY(QMetaType::unregisterType(unregId));
|
||||||
|
QCOMPARE(QMetaType::type("UnregisterMe"), 0);
|
||||||
|
QCOMPARE(QMetaType::type("UnregisterMeTypedef"), 0);
|
||||||
|
QCOMPARE(QMetaType::type("UnregisterMe2"), unregId2);
|
||||||
|
QVERIFY(QMetaType::unregisterType(unregId2));
|
||||||
|
QCOMPARE(QMetaType::type("UnregisterMe2"), 0);
|
||||||
|
|
||||||
|
// re-registering should always return the lowest free index
|
||||||
|
QCOMPARE(QMetaType::registerType("UnregisterMe2",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct,
|
||||||
|
0, QMetaType::TypeFlags(), 0), unregId);
|
||||||
|
QCOMPARE(QMetaType::registerType("UnregisterMe",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct,
|
||||||
|
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct,
|
||||||
|
0, QMetaType::TypeFlags(), 0), unregId + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class IsRegisteredDummyType { };
|
class IsRegisteredDummyType { };
|
||||||
|
Loading…
Reference in New Issue
Block a user