CFNotificationCenter 中的 NSWorkspace 通知

Posted

技术标签:

【中文标题】CFNotificationCenter 中的 NSWorkspace 通知【英文标题】:NSWorkspace Notifications in CFNotificationCenter 【发布时间】:2012-01-31 13:51:26 【问题描述】:

我们正在开发一个 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,所以我们得到一个空指针或坏内存。这是如何或为什么会发生的?

【问题讨论】:

我没有时间为您输入解决方案(但如果我回来时没有其他人回答,那么我会的)。据我所知,没有与 NSWorkspace 等效的 Core Foundation,但我的解决方案将涉及编写一些可以从 Qt cpp 类调用的 Objective C 胶水代码。 谢谢,非常感谢您的帮助。我们的示例程序运行良好,但在 Qt 中,我们无法找到使用 CF 注册特定事件的方法。我们之前使用 NSWorkspace 的尝试也没有奏效。 【参考方案1】:

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

我认为没有严格的 CF 等效于那些 NSWorkspace 调用。可能存在基于碳的高级等价物,但它们在 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 是免费桥接的,因此您可以简单地将appInfo NSDictionary 转换为CFDictionaryRef,然后调用CF 函数以获取内容如果您更喜欢 C 而不是 Objective-C,则可以使用字典。

请注意,通知中心拥有appInfo 字典(换句话说,它将被自动释放),因此您不应像使用CFCreate*/CFCopy* 相关代码那样调用CFRelease() .

【讨论】:

感谢您的回复!我们已经尝试过了,但它没有用。我们不能在 .h 中包含任何与 Objective-C 相关的内容,因为 Qt 会抱怨它。因此,我们用前向声明声明了一个私有成员,并在 .mm 中声明了该类。代码在第一篇文章中添加。

以上是关于CFNotificationCenter 中的 NSWorkspace 通知的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Haskell 中的递归习语是“'n+1' and 'n'”而不是“'n' and 'n-1'”?

varchar(N)和char(N)中的N解析

C中的N叉树

加法减法乘法及幂运算中的取余公式

解读源码中的问题

如何从 bigquery 中的句子中删除 \n����