Apple XPC框架
Apple XPC框架

Apple XPC框架

XPC 是 Apple 提供的现代化“进程级函数调用机制”,用于在不同进程之间像调用本地方法一样交换数据和执行任务。

XPC 可以将后台任务从主应用中分离出来,运行在独立的进程中,提高系统的整体稳定性。

使用场景

例如,一个普通的图片压缩 App,App 的任务:显示UI、读取、解码、压缩以及写入文件。

在运行App的过程中,可能会存在主线程任务导致UI卡顿甚至崩溃的风险。

XPC将 App和任务分离,App主要用于显示UI进程,App.xpc 独立运行后台服务进程中,实现读取、解码、压缩以及写入文件等操作。

App 不再是调用函数,而是请求另一个进程帮忙执行函数。

XPC = 进程级RPC(远程函数调用),由 Mach 消息、自动序列化、生命周期托管等技术组成。

因为XPC将UI进程和后台任务进行分离,当调用函数发生崩溃时,UI不会受到影响,两者不同地址空间,权限隔离,内存安全。当发生问题时,XPC只会杀死helper,不会杀死UI。

实现步骤

1、创建XPC Service

在Xcode菜单栏中找到File → New → Target → XPC Service。

2、定义通信协议

创建一个共享的协议文件,定义服务接口:

// XPCServiceProtocol.h
#import <Foundation/Foundation.h>

@protocol XPCServiceProtocol

- (void)performCalculation:(NSInteger)value 
                withReply:(void (^)(NSInteger result))reply;

- (void)processData:(NSString *)data 
         completion:(void (^)(NSString *result, NSError *error))completion;

@end

3、实现 XPC Service

// XPCServiceDelegate.m
#import "XPCServiceDelegate.h"
#import "XPCServiceProtocol.h"

@interface XPCServiceDelegate () <NSXPCListenerDelegate, XPCServiceProtocol>
@end

@implementation XPCServiceDelegate

- (BOOL)listener:(NSXPCListener *)listener 
shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
    
    // 设置导出的接口
    newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCServiceProtocol)];
    newConnection.exportedObject = self;
    
    // 处理连接中断
    newConnection.interruptionHandler = ^{
        NSLog(@"Connection interrupted");
    };
    
    newConnection.invalidationHandler = ^{
        NSLog(@"Connection invalidated");
    };
    
    [newConnection resume];
    return YES;
}

#pragma mark - XPCServiceProtocol

- (void)performCalculation:(NSInteger)value 
                withReply:(void (^)(NSInteger))reply {
    // 执行计算
    NSInteger result = value * 2;
    reply(result);
}

- (void)processData:(NSString *)data 
         completion:(void (^)(NSString *, NSError *))completion {
    // 处理数据
    NSString *processed = [data uppercaseString];
    completion(processed, nil);
}

@end

4、主 App 中使用 XPC Service

// ViewController.m
#import "ViewController.h"
#import "XPCServiceProtocol.h"

@interface ViewController ()
@property (strong) NSXPCConnection *connection;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupXPCConnection];
}

- (void)setupXPCConnection {
    // 创建连接
    self.connection = [[NSXPCConnection alloc] initWithServiceName:@"com.yourcompany.XPCService"];
    
    // 设置远程对象接口
    self.connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCServiceProtocol)];
    
    // 处理连接中断
    self.connection.interruptionHandler = ^{
        NSLog(@"Connection interrupted");
    };
    
    self.connection.invalidationHandler = ^{
        NSLog(@"Connection invalidated");
    };
    
    [self.connection resume];
}

- (void)callXPCService {
    // 获取远程对象代理
    id<XPCServiceProtocol> service = [self.connection remoteObjectProxyWithErrorHandler:^(NSError *error) {
        NSLog(@"Remote proxy error: %@", error);
    }];
    
    // 调用服务方法
    [service performCalculation:42 withReply:^(NSInteger result) {
        NSLog(@"Result: %ld", (long)result);
    }];
    
    [service processData:@"hello world" completion:^(NSString *result, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"Processed: %@", result);
        }
    }];
}

- (void)dealloc {
    [self.connection invalidate];
}

@end

5、Swift实现示例

// XPCServiceProtocol.swift
import Foundation

@objc protocol XPCServiceProtocol {
    func performCalculation(_ value: Int, withReply reply: @escaping (Int) -> Void)
    func processData(_ data: String, completion: @escaping (String?, Error?) -> Void)
}

// 主应用中使用
class ViewController: NSViewController {
    var connection: NSXPCConnection?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupXPCConnection()
    }
    
    func setupXPCConnection() {
        connection = NSXPCConnection(serviceName: "com.yourcompany.XPCService")
        connection?.remoteObjectInterface = NSXPCInterface(with: XPCServiceProtocol.self)
        
        connection?.interruptionHandler = {
            print("Connection interrupted")
        }
        
        connection?.invalidationHandler = {
            print("Connection invalidated")
        }
        
        connection?.resume()
    }
    
    func callXPCService() {
        guard let service = connection?.remoteObjectProxyWithErrorHandler({ error in
            print("Remote proxy error: \(error)")
        }) as? XPCServiceProtocol else {
            return
        }
        
        service.performCalculation(42) { result in
            print("Result: \(result)")
        }
    }
    
    deinit {
        connection?.invalidate()
    }
}

XPC Service的Info.plist需要配置:

<key>XPCService</key>
<dict>
    <key>ServiceType</key>
    <string>Application</string>
</dict>

常见的 ServiceType:

1、Application:应用级服务。

2、System:系统级服务(需要特殊权限)。

主要特点

1、安全性:XPC 采用沙盒机制,服务运行在独立的进程中,即使服务崩溃也不会影响主应用程序。这种隔离性提高了系统的整体稳定性和安全性。

2、权限分离:可以将需要特殊权限的操作放在独立的 XPC 服务中,主应用只需要最小权限,符合最小权限原则。

3、异步通信:XPC 使用基于块(block)的异步 API,避免阻塞主线程。

4、对象序列化:支持传输多种数据类型,包括字典、数组、字符串、数据、文件描述符等。

注意事项

1、始终使用 remoteObjectProxyWithErrorHandler: 而不是 remoteObjectProxy。

2、及时调用 invalidate 释放连接。

3、XPC 回调可能在非主线程,注意 UI 更新要回到主线程。

4、传输自定义对象需要实现 NSSecureCoding。

5、保持对 NSXPCConnection 的强引用,避免过早释放。

总结

当需要调用C/C++等不稳定库、需要执行大量CPU任务、希望UI不会被阻塞,可以考虑使用XPC框架。

把高风险、高耗时、有权限要求的代码从 UI 进程中剥离,形成独立可控的系统托管服务进程。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注