添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Unity 中IOS平台 C#怎么和OC通信?

C#和Objective-C通信有多种方式,其中比较常见的方法是通过使用Mono框架中的Objective-C运行时库和C#的P/Invoke功能。

一、 C# 调用 OC的接口

1、首先介绍下 C#的P/Invoke功能

具体来说,我们可以通过在C#中使用DllImportAttribute特性和native方法签名来声明Objective-C中的函数。然后,我们可以在C#中调用这些native方法,从而实现与Objective-C的交互

首先在C# 中声明OC 中定义的接口,如下所示

public class SDKForIOS : MonoBehaviour
    [DllImport("__Internal")]
    private static extern void SDKInit(); // 此接口在mm文件中定义
    public  void Start()
        SDKInit();


DllImport 其功能是提供从非托管DLL导出的函数的必要调用信息,DllImport属性应用于方法,要求最少要提供包含入口点的dll的名称。要不然调用不成功哦!!!

然后看OC中怎么定义的?


在头文件 SDKIOS.h 中定义

//用于Unity的调用(Unity只能调用C的方法,调不到OC的方法)
#if defined(__cplusplus)
extern "C"
#endif
    extern void SDKInit();
#if defined(__cplusplus)
#endif


在源文件 SDKIOS.mm 中定义

void OnSDKInit()
    NSLog(@"hello world!");
}


extern "C"会指示编译器这部分代码按C语言的方式进行编译,因为il2cpp后的代码都是按C语言的方式编译的,所以我们这里也要这样定义,要不调用会失败哦!!!

按照上面的方式我们启动游戏后就能看到打印"hello world!" 了。


2、使用Mono框架中的Objective-C运行时库来动态地获取Objective-C对象的方法和属性


当使用Mono框架中的Objective-C运行时库时,可以使用 objc_getClass objc_msgSend 函数来获取Objective-C类和调用它们的方法。以下是一个简单的例子,演示如何使用这些函数从C#代码中访问Objective-C对象的方法和属性:

using System;
using System.Runtime.InteropServices;
public class Example
    [DllImport("__Internal")]
    private static extern IntPtr objc_getClass(string name);
    [DllImport("__Internal")]
    private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
    [DllImport("__Internal")]
    private static extern IntPtr objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);
    static void Main()
        // 获取NSString类
        IntPtr nsstringClass = objc_getClass("NSString");
        // 创建NSString对象
        IntPtr nsstring = objc_msgSend(nsstringClass, Selector.GetHandle("stringWithUTF8String:"), "Hello, Objective-C!");
        // 调用NSString对象的length方法
        IntPtr lengthSelector = Selector.GetHandle("length");
        int length = (int)objc_msgSend(nsstring, lengthSelector);
        Console.WriteLine("Length of the string: {0}", length);
        // 调用NSString对象的UTF8String属性
        IntPtr utf8StringSelector = Selector.GetHandle("UTF8String");
        IntPtr utf8String = objc_msgSend(nsstring, utf8StringSelector);
        string str = Marshal.PtrToStringAuto(utf8String);
        Console.WriteLine("UTF8 String: {0}", str);
// 定义Selector类,用于获取Objective-C方法的选择器
public static class Selector
    [DllImport("__Internal")]
    private static extern IntPtr sel_registerName(string name);
    public static IntPtr GetHandle(string name)
        return sel_registerName(name);
}


在上面的例子中,我们首先使用 objc_getClass 函数获取了Objective-C中的 NSString 类。然后使用 objc_msgSend 函数调用了 stringWithUTF8String: 方法,创建了一个 NSString 对象。接下来,使用 objc_msgSend 函数调用了 length 方法,获取了字符串的长度。最后,使用 objc_msgSend 函数调用了 UTF8String 属性,获取了字符串的UTF8编码。
需要注意的是,上面的例子只是演示了如何使用Objective-C运行时库来访问Objective-C对象的方法和属性,实际上还需要进行一些额外的操作才能将Objective-C对象和C#对象进行转换和交互。



二、OC怎么调用C#代码呢?

OC调用C#代码有 两种调用方式

第一种方式:

如果对Unity有些了解的同学肯定都知道一个接口,就是 UnitySendMessage ,然后传入组件的名字和方法的名字就可以调用了。

这种调用方式是线程安全的,但是可能会相对耗时一些,它的实现会遍历所有的transfrom然后找到相应的组件及方法进行调用。


第二种方式:

public class SDKIOS : MonoBehaviour
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void callbackN0Delegate(IntPtr data);
    [DllImport("__Internal")]
    public static extern void SetN0CallBack(IntPtr callback);
    [DllImport("__Internal")]
    private static  extern void OnSDKInit();
    // Start is called before the first frame update
    void Start()
        RegisterCallback();
        OnSDKInit();
    private void RegisterCallback()
        callbackN0Delegate callbackN0_delegate = SendDataToGame;
        //将Delegate转换为非托管的函数指针
        IntPtr intptrN0_delegate = Marshal.GetFunctionPointerForDelegate(callbackN0_delegate);
        //调用非托管函数
        SetN0CallBack(intptrN0_delegate);
    //原生层发送数据到游戏层
    [MonoPInvokeCallback(typeof(callbackN0Delegate))]
    static void SendDataToGame(IntPtr data)
        var dataStr = Marshal.PtrToStringAnsi(data);
        Debug.Log(dataStr);


如代码所示,一开始定义了委托函数 public delegate void callbackN0Delegate(IntPtr data); IntPtr类似于C++中的指针,用于和原生代码通信


[DllImport("__Internal")]

public static extern void SetN0CallBack(IntPtr callback);

这行代码代表原生层定义了SetN0CallBack 接口,用于回调SendDataToGame


RegisterCallback 函数目的是将委托函数SendDataToGame 通过SetN0CallBack 接口注册到原生层。

注意:[MonoPInvokeCallback(typeof(callbackN0Delegate))] 标签必须加,否则执行时会报错!!

接下来我们看下OC层是怎么定义的?

#if defined(__cplusplus)
extern "C"
#endif
    extern void OnSDKInit();
    typedef void(*CALLBACK_N0)(const char*);  //定义函数指针
    static CALLBACK_N0 callback_n0 = nil;     //定义函数指针对象
    void SetN0CallBack(CALLBACK_N0 callback);   
#if defined(__cplusplus)
#endif
void OnSDKInit()
    NSLog(@"OnSDKInit");
    if(callback_n0 != nil)
        callback_n0("call back ok!!!");  //这里将”call back ok!!!“字符串发给C#层打印