【问题标题】:How to send a message from XPC helper app to main application?如何从 XPC 助手应用程序向主应用程序发送消息?
【发布时间】:2014-03-04 14:49:49
【问题描述】:

我成功地创建了 XPC 服务并通过从主应用程序发送消息与 XPC 服务进行通信。但我想知道的是,是否可以启动从 XPC 服务到主应用程序的通信。 Apple documentation 表示 XPC 是双向的。如果有人可以通过示例为我指明正确的方向,将不胜感激。

请注意,

  • 我想从主应用程序启动 XPC。
  • 从主应用程序与 XPC 通信。
  • 当某些事件发生时,XPC 应该向主应用程序发送消息。

我在前两个中成功了,但在第三个中找不到任何资源。

谢谢。 :)

【问题讨论】:

  • 你在回调主程序方面有什么尝试?您是否尝试在主程序中使用exportedMethodsexportedObjects
  • 是的,我正在试验来自 XPC 服务的 remoteObjectInterface 和来自主应用程序的 exportedObjects。如果你能提供一个例子,我会很高兴。
  • 向我们展示您的尝试,我们会尽力帮助您解决问题。

标签: macos cocoa xpc nsxpcconnection


【解决方案1】:

想通了一切。下面应该是一个不错的例子:

ProcessorListener.h(包含在客户端和服务器中):

@protocol Processor

- (void) doProcessing: (void (^)(NSString *response))reply;

@end

@protocol Progress

- (void) updateProgress: (double) currentProgress;
- (void) finished;

@end

@interface ProcessorListener : NSObject<NSXPCListenerDelegate, Processor>

@property (weak) NSXPCConnection *xpcConnection;

@end

ProcessorListener.m:(仅包含在服务器中)

#import "ProcessorListener.h"

@implementation ProcessorListener

- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
    [newConnection setExportedInterface: [NSXPCInterface interfaceWithProtocol:@protocol(Processor)]];
    [newConnection setExportedObject: self];
    self.xpcConnection = newConnection;

    newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: @protocol(Progress)];

    // connections start suspended by default, so resume and start receiving them
    [newConnection resume];

    return YES;
}

- (void) doProcessing: (void (^)(NSString *g))reply
{
    dispatch_async(dispatch_get_global_queue(0,0), ^{
      for(int index = 0; index < 60; ++index)
      {
         [NSThread sleepWithTimeInterval: 1];
         [[self.xpcConnection remoteObjectProxy] updateProgress: (double)index / (double)60 * 100];
      }

      [[self.xpcConnection remoteObjectProxy] finished];
    }

    // nil is a valid return value.
    reply(@"This is a reply!");
}

@end

MainApplication.m(您的主应用程序):

#import "ProcessorListener.h"

- (void) executeRemoteProcess
{
    // Create our connection
    NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: @protocol(Processor)];

    NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: kServiceName];

    [connection setRemoteObjectInterface: myCookieInterface];

    connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(Progress)];
    connection.exportedObject = self;

    [connection resume];

    id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err)
                       {
                           NSAlert *alert = [[NSAlert alloc] init];
                           [alert addButtonWithTitle: @"OK"];
                           [alert setMessageText: err.localizedDescription];
                           [alert setAlertStyle: NSWarningAlertStyle];

                           [alert performSelectorOnMainThread: @selector(runModal) withObject: nil waitUntilDone: YES];
                       }];

    [theProcessor doProcessing: ^(NSString * response)
     {
        NSLog(@"Received response: %@", response);
     }];
}

#pragma mark -
#pragma mark Progress

- (void) updateProgress: (double) currentProgress
{
    NSLog(@"In progress: %f", currentProgress);
}

- (void) finished
{
    NSLog(@"Has finished!");
}

@end

请注意,此代码是基于我的工作代码的精简版本。它可能无法 100% 编译,但显示了使用的关键概念。在示例中,doProcessing 异步运行以表明在 Progress 协议中定义的回调即使在初始方法返回并且 Received response: This is a reply! 已被记录后仍会执行。

【讨论】:

  • 如果处理器有多个连接怎么办?我如何知道哪个连接调用了doProcessing,以便向它报告进度?
  • @pointum 你可以将另一个参数传递给doProcessing
  • 这是一个很好的答案,但是在定义不同的部分和去哪里的时候,为什么不谈论应用程序和服务而不是客户端和服务器呢?
  • 秘诀在于“服务器”必须在特定上下文中运行。我的意思是,如果你从 gdb 或命令行运行服务器,这是行不通的。
猜你喜欢
  • 2023-03-22
  • 2015-10-13
  • 2015-01-08
  • 1970-01-01
  • 2015-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多