iOS面試題:騰訊一面以及參考思路:
http://www.itdecent.cn/p/c2048ae9d799
iOS面試題:阿里-P6一面-參考思路:
http://www.itdecent.cn/p/c2b5908cb48d
1、編譯過程做了哪些事情?
1.C++,Objective C都是編譯語言。編譯語言在執(zhí)行的時候,必須先通過編譯器生成機(jī)器碼,機(jī)器碼可以直接在CPU上執(zhí)行,所以執(zhí)行效率較高。
iOS開發(fā)目前的常用語言是:Objective和Swift。二者都是編譯語言,換句話說都是需要編譯才能執(zhí)行的。二者的編譯都是依賴于Clang + LLVM. OC和Swift因為原理上大同小異,知道一個即可!
iOS編譯
不管是OC還是Swift,都是采用Clang作為編譯器前端,LLVM(Low level vritual machine)作為編譯器后端。所以簡單的編譯過程如圖

編譯器前端
編譯器前端的任務(wù)是進(jìn)行:語法分析,語義分析,生成中間代碼(intermediate representation )。在這個過程中,會進(jìn)行類型檢查,如果發(fā)現(xiàn)錯誤或者警告會標(biāo)注出來在哪一行。

編譯器后端
編譯器后端會進(jìn)行機(jī)器無關(guān)的代碼優(yōu)化,生成機(jī)器語言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。iOS的編譯過程,后端的處理如下
LVVM優(yōu)化器會進(jìn)行BitCode的生成,鏈接期優(yōu)化等等

LLVM機(jī)器碼生成器會針對不同的架構(gòu),比如arm64等生成不同的機(jī)器碼。

執(zhí)行一次
XCode build的流程
當(dāng)你在XCode中,選擇build的時候(快捷鍵command+B),會執(zhí)行如下過程
編譯信息寫入輔助文件,創(chuàng)建編譯后的文件架構(gòu)(name.app)
處理文件打包信息,例如在debug環(huán)境下

執(zhí)行CocoaPod編譯前腳本
例如對于使用CocoaPod的工程會執(zhí)行CheckPods Manifest.lock
編譯各個.m文件,使用CompileC和clang命令。
1.CompileC ClassName.o ClassName.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
2.export.US-ASCII
3.export PATH="..."
4.clang-x objective-c -arch x86_64 -fmessage-length=0 -fobjc-arc...
-Wno-missing-field-initializers ... -DDEBUG=1 ... -isysroot
iPhoneSimulator10.1.sdk -fasm-blocks ... -I 上文提到的文件 -F 所需要的Framework-iquote 所需要的Framework ... -c ClassName.c -o ClassName.o
通過這個編譯的命令,我們可以看到

2.字典大致實現(xiàn)原理;
一:字典原理
NSDictionary(字典)是使用hash表來實現(xiàn)key和value之間的映射和存儲的
方法:- (void)setObject:(id)anObject forKey:(id)aKey;
Objective-C中的字典NSDictionary底層其實是一個哈希表
二:哈希原理
散列表(Hash table,也叫哈希表),是根據(jù)關(guān)鍵碼值(Key value)而直接進(jìn)行訪問的數(shù)據(jù)結(jié)構(gòu)。也就是說,它通過把關(guān)鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數(shù)叫做散列函數(shù),存放記錄的數(shù)組叫做散列表。
給定表M,存在函數(shù)f(key),對任意給定的關(guān)鍵字值key,代入函數(shù)后若能得到包含該關(guān)鍵字的記錄在表中的地址,則稱表M為哈希(Hash)表,函數(shù)f(key)為哈希(Hash) 函數(shù)。
哈希概念:哈希表的本質(zhì)是一個數(shù)組,數(shù)組中每一個元素稱為一個箱子(bin),箱子中存放的是鍵值對。
三:哈希存儲過程
1.根據(jù) key 計算出它的哈希值 h。
2.假設(shè)箱子的個數(shù)為 n,那么這個鍵值對應(yīng)該放在第 (h % n) 個箱子中。
3.如果該箱子中已經(jīng)有了鍵值對,就使用開放尋址法或者拉鏈法解決沖突。
在使用拉鏈法解決哈希沖突時,每個箱子其實是一個鏈表,屬于同一個箱子的所有鍵值對都會排列在鏈表中。
哈希表還有一個重要的屬性: 負(fù)載因子(load factor),它用來衡量哈希表的空/滿程度,一定程度上也可以體現(xiàn)查詢的效率,計算公式為:
負(fù)載因子 = 總鍵值對數(shù) / 箱子個數(shù)
負(fù)載因子越大,意味著哈希表越滿,越容易導(dǎo)致沖突,性能也就越低。因此,一般來說,當(dāng)負(fù)載因子大于某個常數(shù)(可能是 1,或者 0.75 等)時,哈希表將自動擴(kuò)容。
哈希表在自動擴(kuò)容時,一般會創(chuàng)建兩倍于原來個數(shù)的箱子,因此即使 key 的哈希值不變,對箱子個數(shù)取余的結(jié)果也會發(fā)生改變,因此所有鍵值對的存放位置都有可能發(fā)生改變,這個過程也稱為重哈希(rehash)。
哈希表的擴(kuò)容并不總是能夠有效解決負(fù)載因子過大的問題。假設(shè)所有 key 的哈希值都一樣,那么即使擴(kuò)容以后他們的位置也不會變化。雖然負(fù)載因子會降低,但實際存儲在每個箱子中的鏈表長度并不發(fā)生改變,因此也就不能提高哈希表的查詢性能。
基于以上總結(jié),細(xì)心的朋友可能會發(fā)現(xiàn)哈希表的兩個問題:
1.如果哈希表中本來箱子就比較多,擴(kuò)容時需要重新哈希并移動數(shù)據(jù),性能影響較大。
2.如果哈希函數(shù)設(shè)計不合理,哈希表在極端情況下會變成線性表,性能極低。
3.block和函數(shù)指針的理解;
相似點:
函數(shù)指針和Block都可以實現(xiàn)回調(diào)的操作,聲明上也很相似,實現(xiàn)上都可以看成是一個代碼片段。
函數(shù)指針類型和Block類型都可以作為變量和函數(shù)參數(shù)的類型。(typedef定義別名之后,這個別名就是一個類型)
不同點:
函數(shù)指針只能指向預(yù)先定義好的函數(shù)代碼塊(可以是其他文件里面定義,通過函數(shù)參數(shù)動態(tài)傳入的),函數(shù)地址是在編譯鏈接時就已經(jīng)確定好的。
Block本質(zhì)是Objective-C對象,是NSObject的子類,可以接收消息。
函數(shù)里面只能訪問全局變量,而Block代碼塊不光能訪問全局變量,還擁有當(dāng)前棧內(nèi)存和堆內(nèi)存變量的可讀性(當(dāng)然通過__block訪問指示符修飾的局部變量還可以在block代碼塊里面進(jìn)行修改)。
從內(nèi)存的角度看,函數(shù)指針只不過是指向代碼區(qū)的一段可執(zhí)行代碼,而block實際上是程序運(yùn)行過程中在棧內(nèi)存動態(tài)創(chuàng)建的對象,可以向其發(fā)送copy消息將block對象拷貝到堆內(nèi)存,以延長其生命周期。
4.一般開始做一個項目,你的架構(gòu)是如何思考的?
參考1:https://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html
參考2:https://casatwy.com/iosying-yong-jia-gou-tan-wang-luo-ceng-she-ji-fang-an.html

文章轉(zhuǎn)載:http://www.cocoachina.com/ios/20171127/21331.html