移动平台native代码遭遇的坑

最近客户端终于开始运行在移动平台上了,以前在PC平台上彻底没问题的代码,开始出现一些诡异的问题。服务器


为了保证客户端和服务器使用绝对相同的逻辑执行流程,咱们采用C++来开发一部分native代码同时供客户端和服务端来使用。在迁移到移动平台时,这些native库在IOS和Android平台上出现了不一样程度的水土不服。微信


首次在移动平台就发生了crash,而且只有Android平台会crash, 而IOS能够正常进入游戏。架构


最后定位到,当执行相似下面的代码时安卓平台就会发生crash。iphone


1
2
3
4
5
int a = 3;
char buf[64];
char *p = buf;
*p = 0;
*( int *)(p + 1) = a;


在编译安卓平台native动态库时,为了尽量的保证兼容性,咱们采用了armeabi-v7a来编译native动态库,据ARMv7开发文档显示,在ARMv7架构下,uint32_t *须要4字节对齐,而uint16_t *则须要2字节对齐,只有uint8_t *才不须要对齐约定。函数


而苹果自iphone5s发行时,就采用了基于ARMv8-A架构的Apple-A7。根据ARMv8-A开发文档显示,在ARMv8-A架构下,全部地址访问都再也不须要指针对齐要求。换句话说在IOS的64位平台上,上面代码是彻底正确的。布局


固然,木桶原理,为了保证代码在全部平台上都能正常运行,须要作出以下修改:优化


1
2
3
4
5
6
//此段代码同时能够无视机器大小端,而强制a在内存中的布局为大端仍是小端,此种写法为小端
- *( int *)(p + 1) = a;
+ p[1] = a & 0xff;
+ p[2] = (a >> 8) & 0xff;
+ p[3] = (a >> 16) & 0xff;
+ p[4] = (a >> 24) & 0xff;


若是要保持与机器大小端相同可采用以下写法:ui


1
2
3
- *( int *)(p + 1) = a;
//因为a只有四个字节,此处能够手动展开memcpy以优化函数调用和for循环
+ memcpy (p, &a, sizeof (a));


看到Android这么热闹,IOS也有点不平衡,在调用某个native函数时,报出了`To marshal a managed method, please add an attribute named ‘MonoPInvokeCallback’ to the method definition.`错误。spa


可是并非全部native函数都会有这个问题。通过比较发现,这个函数在设计时,为了方便方便Unity能够接管native内部的log, 多增长了一个参数,用来将C#中log函数传入。直接将参数改成NULL时,果真问题解决了。可是很奇怪的是,在Windows下并不会有此问题。.net


最终在MonoTouch的官方文档中找到了答案。


在微软向ECMA提出的CLI(通用语言基础架构)中,并无定义标签`MonoPInvokeCallback`。这也正印证了,咱们在PC平台上历来没有出现过此问题。


若是在编译成移动平台时’Scripting backend’选项选用了`IL2CPP`,就须要使用AOT编译器来进行编译。


而进行AOT编译时,MONO须要知道哪些静态函数可能会被native代码调用,以便对C#函数进行额外处理(ps. 理论上,一个函数是否须要会被传入native函数中,是能够在编译时推导出来的,不知道MONO为何没有作这件事)。


本文分享自微信公众号 - 重归混沌(findstrx)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索