You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
        }
    }
}

错误原因分析

  1. iOS编译错误:通常是直接将普通Swift闭包暴露给C层,未添加@convention(c)标记,导致类型不兼容,编译器无法将Swift闭包转换为C函数指针。
  2. 运行时崩溃:几乎都是因为C#中的委托被GC回收,原生层回调时访问了无效的函数指针。解决核心是将委托赋值给类成员变量,让GC无法回收它。

内容的提问来源于stack exchange,提问作者Matrix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 07:48:43