一、背景
由于定义了unity和原生之间交互的桥,因此在导出unity项目后,需要将桥源码加到unityFramework里面参与编译,编译出动态库。
二、问题及排查历程
符号未定义
在unity那个demo里面进行桥使用:
发现符号未定义的问题:
然后发现为什么库里面其他的符号可以被使用:
符号未定义原因
细看不同之处:
原来加了这个显示,符号默认对外界隐藏。查看动态库设置参数,果然如此:
于是乎我也在自定义的类加上这个参数不就解决了?(哈哈哈,只能说运气没有站在我这边,啪啪打脸):
在自定义的类声明前面,也加上了这个标识,再次编译,wtf还是报符号未定义的错误。然后开始反思为什么会出现这样的效果,明明是一样的。我甚至尝试把这个类定义到UnityFramework文件里面,以避免库单独对这个文件做了什么处理。结果还是不行(依然报错)。然后再次问:明明是一样的,为什么还会报这个问题????但是真的是一样的吗?(使用的时候还是有不一样的)请看:
会发现,没有报错的12行是通过bundle.principalclass去获取的class,查看静态库的info.plist可以发现,这个主要类设置的就是UnityFramework
那么至于为什么会出现符号未定义的问题也就显而易见了:因为这个动态库是懒加载的,并不是我们常用的系统自动加载的动态库。报符号未定义的错是因为在编译时,如果调用了[HostRouterApi sharedInstance].sendEventToHostBlock,编译器会去验证app的mach-o文件以及它依赖的动态库的mach-o文件中是否有这个类的定义。
由于在编译时,程序还没有加载动态库UnityFramework,而程序只包含了HostRouterApi类的头文件,并没有它对应的.m文件(编译器只会将.m文件编译到最终的mach-o文件中),所以编译器在app的mach-o文件以及它依赖的动态库中找不到HostRouterApi类的定义,然后编译器就报错了。所以如果我们把12行换成13行,也会出现同样的报错:
三、究竟设置了啥,让这个demo只能懒加载动态库
一开始以为是因为Link Binary With Libraries(构建阶段(Build Phase),用于指定要与你的应用程序一起链接的二进制文件。而且因为正常我们引入framework的时候,这里也会存在。但是Unity-iPhone这个demo是没有的)。
但是经过自己创建新的demo,去验证发现,动态库依然会主动被加载,并不需要手动去加载(没有模拟出Unity-iPhone这个demo的效果)。
这个问题还有待确定(为什么unity导出来的demo会出现使用类编译找不到符号的错误)。。。
四、修复措施
新建的类按照这个方法,让其符号是可见的,然后直接导入该动态库使用即可。
五、自定义组件
修改组件名称
不要直接修改target名称
直接修改target名称虽然可以达到修改组件名称的目的,但是后续unity项目无法继续导出项目(会因为路径问题导致导出出错)。
修改framework对应target的build settings里面的product name
更新bridge文件
bridge文件直接放在unityframework参与编译
bridge文件直接拖进unityframework里面参与编译,有更新的时候直接替换bridge文件即可。
unityframework依赖bridge组件
bridge作为一个单独的组件,unityframework依赖该组件:
注意要把use_frameworks!注释掉,否则unityframework里面不会有bridge组件里面的符号(也就达不到最终只提供一个framework给业务的目的)。
而且LZAvatarBridge需要支持bitcode(因为unityframework支持)
但是由于最新苹果要求关掉该配置,因此需要把unityframework默认的yes改成NO。LZAvatarBridge也不用开启bitcode。
自动获取main函数的两个参数
objectivec
- (void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts
启动unity引擎需要main入口函数的两个参数(int)argc 和 argv:(char*[])argv,一开始尝试让组件使用方在main函数里面将这两个参数传给组件。但是细想一下,发现这样不太美观(主要是即构也没这样搞,那么肯定是内部有办法去获取这两个参数的)。经过一顿牛逼的操作后,终于实现:
ini
NSArray<NSString *> *arguments = [[NSProcessInfo processInfo] arguments];
// 创建一个 char ** 数组,长度为 arguments 数组的长度加一(用于存储 NULL 结束符)
char **argv = (char **)malloc((arguments.count + 1) * sizeof(char *));
// 遍历 arguments 数组,将每个 NSString 对象转换为对应的 C 字符串
for (NSInteger i = 0; i < arguments.count; i++) {
NSString *argument = arguments[i];
const char *cString = [argument UTF8String];
// 复制 C 字符串到动态分配的内存中
argv[i] = strdup(cString);
}
// 最后一个元素设置为 NULL,表示参数列表的结束
argv[arguments.count] = NULL;
int argc = (int)arguments.count;