Add private QObjCWeakPointer for tracking Objective-C object lifetime

Uses __weak if possible, and otherwise falls back to associated objects.

Replaces manual tracking of QNSView lifetime for QCocoaDrag.

Task-number: QTBUG-116554
Change-Id: I56f2707bbf5aa14a9efd0ec29e37b157e97cfc3e
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit fa9449bbe8fb20cbdfc5b1e81181a30e4a7c358a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Tor Arne Vestbø 2024-11-11 13:00:10 +01:00 committed by Qt Cherry-pick Bot
parent ef8fc89355
commit 059cbd17ba
5 changed files with 105 additions and 19 deletions

View File

@ -774,5 +774,19 @@ QMacVersion::VersionTuple QMacVersion::libraryVersion()
// -------------------------------------------------------------------------
#if !(__has_feature(objc_arc_weak) && __has_feature(objc_arc_fields))
QT_END_NAMESPACE
@implementation QT_MANGLE_NAMESPACE(WeakPointerLifetimeTracker)
- (void)dealloc
{
*self.pointer = {};
[super dealloc];
}
@end
QT_BEGIN_NAMESPACE
#endif
// -------------------------------------------------------------------------
QT_END_NAMESPACE

View File

@ -462,6 +462,89 @@ qt_objc_cast(id object)
// -------------------------------------------------------------------------
#if defined( __OBJC__)
template <typename T = NSObject>
class QObjCWeakPointer;
#if __has_feature(objc_arc_weak) && __has_feature(objc_arc_fields)
# define USE_OBJC_WEAK 1
#endif
#if !USE_OBJC_WEAK
QT_END_NAMESPACE
#include <objc/runtime.h>
Q_CORE_EXPORT
QT_DECLARE_NAMESPACED_OBJC_INTERFACE(WeakPointerLifetimeTracker, NSObject
@property (atomic, assign) QT_PREPEND_NAMESPACE(QObjCWeakPointer)<NSObject> *pointer;
)
QT_BEGIN_NAMESPACE
#endif
template <typename T>
class QObjCWeakPointer
{
public:
QObjCWeakPointer(T *object = nil) : m_object(object)
{
#if !USE_OBJC_WEAK
trackObjectLifetime();
#endif
}
QObjCWeakPointer(const QObjCWeakPointer &other)
{
QMacAutoReleasePool pool;
m_object = other.m_object;
#if !USE_OBJC_WEAK
trackObjectLifetime();
#endif
}
QObjCWeakPointer &operator=(const QObjCWeakPointer &other)
{
QMacAutoReleasePool pool;
m_object = other.m_object;
#if !USE_OBJC_WEAK
trackObjectLifetime();
#endif
return *this;
}
~QObjCWeakPointer()
{
#if !USE_OBJC_WEAK
if (m_object)
objc_setAssociatedObject(m_object, this, nil, OBJC_ASSOCIATION_RETAIN);
#endif
}
operator T*() const { return static_cast<T*>([[m_object retain] autorelease]); }
private:
#if USE_OBJC_WEAK
__weak
#else
void trackObjectLifetime()
{
if (!m_object)
return;
auto *lifetimeTracker = [WeakPointerLifetimeTracker new];
lifetimeTracker.pointer = reinterpret_cast<QObjCWeakPointer<NSObject>*>(this);
objc_setAssociatedObject(m_object, this, lifetimeTracker, OBJC_ASSOCIATION_RETAIN);
[lifetimeTracker release];
}
#endif
NSObject *m_object = nil;
};
#undef USE_OBJC_WEAK
#endif // __OBJC__
// -------------------------------------------------------------------------
QT_END_NAMESPACE
#endif // QCORE_MAC_P_H

View File

@ -8,6 +8,8 @@
#include <qpa/qplatformdrag.h>
#include <private/qsimpledrag_p.h>
#include <QtCore/private/qcore_mac_p.h>
#include <QtGui/private/qdnd_p.h>
#include <QtGui/private/qinternalmimedata_p.h>
@ -36,14 +38,13 @@ public:
* event and view when handling an event in QNSView
*/
void setLastInputEvent(NSEvent *event, NSView *view);
void viewDestroyed(NSView *view);
void setAcceptedAction(Qt::DropAction act);
void exitDragLoop();
private:
QDrag *m_drag;
NSEvent *m_lastEvent;
NSView *m_lastView;
QObjCWeakPointer<NSView> m_lastView;
Qt::DropAction m_executed_drop_action;
QEventLoop *m_internalDragLoop = nullptr;

View File

@ -38,17 +38,6 @@ void QCocoaDrag::setLastInputEvent(NSEvent *event, NSView *view)
m_lastView = view;
}
void QCocoaDrag::viewDestroyed(NSView *view)
{
if (view == m_lastView) {
if (m_lastEvent.window.contentView == view) {
[m_lastEvent release];
m_lastEvent = nil;
}
m_lastView = nil;
}
}
QMimeData *QCocoaDrag::dragMimeData()
{
if (m_drag)
@ -107,8 +96,11 @@ Qt::DropAction QCocoaDrag::defaultAction(Qt::DropActions possibleActions,
Qt::DropAction QCocoaDrag::drag(QDrag *o)
{
m_executed_drop_action = Qt::IgnoreAction;
if (!m_lastEvent)
if (!m_lastView) {
[m_lastEvent release];
m_lastEvent = nil;
return m_executed_drop_action;
}
m_drag = o;
QMacPasteboard dragBoard(CFStringRef(NSPasteboardNameDrag), QUtiMimeConverter::HandlerScopeFlag::DnD);
@ -151,7 +143,7 @@ bool QCocoaDrag::maybeDragMultipleItems()
const QMacAutoReleasePool pool;
NSView *view = m_lastView ? m_lastView : m_lastEvent.window.contentView;
NSView *view = m_lastView ? static_cast<NSView*>(m_lastView) : m_lastEvent.window.contentView;
if (![view respondsToSelector:@selector(draggingSession:sourceOperationMaskForDraggingContext:)])
return false;

View File

@ -173,10 +173,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMenuHelper);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[m_mouseMoveHelper release];
// FIXME: Replace with __weak or someting equivalent
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
nativeDrag->viewDestroyed(self);
[super dealloc];
}