diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-04-20 18:02:16 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-05-08 11:01:39 +0000 |
commit | 4a5a4245b7f24b53847e96f1eee5445bdae537e6 (patch) | |
tree | c9c4d42fc00df2cd9e12dd2a9224db28b31c70b8 | |
parent | a6f3bb21e3cfcbd8ba3356fb509b465041c35858 (diff) |
macOS: Add root level NSAutoreleasePool for objects autoreleased in main()
Any objects directly or indirectly autoreleased in main(), before we start
the event loop, will never be released, as there are no pools present yet.
This includes all resources allocated during application and window setup,
unless those function have local pools. Ideally that setup code would be
called from within the runloop callstack, where there is a pool present,
but that requires a new main/startup-API for Qt.
To aid in debugging object ownership and hierarchies within Qt, we set up
our own root level pool tied to QApplication, which ensures that most objects
autoreleased in main() will eventually be released and have their dealloc
methods called.
The feature can be disabled by setting an environment variable:
QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL=1
Combined with OBJC_DEBUG_MISSING_POOLS=YES, this allows breaking on the
function objc_autoreleaseNoPool to weed out codepaths in Qt that should
have local pools.
Change-Id: Id02e1edaaaeaa04c53862d7228e519214c99ab51
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
-rw-r--r-- | src/corelib/kernel/qcore_mac_objc.mm | 39 | ||||
-rw-r--r-- | src/corelib/kernel/qcore_mac_p.h | 13 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication_p.h | 8 |
3 files changed, 60 insertions, 0 deletions
diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm index a91c02f54e..272d6179d1 100644 --- a/src/corelib/kernel/qcore_mac_objc.mm +++ b/src/corelib/kernel/qcore_mac_objc.mm @@ -93,6 +93,45 @@ QMacAutoReleasePool::~QMacAutoReleasePool() [static_cast<NSAutoreleasePool*>(pool) drain]; } +#ifdef Q_OS_MACOS +/*! + Ensure that Objective-C objects auto-released in main(), directly or indirectly, + after QCoreApplication construction, are released when the app goes out of scope. + The memory will be reclaimed by the system either way when the process exits, + but by having a root level pool we ensure that the objects get their dealloc + methods called, which is useful for debugging object ownership graphs, etc. +*/ + +QT_END_NAMESPACE +#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE +@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end +@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end +QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER); +QT_BEGIN_NAMESPACE + +const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] = "QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL"; + +QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool() +{ + if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH)) + return; + + pool.reset(new QMacAutoReleasePool); + + [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease]; + + if (qstrcmp(qgetenv("OBJC_DEBUG_MISSING_POOLS"), "YES") == 0) { + qDebug("QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n" \ + "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s", + __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH); + } +} + +QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool() +{ +} +#endif + // ------------------------------------------------------------------------- #ifdef Q_OS_OSX diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index c2c5a519aa..12e9518979 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -68,6 +68,7 @@ #endif #include "qstring.h" +#include "qscopedpointer.h" #if defined( __OBJC__) && defined(QT_NAMESPACE) #define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__) @@ -96,6 +97,18 @@ protected: T value; }; + +#ifdef Q_OS_MACOS +class QMacRootLevelAutoReleasePool +{ +public: + QMacRootLevelAutoReleasePool(); + ~QMacRootLevelAutoReleasePool(); +private: + QScopedPointer<QMacAutoReleasePool> pool; +}; +#endif + /* Helper class that automates refernce counting for CFtypes. After constructing the QCFType object, it can be copied like a diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index c646786296..da6ce1249f 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -58,6 +58,10 @@ #include "private/qobject_p.h" #endif +#ifdef Q_OS_MACOS +#include "private/qcore_mac_p.h" +#endif + QT_BEGIN_NAMESPACE typedef QList<QTranslator*> QTranslatorList; @@ -85,6 +89,10 @@ public: QString appName() const; QString appVersion() const; +#ifdef Q_OS_MACOS + QMacRootLevelAutoReleasePool autoReleasePool; +#endif + #ifdef Q_OS_DARWIN static QString infoDictionaryStringProperty(const QString &propertyName); #endif |