CFNotificationCenter中的NSWorkspace通知

时间:2012-01-31 13:51:26

标签: objective-c macos qt core-foundation nsworkspace

我们正在开发一个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的方法,但由于在.h中没有使用Qt添加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,因此我们得到一个空指针或坏内存。怎么或为什么会发生这种情况?

1 个答案:

答案 0 :(得分:0)

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

我不相信那些NSWorkspace次呼叫存在严格的CF等价物。可能存在高级别的基于碳的等价物,但它们不是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,然后再拨打电话如果您更喜欢C而不是Objective-C,CF函数可以获取字典的内容。

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