Delphi调用未内置OS X API及NSNetServiceDelegate实现方案咨询
Delphi访问未内置Mac API(以NSNetServiceDelegate为例)的实现方案
第一步:手动声明缺失的Objective-C协议接口
Delphi中OC协议对应带唯一GUID的接口,需继承自NSObjectProtocol,方法签名严格匹配原生OC定义,调用约定指定为cdecl:
uses Macapi.ObjectiveC, Macapi.Foundation; type // 先声明NSNetService前置类型,避免编译错误 NSNetService = interface; NSNetServiceDelegate = interface(NSObjectProtocol) ['{这里替换为你用Ctrl+Shift+G生成的唯一GUID}'] // 服务解析成功回调,对应OC - (void)netServiceDidResolveAddress:(NSNetService *)sender procedure netServiceDidResolveAddress(sender: NSNetService); cdecl; // 服务解析失败回调,对应OC - (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary<NSString *, NSNumber *> *)errorDict procedure netServiceDidNotResolveError(sender: NSNetService; errorDict: NSDictionary); cdecl; // 其他你需要用到的代理方法,全部按照OC头文件的签名依次声明 end; // 补全NSNetService缺失的delegate属性声明(如果Delphi原生单元没提供的话) NSNetServiceClass = interface(NSObjectClass) ['{自行生成对应GUID}'] end; NSNetService = interface(NSObject) ['{自行生成对应GUID}'] procedure setDelegate(delegate: Pointer); cdecl; function delegate: Pointer; cdecl; // 其他缺失的方法、属性按OC签名补全 end; TNSNetService = class(TOCGenericImport<NSNetServiceClass, NSNetService>) end;
第二步:实现代理类
继承Delphi提供的TOCLocal基类,实现刚才声明的NSNetServiceDelegate接口:
type TCustomNetServiceDelegate = class(TOCLocal, NSNetServiceDelegate) public procedure netServiceDidResolveAddress(sender: NSNetService); cdecl; procedure netServiceDidNotResolveError(sender: NSNetService; errorDict: NSDictionary); cdecl; end; procedure TCustomNetServiceDelegate.netServiceDidResolveAddress(sender: NSNetService); begin // 此处编写服务解析成功后的业务逻辑,可读取sender的hostName、addresses等属性获取设备信息 end; procedure TCustomNetServiceDelegate.netServiceDidNotResolveError(sender: NSNetService; errorDict: NSDictionary); begin // 此处编写服务解析失败的异常处理逻辑 end;
第三步:关联代理并调用API
注意必须持有代理类的实例,避免被自动释放导致访问异常:
type TMainForm = class(TForm) // 窗体控件声明 private FNetService: NSNetService; FServiceDelegate: TCustomNetServiceDelegate; public procedure StartBonjourSearch; end; procedure TMainForm.StartBonjourSearch; begin // 初始化代理并持有引用 FServiceDelegate := TCustomNetServiceDelegate.Create; // 初始化NSNetService实例,配置为搜索你指定类型的局域网服务 FNetService := TNSNetService.Alloc.initWithDomain('', '_你的服务类型._tcp', ''); FNetService.setDelegate(FServiceDelegate); FNetService.scheduleInRunLoop(NSRunLoop.currentRunLoop, NSDefaultRunLoopMode); FNetService.startDiscovery; end;
注意事项
- 所有OC方法的签名必须和Apple官方头文件完全一致,包括参数顺序、类型、返回值,调用约定必须加
cdecl,否则会触发访问越界、参数异常等问题 - 代理实例、NSNetService实例必须和使用场景的生命周期绑定,不能作为局部变量声明,否则被自动释放后回调会触发Access Violation
- MacOS开启沙箱的情况下,需要在entitlements配置中添加Bonjour相关权限,否则服务发现会无响应
- 其他未内置的Mac API都可以按照相同的手动绑定逻辑实现,核心是匹配OC的类型、方法签名和调用约定
内容的提问来源于stack exchange,提问作者user3820843
相关产品推荐
相关产品推荐

