CFNotificationCenter 中的 NSWorkspace 通知

发布于 2024-12-29 22:45:00 字数 3274 浏览 4 评论 0原文

我们正在开发一个 Qt 项目,需要添加一些 Mac 特定的代码。我们需要注册一个事件,在一个示例程序中,我们通过使用以下方法来做到这

[[NSNotificationCenter defaultCenter] addObserver:self
                                   selector:@selector(notificationHandler:) 
                                   name:NSWorkspaceDidDeactivateApplicationNotification
                                   object:nil];

一点: 由于我们可以直接在 Qt 上的 mm 文件中使用它,因此我们采取的方法如下:

MyClass::MyClass() : {
    // do other setup ...

    CFNotificationCenterAddObserver
    (
        CFNotificationCenterGetLocalCenter(),
        this,
        &notificationHandler,
        CFSTR("???"),
        NULL,
        CFNotificationSuspensionBehaviorDeliverImmediately
    );
}

“NSWorkspaceDidDeactivateApplicationNotification”的字符串是什么?或者我们如何将自己附加到这个特定的通知?

我们尝试了 NSGod 的方法,但由于无法在 Qt 的 .h 中添加 Objective-C 代码,因此我们添加了一个私有成员,其类在 mm 文件中定义,包含实际逻辑。像这样:

SelectedStuffManager.h

class MacWrap;

class SelectedStuffManager
{
  public:
   ....
    doSomething();

    MacWrap* d;

  private:
   ....
};

SelectedStuffManager.mm

@class MDWorkspaceWatcher;

class MacWrap
{
    public:
        MacWrap();
        ~MacWrap();

        void  applicationDeactivated(NSNotification * notification);

        SystemEventsApplication *systemApplication;
        NSRunningApplication *runApp;

        private:
           MDWorkspaceWatcher *workspaceWatcher;
};
MacWrap::MacWrap() {
      this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}

MacWrap::~MacWrap() {
      [this->workspaceWatcher release];
}

void  MacWrap::applicationDeactivated(NSNotification* notification)
{
    // guardar el id del proceso para utilizarlo luego
    runApp = [[notification userInfo] valueForKey:@"NSWorkspaceApplicationKey"];
    NSString *systemEventsASppName = [runApp bundleIdentifier];
    if( [ systemEventsASppName isNotEqualTo:@"com.yo.SelectedText"])
    {
        systemApplication = [SBApplication applicationWithBundleIdentifier:systemEventsASppName];
        NSLog(@"Launched. %@",systemEventsASppName);
    }

}

@interface MDWorkspaceWatcher : NSObject {

     MacWrap  *manager;
}

- (id)initWithMyClass:(MacWrap*)obj;
- (void)didDeactivateApp:(NSNotification *)notification; @end

@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MacWrap*)obj {
    if ((self = [super init])) {
       manager = obj;

       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didDeactivateApp:)
                name:NSWorkspaceDidDeactivateApplicationNotification
                object:nil];
    }
    return self;
}

- (void)dealloc {
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
    [super dealloc];
}

- (void)didDeactivateApp:(NSNotification *)notification {
   manager->applicationDeactivated(notification);
}
@end

SelectedStuffManager::SelectedStuffManager()
{
    d = new MacWrap();
}
SelectedStuffManager::doSomething()
{
    if ([[d->runApp localizedName] isEqualTo: @"something"]) --> here it fails, bad memory access
    {
       ...
    }
}

似乎有人正在释放 runApp 和 systemApplication,所以我们得到一个空指针或坏内存。怎么或者为什么会发生这种情况?

We are working on a Qt project, and there is some Mac specific code that we need to add. We need to register for an event, in a sample program we did that by using:

[[NSNotificationCenter defaultCenter] addObserver:self
                                   selector:@selector(notificationHandler:) 
                                   name:NSWorkspaceDidDeactivateApplicationNotification
                                   object:nil];

Since we can use that directly in our mm file on Qt, we are taking the approach of doing something like:

MyClass::MyClass() : {
    // do other setup ...

    CFNotificationCenterAddObserver
    (
        CFNotificationCenterGetLocalCenter(),
        this,
        ¬ificationHandler,
        CFSTR("???"),
        NULL,
        CFNotificationSuspensionBehaviorDeliverImmediately
    );
}

whats the string for "NSWorkspaceDidDeactivateApplicationNotification"?? Or how do we attatch ourselves to this particular notification?

We tried NSGod's approach, but since no Objective-C code can be added in a .h with Qt, then we added a private member which its class is defined in the mm file, that containes the actual logic. like this:

SelectedStuffManager.h

class MacWrap;

class SelectedStuffManager
{
  public:
   ....
    doSomething();

    MacWrap* d;

  private:
   ....
};

SelectedStuffManager.mm

@class MDWorkspaceWatcher;

class MacWrap
{
    public:
        MacWrap();
        ~MacWrap();

        void  applicationDeactivated(NSNotification * notification);

        SystemEventsApplication *systemApplication;
        NSRunningApplication *runApp;

        private:
           MDWorkspaceWatcher *workspaceWatcher;
};
MacWrap::MacWrap() {
      this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}

MacWrap::~MacWrap() {
      [this->workspaceWatcher release];
}

void  MacWrap::applicationDeactivated(NSNotification* notification)
{
    // guardar el id del proceso para utilizarlo luego
    runApp = [[notification userInfo] valueForKey:@"NSWorkspaceApplicationKey"];
    NSString *systemEventsASppName = [runApp bundleIdentifier];
    if( [ systemEventsASppName isNotEqualTo:@"com.yo.SelectedText"])
    {
        systemApplication = [SBApplication applicationWithBundleIdentifier:systemEventsASppName];
        NSLog(@"Launched. %@",systemEventsASppName);
    }

}

@interface MDWorkspaceWatcher : NSObject {

     MacWrap  *manager;
}

- (id)initWithMyClass:(MacWrap*)obj;
- (void)didDeactivateApp:(NSNotification *)notification; @end

@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MacWrap*)obj {
    if ((self = [super init])) {
       manager = obj;

       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didDeactivateApp:)
                name:NSWorkspaceDidDeactivateApplicationNotification
                object:nil];
    }
    return self;
}

- (void)dealloc {
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
    [super dealloc];
}

- (void)didDeactivateApp:(NSNotification *)notification {
   manager->applicationDeactivated(notification);
}
@end

SelectedStuffManager::SelectedStuffManager()
{
    d = new MacWrap();
}
SelectedStuffManager::doSomething()
{
    if ([[d->runApp localizedName] isEqualTo: @"something"]) --> here it fails, bad memory access
    {
       ...
    }
}

It seems like someone is freeing both runApp and systemApplication, so we get a null pointer or bad memory. How or why could this be happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

面如桃花 2025-01-05 22:45:00

我不相信你能像你希望的那样做。首先,NSWorkspace使用自己的NSNotificationCenter,它与+defaultCenter返回的默认NSNotificationCenter不同。

我不相信这些 NSWorkspace 调用有严格的 CF 等效项。可能存在基于 Carbon 的高级等效项,但它们在 64 位中不可用,因此应该避免使用。

您应该能够使用小型 Objective-C 帮助程序类来接收通知并将其转发到您的 C++ 类,如以下代码所示:

编辑:更新以从头文件中删除任何 Objective-C。只需使用通用的 void * 指针,您可以在 .mm 文件中对其进行转换。

.h:

//@class MDWorkspaceWatcher;

class MyClass {
   private:
      // MDWorkspaceWatcher *workspaceWatcher;
      void *workspaceWatcher;
   public:
       MyClass();
       ~MyClass();

       // const void didActivateApp(NSNotification *notification) const;
       // const void didDeactivateApp(NSNotification *notification) const;
       const void didActivateApp(void *anNSnotification) const;
       const void didDeactivateApp(void *anNSnotification) const;

};

.mm:

Objective-C 部分:

@interface MDWorkspaceWatcher : NSObject {
    MyClass    *myClass;
}
- (id)initWithMyClass:(MyClass *)aMyClass;
@end

@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MyClass *)aMyClass {
    if ((self = [super init])) {
       myClass = aMyClass;
       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didActivateApp:)
                name:NSWorkspaceDidActivateApplicationNotification
                object:nil];
       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didDeactivateApp:)
                name:NSWorkspaceDidDeactivateApplicationNotification
                object:nil];
    }
    return self;
}
// very important:
- (void)dealloc {
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
    [super dealloc];
}
- (void)didActivateApp:(NSNotification *)notification {
   myClass->didActivateApp(notification);
}
- (void)didDeactivateApp:(NSNotification *)notification {
   myClass->didDeactivateApp(notification);
}
@end

C++ 部分:

MyClass::MyClass() {
      this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}

MyClass::~MyClass() {
      [(MDWorkspaceWatcher *)this->workspaceWatcher release];
}

MyClass::didActivateApp(void *anNSnotification) {
     NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];          
     NSLog(@"appInfo == %@", appInfo);

}

MyClass::didDeactivateApp(void *anNSnotification) {
     NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];          
     NSLog(@"appInfo == %@", appInfo);

}

请注意,NSDictionaryCFDictionaryRef 是免费桥接的,因此您只需转换 如果您更喜欢 C 而不是 Objective,则将 appInfo NSDictionary 转换为 CFDictionaryRef,然后调用 CF 函数来获取字典的内容-C。

请注意,通知中心拥有 appInfo 字典(换句话说,它将自动释放),因此您不应该像使用 < code>CFCreate*/CFCopy* 相关代码。

I don't believe you can just do like you're hoping to. First of all, NSWorkspace uses its own NSNotificationCenter, which is different than the default NSNotificationCenter returned with +defaultCenter.

I don't believe there's a strict CF-equivalent to those NSWorkspace calls. There are likely high-level Carbon-based equivalents, but they're not available in 64-bit, and should likely be avoided.

You should be able to accomplish what you want using a small Objective-C helper class to receive the notifications and forward them to your C++ class like in the following code:

EDIT: updated to remove any Objective-C from header files. Just use generic void * pointers, which you can cast inside the .mm file.

.h:

//@class MDWorkspaceWatcher;

class MyClass {
   private:
      // MDWorkspaceWatcher *workspaceWatcher;
      void *workspaceWatcher;
   public:
       MyClass();
       ~MyClass();

       // const void didActivateApp(NSNotification *notification) const;
       // const void didDeactivateApp(NSNotification *notification) const;
       const void didActivateApp(void *anNSnotification) const;
       const void didDeactivateApp(void *anNSnotification) const;

};

.mm:

Objective-C part:

@interface MDWorkspaceWatcher : NSObject {
    MyClass    *myClass;
}
- (id)initWithMyClass:(MyClass *)aMyClass;
@end

@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MyClass *)aMyClass {
    if ((self = [super init])) {
       myClass = aMyClass;
       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didActivateApp:)
                name:NSWorkspaceDidActivateApplicationNotification
                object:nil];
       [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
                selector:@selector(didDeactivateApp:)
                name:NSWorkspaceDidDeactivateApplicationNotification
                object:nil];
    }
    return self;
}
// very important:
- (void)dealloc {
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
    [super dealloc];
}
- (void)didActivateApp:(NSNotification *)notification {
   myClass->didActivateApp(notification);
}
- (void)didDeactivateApp:(NSNotification *)notification {
   myClass->didDeactivateApp(notification);
}
@end

C++ part:

MyClass::MyClass() {
      this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}

MyClass::~MyClass() {
      [(MDWorkspaceWatcher *)this->workspaceWatcher release];
}

MyClass::didActivateApp(void *anNSnotification) {
     NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];          
     NSLog(@"appInfo == %@", appInfo);

}

MyClass::didDeactivateApp(void *anNSnotification) {
     NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];          
     NSLog(@"appInfo == %@", appInfo);

}

Note that an NSDictionary is toll-free-bridged with CFDictionaryRef, so you can simply cast the appInfo NSDictionary to a CFDictionaryRef and then call the CF functions to get at the contents of the dictionary if you prefer C over Objective-C.

Note that the notification center owns the appInfo dictionary (in other words, it will be autoreleased), so you shouldn't call CFRelease() on it like you might with CFCreate*/CFCopy*-related code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文