移動平臺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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索