一、背景
由于定義了unity和原生之間交互的橋,因此在導(dǎo)出unity項(xiàng)目后,需要將橋源碼加到unityFramework里面參與編譯,編譯出動(dòng)態(tài)庫(kù)。


二、問題及排查歷程
-
符號(hào)未定義
在unity那個(gè)demo里面進(jìn)行橋使用:

發(fā)現(xiàn)符號(hào)未定義的問題:

然后發(fā)現(xiàn)為什么庫(kù)里面其他的符號(hào)可以被使用:

-
符號(hào)未定義原因
細(xì)看不同之處:

原來(lái)加了這個(gè)顯示,符號(hào)默認(rèn)對(duì)外界隱藏。查看動(dòng)態(tài)庫(kù)設(shè)置參數(shù),果然如此:

于是乎我也在自定義的類加上這個(gè)參數(shù)不就解決了?(哈哈哈,只能說(shuō)運(yùn)氣沒有站在我這邊,啪啪打臉):

在自定義的類聲明前面,也加上了這個(gè)標(biāo)識(shí),再次編譯,wtf還是報(bào)符號(hào)未定義的錯(cuò)誤。然后開始反思為什么會(huì)出現(xiàn)這樣的效果,明明是一樣的。我甚至嘗試把這個(gè)類定義到UnityFramework文件里面,以避免庫(kù)單獨(dú)對(duì)這個(gè)文件做了什么處理。結(jié)果還是不行(依然報(bào)錯(cuò))。然后再次問:明明是一樣的,為什么還會(huì)報(bào)這個(gè)問題????但是真的是一樣的嗎?(使用的時(shí)候還是有不一樣的)請(qǐng)看:

會(huì)發(fā)現(xiàn),沒有報(bào)錯(cuò)的12行是通過bundle.principalclass去獲取的class,查看靜態(tài)庫(kù)的info.plist可以發(fā)現(xiàn),這個(gè)主要類設(shè)置的就是UnityFramework

那么至于為什么會(huì)出現(xiàn)符號(hào)未定義的問題也就顯而易見了:因?yàn)檫@個(gè)動(dòng)態(tài)庫(kù)是懶加載的,并不是我們常用的系統(tǒng)自動(dòng)加載的動(dòng)態(tài)庫(kù)。報(bào)符號(hào)未定義的錯(cuò)是因?yàn)樵诰幾g時(shí),如果調(diào)用了[HostRouterApi sharedInstance].sendEventToHostBlock,編譯器會(huì)去驗(yàn)證app的mach-o文件以及它依賴的動(dòng)態(tài)庫(kù)的mach-o文件中是否有這個(gè)類的定義。
由于在編譯時(shí),程序還沒有加載動(dòng)態(tài)庫(kù)UnityFramework,而程序只包含了HostRouterApi類的頭文件,并沒有它對(duì)應(yīng)的.m文件(編譯器只會(huì)將.m文件編譯到最終的mach-o文件中),所以編譯器在app的mach-o文件以及它依賴的動(dòng)態(tài)庫(kù)中找不到HostRouterApi類的定義,然后編譯器就報(bào)錯(cuò)了。所以如果我們把12行換成13行,也會(huì)出現(xiàn)同樣的報(bào)錯(cuò):

三、究竟設(shè)置了啥,讓這個(gè)demo只能懶加載動(dòng)態(tài)庫(kù)
一開始以為是因?yàn)長(zhǎng)ink Binary With Libraries(構(gòu)建階段(Build Phase),用于指定要與你的應(yīng)用程序一起鏈接的二進(jìn)制文件。而且因?yàn)檎N覀円雈ramework的時(shí)候,這里也會(huì)存在。但是Unity-iPhone這個(gè)demo是沒有的)。

但是經(jīng)過自己創(chuàng)建新的demo,去驗(yàn)證發(fā)現(xiàn),動(dòng)態(tài)庫(kù)依然會(huì)主動(dòng)被加載,并不需要手動(dòng)去加載(沒有模擬出Unity-iPhone這個(gè)demo的效果)。
這個(gè)問題還有待確定(為什么unity導(dǎo)出來(lái)的demo會(huì)出現(xiàn)使用類編譯找不到符號(hào)的錯(cuò)誤)。。。
四、修復(fù)措施

新建的類按照這個(gè)方法,讓其符號(hào)是可見的,然后直接導(dǎo)入該動(dòng)態(tài)庫(kù)使用即可。
五、自定義組件
-
修改組件名稱
不要直接修改target名稱

直接修改target名稱雖然可以達(dá)到修改組件名稱的目的,但是后續(xù)unity項(xiàng)目無(wú)法繼續(xù)導(dǎo)出項(xiàng)目(會(huì)因?yàn)槁窂絾栴}導(dǎo)致導(dǎo)出出錯(cuò))。

修改framework對(duì)應(yīng)target的build settings里面的product name
-
更新bridge文件
-
bridge文件直接放在unityframework參與編譯
bridge文件直接拖進(jìn)unityframework里面參與編譯,有更新的時(shí)候直接替換bridge文件即可。

-
unityframework依賴bridge組件
bridge作為一個(gè)單獨(dú)的組件,unityframework依賴該組件:

注意要把use_frameworks!注釋掉,否則unityframework里面不會(huì)有bridge組件里面的符號(hào)(也就達(dá)不到最終只提供一個(gè)framework給業(yè)務(wù)的目的)。
而且LZAvatarBridge需要支持bitcode(因?yàn)閡nityframework支持)

但是由于最新蘋果要求關(guān)掉該配置,因此需要把unityframework默認(rèn)的yes改成NO。LZAvatarBridge也不用開啟bitcode。
-
自動(dòng)獲取main函數(shù)的兩個(gè)參數(shù)
- (void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts
啟動(dòng)unity引擎需要main入口函數(shù)的兩個(gè)參數(shù)(int)argc 和 argv:(char*[])argv,一開始嘗試讓組件使用方在main函數(shù)里面將這兩個(gè)參數(shù)傳給組件。但是細(xì)想一下,發(fā)現(xiàn)這樣不太美觀(主要是即構(gòu)也沒這樣搞,那么肯定是內(nèi)部有辦法去獲取這兩個(gè)參數(shù)的)。經(jīng)過一頓牛逼的操作后,終于實(shí)現(xiàn):
NSArray<NSString *> *arguments = [[NSProcessInfo processInfo] arguments];
// 創(chuàng)建一個(gè) char ** 數(shù)組,長(zhǎng)度為 arguments 數(shù)組的長(zhǎng)度加一(用于存儲(chǔ) NULL 結(jié)束符)
char **argv = (char **)malloc((arguments.count + 1) * sizeof(char *));
// 遍歷 arguments 數(shù)組,將每個(gè) NSString 對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的 C 字符串
for (NSInteger i = 0; i < arguments.count; i++) {
NSString *argument = arguments[i];
const char *cString = [argument UTF8String];
// 復(fù)制 C 字符串到動(dòng)態(tài)分配的內(nèi)存中
argv[i] = strdup(cString);
}
// 最后一個(gè)元素設(shè)置為 NULL,表示參數(shù)列表的結(jié)束
argv[arguments.count] = NULL;
int argc = (int)arguments.count;