Unity iOS插件开发:Swift闭包转C#回调参数问题及报错求助
Unity调用Swift带CompletionHandler的初始化方法解决方案
核心思路
Swift闭包无法直接被C#的P/Invoke识别,必须通过Objective-C桥接层+C风格函数指针完成跨语言回调映射,具体步骤如下:
1. 封装Swift类并适配C风格回调
先编写你的Swift业务类,将completionHandler转换为C兼容的函数指针类型:
@objc public class YourSwiftModule: NSObject { private var completionCallback: ((Bool) -> Void)? @objc public static func setupWithCallback(callback: @convention(c) @escaping (Bool) -> Void) -> YourSwiftModule { let instance = YourSwiftModule() // 将C风格回调包装为Swift闭包 instance.completionCallback = { result in callback(result) } // 模拟初始化逻辑,异步操作后触发回调 DispatchQueue.global().async { // 这里替换成你的实际初始化代码 Thread.sleep(forTimeInterval: 1) DispatchQueue.main.async { instance.completionCallback?(true) } } return instance } @objc public func keepInstance() { // 空实现,用于ObjC层持有实例引用,避免提前释放 } }
关键:@convention(c) 标记会把Swift闭包转换成C语言兼容的函数指针,是跨语言回调的核心。
2. 编写Objective-C桥接头文件
创建UnitySwiftBridge.h文件,定义供C#调用的C风格接口:
#ifndef UnitySwiftBridge_h #define UnitySwiftBridge_h #import <Foundation/Foundation.h> // 定义和Swift回调匹配的C函数指针类型 typedef void (*InitCompletion)(BOOL success); // 暴露给C层的初始化函数 void* YourSwiftModule_Init(InitCompletion callback); // 用于保持Swift实例引用,防止被提前释放 void YourSwiftModule_Hold(void* instance); #endif /* UnitySwiftBridge_h */
3. 实现Objective-C桥接逻辑
创建UnitySwiftBridge.m文件,完成C接口到Swift类的中转:
#import "UnitySwiftBridge.h" #import "YourProjectName-Swift.h" // 替换为你的项目自动生成的Swift桥接文件名 void* YourSwiftModule_Init(InitCompletion callback) { YourSwiftModule* module = [YourSwiftModule setupWithCallback:callback]; // 将Swift实例转为void*,方便C#层持有 return (__bridge_retained void*)module; } void YourSwiftModule_Hold(void* instance) { YourSwiftModule* swiftModule = (__bridge YourSwiftModule*)instance; [swiftModule keepInstance]; }
注意:YourProjectName-Swift.h是Xcode自动生成的文件,需确保项目Build Settings中Defines Module选项设为YES。
4. Unity C#端调用实现
在C#中定义匹配的委托,通过P/Invoke调用原生接口,必须持有委托引用防止GC回收:
using System; using System.Runtime.InteropServices; using UnityEngine; public class SwiftBridge : MonoBehaviour { // 定义与C层匹配的委托类型 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InitCompletion(bool success); [DllImport("__Internal")] private static extern IntPtr YourSwiftModule_Init(InitCompletion callback); [DllImport("__Internal")] private static extern void YourSwiftModule_Hold(IntPtr instance); // 持有委托和实例引用,避免被GC回收 private InitCompletion _initCallback; private IntPtr _swiftInstance; void Start() { // 初始化委托并绑定回调方法 _initCallback = OnInitCompleted; _swiftInstance = YourSwiftModule_Init(_initCallback); // 保留Swift实例,防止提前释放 YourSwiftModule_Hold(_swiftInstance); } private void OnInitCompleted(bool success) { Debug.Log($"Swift模块初始化结果:{success}"); } void OnDestroy() { // 释放持有的实例引用 if (_swiftInstance != IntPtr.Zero) { Marshal.Release(_swiftInstance); _swiftInstance = IntPtr.Zero; } } }
错误原因分析
- iOS编译错误:通常是直接将普通Swift闭包暴露给C层,未添加
@convention(c)标记,导致类型不兼容,编译器无法将Swift闭包转换为C函数指针。 - 运行时崩溃:几乎都是因为C#中的委托被GC回收,原生层回调时访问了无效的函数指针。解决核心是将委托赋值给类成员变量,让GC无法回收它。
内容的提问来源于stack exchange,提问作者Matrix
相关产品推荐
相关产品推荐

