關(guān)于蘋果Xcode編譯器與Bitcode

前言

做iOS開發(fā)的朋友們都知道,目前最新的Xcode7,新建項(xiàng)目默認(rèn)就打開了bitcode設(shè)置.而且大部分開發(fā)者都被這個(gè)突如其來的bitcode功能給坑過導(dǎo)致項(xiàng)目編譯失敗,而這些因?yàn)閎itcode而編譯失敗的的項(xiàng)目都有一個(gè)共同點(diǎn),就是鏈接了第三方二進(jìn)制的庫或者框架,而這些框架或者庫恰好沒有包含bitcode的東西(暫且稱為東西),從而導(dǎo)致項(xiàng)目編譯不成功.所以每當(dāng)遇到這個(gè)情況時(shí)候大部分人都是直接設(shè)置Xcode關(guān)閉bitcode功能,全部不生成bitcode.也不去深究這一開關(guān)背后隱藏的原理.中槍的請(qǐng)點(diǎn)個(gè)贊.

LLVM是目前蘋果采用的編譯器工具鏈,Bitcode是LLVM編譯器的中間代碼的一種編碼,LLVM的前端可以理解為C/C++/OC/Swift等編程語言,LLVM的后端可以理解為各個(gè)芯片平臺(tái)上的匯編指令或者可執(zhí)行機(jī)器指令數(shù)據(jù),那么,BitCode就是位于這兩者直接的中間碼. LLVM的編譯工作原理是前端負(fù)責(zé)把項(xiàng)目程序源代碼翻譯成Bitcode中間碼,然后再根據(jù)不同目標(biāo)機(jī)器芯片平臺(tái)轉(zhuǎn)換為相應(yīng)的匯編指令以及翻譯為機(jī)器碼.這樣設(shè)計(jì)就可以讓LLVM成為了一個(gè)編譯器架構(gòu),可以輕而易舉的在LLVM架構(gòu)之上發(fā)明新的語言(前端),以及在LLVM架構(gòu)下面支持新的CPU(后端)指令輸出,雖然Bitcode僅僅只是一個(gè)中間碼不能在任何平臺(tái)上運(yùn)行,但是它可以轉(zhuǎn)化為任何被支持的CPU架構(gòu),包括現(xiàn)在還沒被發(fā)明的CPU架構(gòu),也就是說現(xiàn)在打開Bitcode功能提交一個(gè)App到應(yīng)用商店,以后如果蘋果新出了一款手機(jī)并CPU也是全新設(shè)計(jì)的,在蘋果后臺(tái)服務(wù)器一樣可以從這個(gè)App的Bitcode開始編譯轉(zhuǎn)化為新CPU上的可執(zhí)行程序,可供新手機(jī)用戶下載運(yùn)行這個(gè)App.

歷史回顧

在iPhone出來之前,蘋果主要的編譯器技術(shù)是用經(jīng)過稍微改進(jìn)的GCC工具鏈來把Objective-C語言編寫的代碼編譯出所指定的機(jī)器處理器上原生的可執(zhí)行程序.編譯器產(chǎn)生的可執(zhí)行程序叫做"Fat Binaries"--類似于Windows下PE格式的exe和Linux下的ELF格式的二進(jìn)制,不同的是,一個(gè)"Fat Binary"可以包含同一個(gè)程序的很多版本,所以同一個(gè)可執(zhí)行文件可以在不同的處理器上運(yùn)行.主要就是這個(gè)技術(shù)讓蘋果的硬件很容易的從PowerPC遷移到PowerPC64的處理器,以及后來再遷移到Intel和Intel64處理器.這個(gè)方案帶來的負(fù)面影響就是同一個(gè)文件中存了多份可執(zhí)行代碼,除了當(dāng)前機(jī)器可執(zhí)行的那一份之外其他都是無用的,白占空間. 這個(gè)在市場上被稱為"Universal Binary",在蘋果從PowerPC遷移到Intel處理器的事情開始存在的(一個(gè)二進(jìn)制文件既包含一份PowerPC版本和一份Intel版本).慢慢的后來又支持同時(shí)包含Intel 32bit和Intel 64bit. 在一個(gè)Fat binary中,又操作系統(tǒng)運(yùn)行時(shí)根據(jù)處理器類型動(dòng)態(tài)選擇正確的二進(jìn)制版本來運(yùn)行,但是應(yīng)用程序要支持不同平臺(tái)的處理器的話,應(yīng)用程序本身要多占用一些空間.當(dāng)然也有一些瘦身的工具,比如lipo,可以用來移除fat binary中那些當(dāng)前機(jī)器中不被支持的或者多余的可執(zhí)行代碼達(dá)到瘦身目的,lipo不會(huì)改變程序執(zhí)行邏輯,僅僅只是文件的大小瘦身.

編譯器現(xiàn)狀

隨著移動(dòng)設(shè)備移動(dòng)互聯(lián)網(wǎng)的深入發(fā)展,現(xiàn)在移動(dòng)設(shè)備中的程序大小變得越來越重要了,主要是因?yàn)橐苿?dòng)設(shè)備中不會(huì)有電腦上那么大的一個(gè)硬盤驅(qū)動(dòng)器.還有就是蘋果早就從原始的ARM處理器遷移到自家設(shè)計(jì)的A4,A5,A5X,A6,A7,A8,A8X,A9,A9X以及后續(xù)的A10處理器,他們的指令集已經(jīng)發(fā)生了改變和原始ARM設(shè)計(jì)的有所區(qū)別,所有的這些變化都被iOS操作系統(tǒng)底層以及Xcode/LLVM編譯工具向上層程序員一定程度的透明了,編譯出來的程序會(huì)包含很多執(zhí)行代碼版本.當(dāng)面對(duì)這個(gè)問題后,蘋果投入大量成本遷移到LLVM編譯器架構(gòu)并使用bitcode的必要性越來越大.從最開始的把OPENGL編譯為特定的GPU指令到把Clang編譯器(LLCM的C/OC編譯前端)支持Objective-C的改進(jìn)并作為Xcode的默認(rèn)編譯器.

LLVM提供了一個(gè)虛擬指令集機(jī)制,它可以翻譯出指定的所支持的處理器架構(gòu)的執(zhí)行代碼(機(jī)器碼).這個(gè)就使得為iOS應(yīng)用程序的編譯開發(fā)一個(gè)完全基于LLVM架構(gòu)的工具鏈成為可能.而LLVM的這個(gè)虛擬的通用的指令集可以用很多種表示格式:

叫做IR的文本表示的匯編格式(像匯編語言);
轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)表示的格式(像目標(biāo)代碼),這個(gè)二進(jìn)制格式就是我們所說的bitcode.
Bitcode和傳統(tǒng)的可執(zhí)行指令集不同,他維護(hù)的是函數(shù)功能的類型和簽名,比如,傳統(tǒng)可執(zhí)行指令集中,一系列(<=8)的布爾值可以壓縮存儲(chǔ)到單個(gè)字節(jié)中,但是在bitcode中他們是各自獨(dú)自表示的.此外,邏輯運(yùn)算操作(比如寄存器清零操作)也由他們對(duì)應(yīng)的邏輯表示方法($R=0);當(dāng)這些BitCode要轉(zhuǎn)換為特定機(jī)器平臺(tái)的指令集時(shí),他可以用經(jīng)過針對(duì)特定機(jī)器平臺(tái)優(yōu)化過的匯編指令來代替:xor eax, eax.(這個(gè)匯編指令同樣是寄存器<eax>清零操作).

然而bitcode他也不是完全獨(dú)立于處理器平臺(tái)和調(diào)用約定的.寄存器的大小在指令集中是一個(gè)相當(dāng)重要的特性,眾所周知,64bit寄存器可以比32bit寄存器存儲(chǔ)更多的數(shù)據(jù),生成64bit平臺(tái)的bitcode和32bit平臺(tái)的bitcode是明顯不同的,還有,調(diào)用約定可以根據(jù)函數(shù)定義或者函數(shù)調(diào)用來定義,這些可以確定函數(shù)的參數(shù)傳遞是傳寄存器值呢還是壓棧. 一些編程語言還有一些像sizeof(long)這樣的預(yù)處理指令,這些將在bitcode生成之前前被翻譯.一般情況下,對(duì)于支持fastcc(fast calling convention)調(diào)用的64bit平臺(tái)會(huì)生成與其一致的bitcode代碼.

蘋果的要求

到此,讓我們思考一下,為什么蘋果默認(rèn)要求watchOS和tvOS的App要上傳bitcode? 因?yàn)榘裝itcode上傳到他自己的中心服務(wù)器后,他可以為目標(biāo)安裝App的設(shè)備進(jìn)行優(yōu)化二進(jìn)制,減小安裝包的下載大小,當(dāng)然iOS開發(fā)者也可以上傳多個(gè)版本而不是打包到單個(gè)包里,但是這樣會(huì)占用更多的存儲(chǔ)空間. 最重要的是允許蘋果可以在后臺(tái)服務(wù)器對(duì)應(yīng)用程序進(jìn)行簽名,而不用導(dǎo)出任何密鑰到終端開發(fā)者那.

上傳到服務(wù)器的bitcode給蘋果帶來更好處是: 以后新設(shè)計(jì)了新指令集的新CPU,可以繼續(xù)從這份bitcode開始編譯出新CPU上執(zhí)行的可執(zhí)行文件,以供用戶下載安裝.
但是bitcode給開發(fā)者帶來的不便之處就是: 沒用bitcode之前,當(dāng)應(yīng)用程序奔潰后,開發(fā)者可以根據(jù)獲取的的奔潰日志再配上上傳到蘋果服務(wù)器的二進(jìn)制文件的調(diào)試符號(hào)表信息可以還原程序運(yùn)行過程到奔潰時(shí)后調(diào)用棧信息,對(duì)問題進(jìn)行定位排查.但是用了bitcode之后,用戶安裝的二進(jìn)制不是開發(fā)者這邊生成的,而是蘋果服務(wù)器經(jīng)過優(yōu)化后生成的,其對(duì)應(yīng)的調(diào)試符號(hào)信息丟失了,也就無法進(jìn)行前面說的還原奔潰現(xiàn)場找原因了.

目前,watchOS和tvOS應(yīng)用發(fā)布必須上傳帶bitcode版本的包.iOS應(yīng)用發(fā)布對(duì)bitcode的要求是可選的,用戶可以在Xcode的項(xiàng)目設(shè)置中關(guān)閉. 相當(dāng)于在編譯的時(shí)候加一個(gè)標(biāo)記:embed-bitcode-marker(調(diào)試構(gòu)建) embed-bitcode(打包/真機(jī)構(gòu)建).這個(gè)在clang編譯器的參數(shù)是-fembed-bitcode,swift編譯器的參數(shù)是-embed-bitcode.

實(shí)踐出真知

我們還是應(yīng)該實(shí)際弄兩個(gè)測試代碼進(jìn)行實(shí)踐和檢驗(yàn)一下比較好.做兩次測試,第一次準(zhǔn)備兩個(gè)C語言源代碼繼續(xù)測試;第二次把其中一個(gè)轉(zhuǎn)變?yōu)閰R編語言源代碼后再一個(gè)C代碼和一個(gè)匯編代碼一起重復(fù)之前的測試步驟進(jìn)行對(duì)比校驗(yàn)差異.

1 . 如下兩個(gè)全部是Objective-C代碼:

test.m :

#import <Foundation/Foundation.h>
void greeting(void)
{
    NSLog(@"hello world!");
}

demo.m :

#import <Foundation/Foundation.h>
void demo(void)
{
    NSLog(@"demo func");
}

用Clang編譯成 ARM64 格式且?guī)itcode的目標(biāo)文件test.o demo.o:

$ xcrun -sdk iphoneos clang -arch arm64 -fembed-bitcode -c test.m demo.m

然后把兩個(gè)目標(biāo)文件打包為一個(gè)靜態(tài)庫文件:

$ xcrun -sdk iphoneos ar  -r libTest.a test.o demo.o
# ar: creating archive libTest.a

用Shell命令otool查看目標(biāo)文件中是否包含bitcode段:

$ otool -l test.o |grep bitcode
# Prints:
#   sectname __bitcode
#   sectname __bitcode

如果看到輸出了2行sectname __bitcode,就是說明這靜態(tài)庫中的兩個(gè)目標(biāo)文件包含了bitcode.

2.下面把其中一個(gè)demo.m換成匯編語言再參與編譯:

用下面的命令把demo.m的C代碼轉(zhuǎn)換為ARM64匯編語言格式demo.s:

$ xcrun -sdk iphoneos clang -arch arm64 -S demo.m
$ cat demo.s
# Prints:
    .section    __TEXT,__text,regular,pure_instructions
    .ios_version_min 9, 2
    .globl    _demo
    .align    2
_demo:                                  ; @demo
    .cfi_startproc
; BB#0:
    stp    x29, x30, [sp, #-16]!
    mov     x29, sp
Ltmp0:
    .cfi_def_cfa w29, 16
Ltmp1:
    .cfi_offset w30, -8
Ltmp2:
    .cfi_offset w29, -16
    adrp    x0, L__unnamed_cfstring_@PAGE
    add    x0, x0, L__unnamed_cfstring_@PAGEOFF
    bl    _NSLog
    ldp    x29, x30, [sp], #16
    ret
    .cfi_endproc

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ; @.str
    .asciz    "demo func"

    .section    __DATA,__cfstring
    .align    4                       ; @_unnamed_cfstring_
L__unnamed_cfstring_:
    .quad    ___CFConstantStringClassReference
    .long    1992                    ; 0x7c8
    .space    4
    .quad    L_.str
    .quad    9                       ; 0x9

    .section    __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
    .long    0
    .long    0
    .subsections_via_symbol

然后刪除demo.m這個(gè)C源代碼,僅留下test.m和demo.s:

$ rm demo.m

現(xiàn)在,我們來把test.m這個(gè)C源代碼和dmeo.s這個(gè)匯編源代碼來一起帶著-fembed-bitcode參數(shù)來生成目標(biāo)代碼并打包為一個(gè)靜態(tài)庫:

$ xcrun -sdk iphoneos clang -arch arm64 -fembed-bitcode -c test.m demo.s
$ xcrun -sdk iphoneos ar -r libTest.a test.o demo.o

然后我們?cè)龠\(yùn)行otool工具來檢查這個(gè)新的靜態(tài)庫中包含的2個(gè)目標(biāo)文件是否都帶有bitcode段:

$ ar -t libTest.a
# Prints:
# __.SYMDEF SORTED
# test.o
# demo.o
$ otool -l libTest.a | grep bitcode
# Prints:
#  sectname __bitcode

很意外,這一次,只有一行sectname __bitcode輸出,這就說明這兩個(gè)目標(biāo)文件,有一個(gè)不帶有bitcode段,哪怕我們?cè)诰幾g的時(shí)候指定了參數(shù)-fembed-bitcode也沒有用.至于具體是哪一個(gè)不帶bitcode段,我們肯定知道就是那個(gè)從ARM64匯編語言編譯過來的目標(biāo)文件不帶.

那么就得出一個(gè)結(jié)論,bitcode的生成,是由匯編語言以上的上層語言編譯而來,和最前面所說的那樣,他是上層語言與匯編語言(機(jī)器語言)之間的一個(gè)中間碼.

目前我們?nèi)粘5膇OS應(yīng)用開發(fā)中,一般不會(huì)需要用到匯編層面去優(yōu)化的代碼.所以我們主要關(guān)注第三方(開源)C代碼,尤其是音視頻編碼解碼這些計(jì)算密集型項(xiàng)目代碼,關(guān)鍵計(jì)算的代碼針對(duì)特定平臺(tái)都有對(duì)應(yīng)平臺(tái)的匯編版本實(shí)現(xiàn),當(dāng)然也有C的實(shí)現(xiàn),但是默認(rèn)編譯一般都是用的匯編版本,這樣就會(huì)導(dǎo)致我們?cè)诰幾g這個(gè)開源代碼的時(shí)候哪怕你帶了-fembed-bitcode參數(shù)也僅僅只是讓項(xiàng)目中的部分C代碼的目標(biāo)文件帶了bitcode段,而那小數(shù)的匯編代碼的目標(biāo)文件一樣不帶bitcode段,這樣編譯出這個(gè)庫交給上層開發(fā)者使用的時(shí)候,就會(huì)出現(xiàn)在打包上傳或者真機(jī)調(diào)試的時(shí)候因?yàn)閄code默認(rèn)開了bitcode功能而鏈接失敗,導(dǎo)致不能真機(jī)調(diào)試或者不能上傳應(yīng)用到AppStore.

此文之初衷

最近在輔導(dǎo)我戴維營戰(zhàn)友們做手機(jī)音視頻直播的App,調(diào)試的時(shí)候手機(jī)采集音視頻,視頻用h264編碼,音頻采用aac編碼,通過RTMP協(xié)議往斗魚直播頻道發(fā)布媒體流,項(xiàng)目需要用FFMPEG和libx264兩個(gè)開源項(xiàng)目,在編譯為iOS框架庫提供給學(xué)生用的時(shí)候,他們遇到了bitcode的問題,雖然可以采取直接關(guān)閉bitcode來避免錯(cuò)誤,但是戰(zhàn)友的求知欲必須滿足,格物致知,必須讓其知其究竟.

libx264是VideoLan基金會(huì)管理的一個(gè)視頻編解碼的開源項(xiàng)目,其大量使用了各個(gè)平臺(tái)的多媒體匯編指令進(jìn)行了優(yōu)化,在編譯為不帶bitcode的庫的時(shí)候,完全按官方autotools編譯方法是沒有任何問題的;編譯全帶bitcode的庫的時(shí)候我們不得不關(guān)閉匯編優(yōu)化,在執(zhí)行./configure階段可以加上--disable-asm參數(shù)來禁用匯編.但是,這個(gè)選項(xiàng)在configure腳本中的實(shí)現(xiàn)機(jī)制有問題.導(dǎo)致其仍然調(diào)用了匯編的函數(shù),但是匯編的代碼卻沒有編譯進(jìn)去,從而會(huì)導(dǎo)致項(xiàng)目為真機(jī)構(gòu)建和打包的鏈接階段會(huì)爆出找不到符號(hào)的錯(cuò)誤,這樣就不能做到兩全其美.出于輕微程度的強(qiáng)迫癥影響,故把之前的FFMPEG和libx264項(xiàng)目的編譯腳本進(jìn)行了改進(jìn)和打補(bǔ)丁.目前已經(jīng)可以做到一鍵編譯出帶全部bitcode的FFMPEG和libx264的框架了.

FFmpeg需要依賴libx264.

自動(dòng)編譯腳本項(xiàng)目位置放在github:
https://github.com/Diveinedu-CN/FFmpeg-iOS-build-script.git

由于時(shí)間和篇幅原因,關(guān)于其他更多詳細(xì)的信息就不細(xì)細(xì)道來了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末裳涛,一起剝皮案震驚了整個(gè)濱河市众辨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌郊闯,老刑警劉巖蛛株,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異欢摄,居然都是意外死亡笋粟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門唆香,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吨艇,“玉大人,你說我怎么就攤上這事东涡。” “怎么了组贺?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵祖娘,是天一觀的道長。 經(jīng)常有香客問我掀潮,道長,這世上最難降的妖魔是什么仪吧? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮择诈,結(jié)果婚禮上出皇,老公的妹妹穿的比我還像新娘。我一直安慰自己恶迈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奈附,像睡著了一般煮剧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勉盅,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音挑胸,去河邊找鬼宰闰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛移袍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播螟左,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼巷嚣!你這毒婦竟也來了奄妨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤砸抛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后景东,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奔誓,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年和措,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜕煌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贫母,死狀恐怖盒刚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情因块,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布靠柑,位于F島的核電站吓懈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耻警。R本人自食惡果不足惜甸怕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一腮恩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧武契,春花似錦荡含、人聲如沸咒唆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寝蹈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箫老,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留界斜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓各薇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親君躺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子峭判,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 前言 2000年,伊利諾伊大學(xué)厄巴納-香檳分校(University of Illinois at Urbana-...
    星光社的戴銘閱讀 15,906評(píng)論 8 180
  • 前言 做iOS開發(fā)的朋友們都知道,目前最新的Xcode7,新建項(xiàng)目默認(rèn)就打開了bitcode設(shè)置.而且大部分開發(fā)者...
    戴維營教育閱讀 55,406評(píng)論 34 253
  • TITLE: 編程語言亂燉 碼農(nóng)最大的煩惱——編程語言太多棕叫。不是我不學(xué)習(xí)林螃,這世界變化快! 有時(shí)候還是蠻懷念十幾俺泣、二...
    碼園老農(nóng)閱讀 5,331評(píng)論 2 35
  • 已經(jīng)接近十二點(diǎn)了疗认,今天的生活很忙碌完残,源于我給自己晚上的生活增添了一個(gè)學(xué)習(xí)任務(wù),學(xué)習(xí)游泳横漏,本著互幫互助,相互督促缎浇,共...
    安然andU閱讀 614評(píng)論 0 1
  • 戰(zhàn)友素跺,但愿負(fù)重前行的不只有你 新媒體海宸2017-09-14 11:06 戰(zhàn)友二蓝,但愿負(fù)重前行的不只有你 你的排長柳...
    海宸明月閱讀 2,648評(píng)論 15 12