Skip to content

Commit

Permalink
Add private QObjCWeakPointer for tracking Objective-C object lifetime
Browse files Browse the repository at this point in the history
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 fa9449b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 059cbd1)
  • Loading branch information
torarnv authored and AlienCowEatCake committed Dec 3, 2024
1 parent c43d1ef commit 3c9f600
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 19 deletions.
14 changes: 14 additions & 0 deletions src/corelib/kernel/qcore_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -774,5 +774,19 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

// -------------------------------------------------------------------------

#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

83 changes: 83 additions & 0 deletions src/corelib/kernel/qcore_mac_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 3 additions & 2 deletions src/plugins/platforms/cocoa/qcocoadrag.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -36,14 +38,13 @@ class QCocoaDrag : public QPlatformDrag
* 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;

Expand Down
18 changes: 5 additions & 13 deletions src/plugins/platforms/cocoa/qcocoadrag.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,6 @@
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)
Expand Down Expand Up @@ -106,8 +95,11 @@
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);
Expand Down Expand Up @@ -150,7 +142,7 @@

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;

Expand Down
4 changes: 0 additions & 4 deletions src/plugins/platforms/cocoa/qnsview.mm
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,6 @@ - (void)dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
[m_mouseMoveHelper release];

// FIXME: Replace with __weak or someting equivalent
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
nativeDrag->viewDestroyed(self);

[super dealloc];
}

Expand Down

0 comments on commit 3c9f600

Please sign in to comment.