【面試】iOS 開發面試題(三)
1、iOS資料持久化儲存方案有哪些?
參考答案:
plist屬性列表格儲存體(如NSUserDefaults) 檔案儲存體(如位元據寫入檔案儲存體,通過NSFileManager來操作將下載起來的位元據寫一篇檔案中儲存) NSKeydeArchiverArchive Storage,常見的是自動化歸檔/解檔處理,想要學習如何通過runtime實現自動化歸檔/解檔,可 資料庫SQLite3儲存(如FMDB、Core Data)2、沙箱的目錄結構是怎樣的?各自一般用於什麼場合?
參考答案:
Application:存放程式源檔案,上架前經過數位簽章,上架後不可修改 Documents: 儲存應?運行時產生的需要持久化的資料,iTunes同步裝置時會備份該目 錄。例如,遊戲應用可將遊戲存檔儲存在該目錄 tmp: 儲存應?運行時所需的臨時資料,使?完畢後再將相應的檔案從該目錄刪除。應用 沒有運行時,系統也可能會清除該目錄下的檔案。iTunes同步裝置時 不會備份該目錄 Library/Caches: 儲存應用運行時?成的需要持久化的資料,iTunes同步裝置時不會備份 該目錄。?一般儲存體積大、不需要備份的非重要資料,比如網路資料緩衝儲存到Caches下 Library/Preference: 儲存應用的所有喜好設定,如iOS的Settings(設定) 應?會在該目錄中尋找應?的設定資訊。iTunes同步裝置時會備份該目錄3、#define定義的宏和const定義的常量有什麼區別?
參考答案:
#define定義宏的指令,程式在預先處理階段將用#define所定義的內容只是進行了替換。因此程式運行時,常量表中並沒有用#define所定義的宏,系統並不為它分配記憶體,而且在編譯時間不會檢查資料類型,出錯的機率要大一些。 const定義的常量,在程式運行時是存放在常量表中,系統會為它分配記憶體,而且在編譯時間會進行類型檢查。 #define定義運算式時要注意“邊緣效應”,例如如下定義:
| 1 2 3 4 |
#define N 2 + 3 // 我們預想的N值是5,我們這樣使用N int a = N / 2;// 我們預想的a的值是2.5,可實際上a的值是3.5 |
4、常見的出現記憶體循環參考的情境有哪些?
參考答案:
定時器(NSTimer):NSTimer經常會被作為某個類的成員變數,而NSTimer初始化時要指定self為target,容易造成循環參考(self->timer->self)。 另外,若timer一直處於validate的狀態,則其引用計數將始終大於0,因此在不再使用定時器以後,應該先調用invalidate方法 block的使用:block在copy時都會對block內部用到的對象進行強引用(ARC)或者retainCount增1(非ARC)。在ARC與非ARC環境下對block使用不當都會引起循環參考問題, 一般表現為,某個類將block作為自己的屬性變數,然後該類在block的方法體裡面又使用了該類本身,簡單說就是self.someBlock =Type var{[self dosomething];或者self.otherVar = XXX;或者_otherVar = …};出現迴圈的原因是:self->block->self或者self->block->_ivar(成員變數) 代理(delegate):在委託問題上出現循環參考問題已經是老生常談了,規避該問題的殺手鐧也是簡單到哭,一字訣:聲明delegate時請用assign(MRC)或者weak(ARC),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉循環參考了!5、block中的weak self,是任何時候都需要加的嗎?
參考答案:
不是什麼任何時候都需要添加的,不過任何時候都添加似乎總是好的。只要出現像self->block->self.property/self->_ivar這樣的結構鏈時,才會出現循環參考問題。好好分析一下,就可以推斷出是否會有循環參考問題。
6、GCD的queue、main queue中執行的代碼一定是在main thread嗎?
參考答案:
對於queue中所執行的代碼不一定在main thread中。如果queue是在主線程中建立的,那麼所執行的代碼就是在主線程中執行。如果是在子線程中建立的,那麼就不會在main thread中執行。 對於main queue就是在主線程中的,因此一定會在主線程中執行。擷取main queue就可以了,不需要我們建立,擷取方式通過調用方法dispatchgetmain_queue來擷取。7、標頭檔中聲明的成員變數(不是屬性),外部可直接存取嗎?
參考答案:
外部不能直接存取標頭檔所聲明的成員變數,需要提供成員變數的getter方法才能在外部存取。而屬性已經直接給我們自動產生了getter方法,因此外部可以直接存取屬性。
8、TCP和UDP的區別是什嗎?
參考答案:
TCP:連線導向、傳輸可靠(保證資料正確性,保證資料順序傳輸)、用於傳輸大量資料(流模式)、速度慢,建立串連需要開銷較多(時間,系統資源)。 UDP:面向非串連、傳輸不可靠、用於傳輸少量資料(資料包模式)、速度快,傳輸的是報文。9、MD5和Base64的區別是什麼,各自使用情境是什嗎?
參考答案:
做過加密相關的功能的,幾乎都會使用到MD5和Base64,它們兩者在實際開發中是最常用的。
MD5:是一種無法復原的摘要演算法,用於產生摘要,無法逆著破解得到原文。常用的是產生32位摘要,用於驗證資料的有效性。比如,在網路請求介面中,通過將所有的參數產生摘要,用戶端和服務端採用同樣的規則產生摘要,這樣可以防篡改。又如,下載檔案時,通過組建檔案的摘要,用於驗證檔案是否損壞。 Base64:屬於密碼編譯演算法,是可逆的,經過encode後,可以decode得到原文。在開發中,有的公司上傳圖片採用的是將圖片轉換成base64字串,再上傳。在做加密相關的功能時,通常會將資料進行base64加密/解密。10、發送10個網路請求,然後再接收到所有回應之後執行後續操作,如何??
參考答案:
從題目分析可知,10個請求要全部完成後,才執行某一功能。比如,下載10圖片後合成一張大圖,就需要非同步全部下載完成後,才能合并成大圖。
做法:通過dispatch_group_t來實現,將每個請求放入到Group中,將合并成大圖的操作放在dispatch_group_notify中實現。
| 1 2 3 4 5 6 7 8 9 10 |
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ /*載入圖片1 */ }); dispatch_group_async(group, queue, ^{ /*載入圖片2 */ }); dispatch_group_async(group, queue, ^{ /*載入圖片3 */ }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 合并圖片 }); |
11、__block和__weak修飾符的區別?
參考答案:
__block不管是ARC還是MRC模式下都可以使用,它的作用是標識block外的變數在block內使用可以修改變數的值。 __weak只能在ARC模式下使用,表示弱引用,主要是用於防止循環參考而引入的。12、常見的Http狀態代碼有哪些?
參考答案:
302是請求重新導向。 500及以上是伺服器錯誤,如503表示伺服器找不到、3840表示伺服器返回無效JSON。 400及以上是請求連結錯誤或者找不到伺服器,如常見的404。 200及以上是正確,如常見的是200表示請求正常。 100及以上是請求接受成功。13、iOS與H5互動的方式有哪些?
參考答案:
通過協議在shouldStartRequest中捕獲請求,擷取scheme來判斷預先定義的功能,然後調用native代碼。比如,定義點擊圖片調用native來展示大圖,那麼js接收到點擊時,重新導向將圖片的url添加上自訂scheme,如HYBImagePreview://這樣。14、如何淺拷貝與深拷貝的理解?
參考答案:
所謂淺拷貝是指只複製指向對象的指標,而不複製引用對象本身(同一份記憶體),而所謂深拷貝是指複製引用對象本身(新建立了一份記憶體)。
15、使用是懶載入?常見情境?
參考答案:
所謂懶載入是指在真正需要到的時候才真正載入記憶體。常見的情境就是重寫屬性的getter方法,在getter方法中判斷是否建立過或者載入過,若未建立過或者未載入過,則建立或者載入。
懶載入可以最佳化效能,有的功能一般情況下不會使用到,但是這些功能一使用就會佔較大的記憶體,此時使用懶載入就非常的好了。
常見的寫法:
| 1 2 3 4 5 6 7 8 9 |
- (NSMutableArray *)dataSource { if (_dataSource == nil) { _dataSource = [[NSMutableArray alloc] init]; // 添加預設資料等 // ... } } |
16、一個tableView是否可以關聯兩個不同的資料來源?
參考答案:
當然是可以關聯多個不同的資料來源,但是不同同時使用多個資料來源而已。比如,一個列表有兩個篩選功能,一個是篩選城市,一個是篩選時間,那麼這兩個就是兩個資料來源了。當篩選城市時,就會使用城市資料來源;當篩選時間時,就會使用時間資料來源。
17、對象添加到通知中樞中,當通知中樞發通知時,這個對象卻已經被釋放了,可能會出現什麼問題?
參考答案:
其實這種只是考查對通知的簡單應用。通知是多對多的關係,主要使用情境是跨模組傳值。當某對象加入到通知中樞後,若在對象被銷毀前不將該對象從通知中樞中移除,當發送通知時,就會造成崩潰。這是很常見的。所以,在添加到通知中樞後,一定要在釋放前移除。
18、實現過架構或者庫以供他人使用嗎?如果有,請談一談構建架構或者庫時候的經驗;如果沒有,請設想和設計架構的public的API,並指出大概需要如何做、需要注意哪些問題,以使人人更容易地使用你的架構。
參考答案:
從以下角度出發來思考和設計公用架構:
確保外部調用簡單,且保證有詳細的標頭檔注釋說明。 確保API編碼規範,保證風格統一。 確保API易擴充,可以考慮預留參數 確保沒有外部依賴或者依賴要儘可能的少,以保證公用庫的純潔(原則上不能有外部依賴) 確保易維護,不存在冗餘API19、如何自動計算cell的高度?
參考答案:
筆者喜歡純程式碼自動布局,一直使用Masonry這個第三方庫來實現純程式碼自動布局的,使用起來非常簡單,而且效率也很高。開發起來,提高了開發效率。
關於Masonry自動計算行高,筆者提供了swift版和oc版本的擴充,這兩個版本都提供了自動計算行高的功能,並且帶有緩衝功能,保證永遠只計算一次行高,效率就會很高,一般的應用也就不會卡屏了。
實現原理:通過資料模型的id作為key,以確保唯一,如何才能保證複用cell時不會出現混亂。在配置完資料後,通過更新約束,得到最後一個控制項的frame,就只可以判斷cell實際需要的高度,並且緩衝下來,下次再擷取時,判斷是否存在,若存在則直接返回。因此,只會計算一遍。
20、UITableView是如何計算內容高度的?為什麼初始化時配置資料時,擷取行高的代理方法會調用資料條數次?
參考答案:
UITableView是繼承於UIScrollView的,因此也有contentSize。要得到tableview的contentsize,就需要得到所有cell的高度,從而計算出總高度,才能得到contentsize。因此,在reloadData時,就會調用該代理方法資料條數次。
為了提高效率,筆者寫了擴充用於自動計算行高的,並且帶有緩衝,以保證只會計算一次,防止卡屏。做到這一點,一般的應用就可以解決卡屏的問題了。對於富文本比較多的應用,還可以繼續最佳化哦。