首页 > 解决方案 > 如何在 macOS 的非主线程上有一个事件循环?

问题描述

另一个问题相关:我需要在 macOS 上收集有关当前活动应用程序的信息。

链接的 QA 答案提供了一种在活动应用程序更改时获得警报(事件)的机制,但在单独的线程上运行时会崩溃:

FocusDetector::AppFocus focus;
focus.run();

//std::thread threadListener(&FocusDetector::AppFocus::run, &focus); //Does not works
//if (threadListener.joinable())
//{
//  threadListener.join();
//}

.

    *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /xxxxxxx/NSUndoManager.m:363
2020-11-24 08:54:41.758 focus_detection[81935:18248374] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.'
*** First throw call stack:
(
    0   CoreFoundation            0x00007fff3006cb57 __exceptionPreprocess + 250
    1   libobjc.A.dylib           0x00007fff68eb35bf objc_exception_throw + 48
    2   CoreFoundation            0x00007fff30095d08 +[NSException raise:format:arguments:] + 88
    3   Foundation                0x00007fff32787e9d -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
    4   Foundation                0x00007fff326c45ee +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 440
    5   AppKit                    0x00007fff2d25165c -[NSApplication run] + 864
    6   focus_detection           0x0000000104b1a010 _ZN13FocusDetector8AppFocus3runEv + 128
    7   focus_detection           0x0000000104b19547 _ZNSt3__1L8__invokeIMN13FocusDetector8AppFocusEFvvEPS2_JEvEEDTcldsdeclsr3std3__1E7forwardIT0_Efp0_Efp_spclsr3std3__1E7forwardIT1_Efp1_EEEOT_OS6_DpOS7_ + 119
    8   focus_detection           0x0000000104b1944e _ZNSt3__1L16__thread_executeINS_10unique_ptrINS_15__thread_structENS_14default_deleteIS2_EEEEMN13FocusDetector8AppFocusEFvvEJPS7_EJLm2EEEEvRNS_5tupleIJT_T0_DpT1_EEENS_15__tuple_indicesIJXspT2_EEEE + 62
    9   focus_detection           0x0000000104b18c66 _ZNSt3__114__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEMN13FocusDetector8AppFocusEFvvEPS8_EEEEEPvSD_ + 118
    10  libsystem_pthread.dylib   0x00007fff6a260109 _pthread_start + 148
    11  libsystem_pthread.dylib   0x00007fff6a25bb8b thread_start + 15
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

这显然与 相关NSApplication,文档指出:

每个应用程序都使用一个 NSApplication 实例来控制主事件循环

因此,我正在寻找另一种监听事件的方法,它不限于主事件循环(或主线程。

直观地说,应该可以在一个单独的线程中获得有关当前应用程序焦点的信息。

我不知道如何解决这个问题,抱歉没有提供太多研究。我确实在互联网上研究了“NSNotification不在主线程中”和其他类似的句子,但没有成功。

问题:

如何activeAppDidChange在主线程外监听 NSNotification?

标签: objective-cmultithreadingmacoseventsnsworkspace

解决方案


将观察者放置在下面的以下通知中,并让他们调用您的方法。NotificationCenter 是单身有充分的理由,而您当然可以创建自己的。[NSWorkspace sharedWorkspace]我猜是一个单身人士,[[NSWorkspace sharedWorkspace] notificationCenter]正如其名——要观察的通知中心。期望的发送对象是nil您的情况,因为您不知道要更具体地观察哪个对象。

#import <AppKit/AppKit.h>
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    [[[NSWorkspace sharedWorkspace] notificationCenter]
        addObserver:self
        selector:@selector(activeAppDidChange:)
        name:NSWorkspaceDidActivateApplicationNotification
        object:nil
    ];
    [[[NSWorkspace sharedWorkspace] notificationCenter]
        addObserver:self
        selector:@selector(activeAppDidTerminate:)
        name:NSWorkspaceDidTerminateApplicationNotification
        object:nil
    ];
    // id<NSObject> myObserver;
    _myObserver = [[[NSWorkspace sharedWorkspace] notificationCenter] 
        addObserverForName:NSWorkspaceDidHideApplicationNotification
        object:nil 
        queue:[NSOperationQueue mainQueue] 
        usingBlock:^(NSNotification * _Nonnull note) {
            // do stuff in block
            NSRunningApplication *app = note.userInfo[NSWorkspaceApplicationKey];
            NSLog(@"%u %@ %ld", 
               app.processIdentifier, 
               app.bundleIdentifier, 
               (long)app.activationPolicy
            );
        }
    ];
}
-(void)activeAppDidChange:(NSNotification *)note {
    NSLog(@"%@",note.userInfo.debugDescription);
}
-(void)activeAppDidTerminate:(NSNotification *)note {
    NSLog(@"%@",note.userInfo.debugDescription);
}

推荐阅读