iOS与unity交互混编, 闭包方式实现互相调用

背景

unity需要调用其没有实现的原生功能。
unity使用C#开发,可以与c++混编。
iOS使用Objective-c开发,可以与C++和C混编。
例如:

  • 支付,分享,音视频,但只提供了 iOS 平台 SDK。
  • 优秀插件
  • 需要通过 Objective-C 调用 iOS 原生类库代码。

交互方式

Unity 通过 C 与 iOS 进行交互。
Unity 不能直接调用 C++ 的原因是 C++ 编译会有 Name mangling 问题。
Objective-C 可以与 C/C++ 进行混编。使用 C 代码封装对应的 Objective-C 代码,提供给 Unity 使用。

unity主动调用链:unity(C#) -> C -> iOS(Objective-C)
iOS调用链: unity(C#) -> 注册回调到iOS -> iOS(Objective-C) -> 通过注册的回调,调用C -> 调用unity(C#)

注意:建议 C 函数名加有特定意义的前缀,避免函数名冲突。

unity交互部分

核心文件上代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;  // 必须导入
using System

public class MonoPInvokeCallbackAttribute : Attribute   // 用于定义C语音回调的类
{
    public MonoPInvokeCallbackAttribute(Type type)
    {

    }
}

public class BridgeUnityToNative   // 定义了一个类,用以做桥,可自定义
{
    protected static BridgeUnityToNative _manager = null;
    public static BridgeUnityToNative Instance
    {
        get
        {
            if(_manager == null)
            {
                _manager = new BridgeUnityToNative();
            }
            return _manager;
        }
    }

    public delegate void CallBack(int res);  // 定义回调,如果定义的方法需要有回调,则必须先定义delegate,才能核心交互代码中使用
    
    // 核心交互代码
    #if UNITY_IOS
    [DllImport("__Internal")]  // 固定形式,注册TestCallback方法为可调用的C函数
    private static extern void TestCallback();  // 封装必须为private, extern需要定义的方法,不然会报错


    [DllImport("__Internal")]
    private static extern void RegistCallback(CallBack callBack);  // 封装必须为private, extern需要定义的方法,不然会报错

    [AOT.MonoPInvokeCallback(typeof(CallBack))]  // 将LogCallback方法,注册为回调
    static void LogCallback(int res)
    {
        Debug.Log("调用了c# callback" + res);
    }

    public void TestCallbackFunc(int retCode)  // 对外接口方法,,外部文件调用此方法验证demo
    {
        Debug.Log("调用了TestCallbackFunc" + retCode);
        TestCallback();
        RegistCallback(BridgeUnityToNative.LogCallback);
    }
    #endif
}

上述代码,一共定义了两个交互函数。
无回调方法TestCallback 和 有回调方法RegistCallback
由于作用域的问题,两个方法都只能private。所以对外接口需要用c#包一层

[AOT.MonoPInvokeCallback(typeof(CallBack))] 的下一个函数,可以用于回调代理。其函数类类型必须与代理完全一致,否则会报错
这里函数名和回调类型,必须要跟iOS(Objective-C)的类型完全一致。不然会报错,无法调用整个函数。

注意事项:TestCallback和'RegistCallback'方法。只在此处extern了方法名,所以不会有任何检查机制,C#或iOS改了方法,对方完全察觉不到,验证也很费劲。所以精心维护一份交互文档是很重要的。

iOS交互部分

因为要混C++ , 所有.m文件的后缀要改成.mm
.h文件:

//
//  IOSToUnity.h
//  Unity-iPhone
//
//  Created by yangda on 2019/1/12.
//

#import 
#import 

#ifndef ConnectUnityToiOS_h
#define ConnectUnityToiOS_h
#endif /* ConnectUnityToiOS_h */

@interface ConnectUnityToiOS : NSObject

typedef void(*CallBack)(int retCode);

+ (ConnectUnityToiOS *)sharedInstance;

extern "C"
{
    void TestCallback();
    
    void RegistCallback(CallBack callBack);
}

@end

其中:typedef void(*CallBack)(int retCode);对应C#代码中的代理public delegate void CallBack(int res);定义
核心代码为:

extern "C"
{
    void TestCallback();
    
    void RegistCallback(CallBack callBack);
}

extern "C"可以扩展C函数,用于交互

.m文件:

//
//  IOSToUnity.m
//  Unity-iPhone
//
//  Created by yangda on 2019/1/12.
//

#import "ConnectUnityToiOS.h"

@interface ConnectUnityToiOS()

@property(atomic, assign)CallBack callBack;

@end


@implementation ConnectUnityToiOS
// MARK:- OC部分
static ConnectUnityToiOS *instance = nil;
+ (ConnectUnityToiOS *)sharedInstance{
    @synchronized(self) {
        if(instance == nil) {
            instance = [[[self class] alloc] init];
        }
    }
    return instance;
}

- (void)registTestCallBack:(CallBack)callBack {
    _callBack = callBack;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        _callBack(1);
        NSLog(@"iOS registTestCallBack");
    });
}

// MARK:- C++部分
void TestCallback()
{
    NSLog(@"TestCallback");
}

void RegistCallback(CallBack callBack)
{
    NSLog(@"iOS RegistCallback");
    [ConnectUnityToiOS.sharedInstance registTestCallBack:callBack];
}


@end

如果有错误的地方欢迎指正

你可能感兴趣的:(iOS与unity交互混编, 闭包方式实现互相调用)