2021年iOS開發-大廠面試iOS真題整理(oc篇)

1.C和 OC 如何混編

xcode可以識別一下幾種擴展名文件:

.m文件,可以編寫 OC語言 和 C 語言代碼 .cpp: 只能識別C++ 或者C語言(C++兼容C) .mm: 主要用於混編 C++和OC代碼,可以同時識別OC,C,C++代碼

2. Swift 和OC 如何調用?

Swift 調用 OC代碼 需要創建一個 Target-BriBridging-Header.h 的橋文件,在喬文件導入需要調用的OC代碼頭文件即可 OC 調用 Swift代碼 直接導入 Target-Swift.h文件即可, Swift如果需要被OC調用,需要使用@objc 對方法或者屬性進行修飾

3. Foundation 對象與 CoreFoundation 對象 有什麼區別?

Foundation對象是OC的,在MRC下需要手動管理內存,ARC下不需要手動管理 Core Foundation對象是C對象, MRC和ARC都需要手動管理內存 數據類型之間的轉換 ARC:__bridge_retained, __bridge_transfer(自動內存管理) 非ARC: __bridge

4.與OC比較.Swift有什麼優點?

Swift 是一門新型語言,借鑑了JS,Python,C#,Ruby等語言特性,看上去偏腳本化,Swift 仍支持 cocoa touch 框架

優點:

Swift更加安全,它是類型安全的語言。 Swift容易閱讀,語法和文件結構簡易化。 Swift更易於維護,文件分離後結構更清晰。 Swift代碼更少,簡潔的語法,可以省去大量冗餘代碼 Swift速度更快,運算性能更高。

5. delegate(代理,委託)

委託是協議的一種,顧名思義,就是委託他人幫自己去做什麼事。

delegate:

非常嚴格的語法。所有將聽到的事件必須是在delegate協議中有清晰的定義,語法清晰,易讀; 如果delegate中的一個方法沒有實現那麼就會出現編譯警告/錯誤 在釋放代理對象時,需要小心的將delegate改爲nil。一旦設定失敗,那麼調用釋放對象的方法將會出現內存crash

6.Notification(通知)

在IOS應用開發中有一個”Notification Center“的概念。它是一個單例對象,允許當事件發生時通知一些對象。

notification:

對於一個發出的通知,多個對象能夠做出反應,即1對多的方式實現簡單 controller能夠傳遞context對象(dictionary),context對象攜帶了關於發送通知的自定義的信息 在調試的時候應用的工作以及控制過程難跟蹤;

7.KVO

KVO是一個對象能夠觀察另外一個對象的屬性的值,並且能夠發現值的變化。

KVO:

能夠提供一種簡單的方法實現兩個對象間的同步。例如:model和view之間同步; 能夠對非我們創建的對象,即內部對象的狀態改變作出響應,而且不需要改變內部對象(SKD對象)的實現;

8. 如何選擇delegate、notification、KVO?

三種模式都是一個對象傳遞事件給另外一個對象,並且不要他們有耦合。

delegate. 一對一 notification 一對多,多對多 KVO 一對一 三者各有自己的特點:

delegate 語法簡潔,方便閱讀,易於調試 notification 靈活多變,可以跨越多個類之間進行使用 KVO 實現屬性監聽,實現model和view同步 可以根據實際開發遇到的場景來使用不同的方式

更多面試資料

9. 若你去設計一個通知中心,你會怎樣設計?

個人理解: 參考現有的通知中心

創建通知中心單例類,並在裏面有個一個保存通知的全局NSDiction 對於註冊通知的類,將其註冊通知名作爲key, 執行的方法和類,以及一些參數做爲一個數組爲值 發送通知可以調用通知中心,通過字典key(通知名),找到對應的 類和方法進行執行調用傳值.

10. Notification 和KVO區別

KVO提供一種機制,當指定的被觀察的對像的屬性被修改後,KVO會自動通知響應的觀察者,KVC(鍵值編碼)是KVO的基礎 通知:是一種廣播機制,在實踐發生的時候,通過通知中心對象,一個對象能夠爲所有關心這個時間發生的對象發送消息,兩者都是觀察者模式,不同在於KVO是被觀察者直接發送消息給觀察者,是對象間的直接交互,通知則是兩者都和通知中心對象交互,對象之間不知道彼此 本質區別,底層原理不一樣.kvo 基於 runtime, 通知則是有個通知中心來進行通知

11.結構體與數組有什麼區別?

結構體可以存不同類型的元素,而數組只能存同一類型 結構體類型需要我們自已定義.數組是用別的類型加[元素個數] 結構體內存分配方式很特別,使用對齊原則,不一定是所有元素的字節數和,而數組一定是所有元素的字節數和. 結構體指針可以指針名->結構體元素名(取元素);數組不行.

12. NSDictionary的實現原理是什麼?

哈希表(NSDictionary 是通過hash表來實現key和value之間的映射和存儲的)

13.說一下靜態庫和動態庫之間的區別

靜態庫:以.a 和 .framework爲文件後綴名。 動態庫:以.tbd(之前叫.dylib) 和 .framework 爲文件後綴名。 靜態庫:鏈接時會被完整的複製到可執行文件中,被多次使用就有多份拷貝。 動態庫:鏈接時不復制,程序運行時由系統動態加載到內存,系統只加載一次,多個程序共用(如系統的UIKit.framework等),節省內存。 // 靜態庫.a 和 framework區別.a 主要是二進制文件,不包含資源,需要自己添加頭文件 .framework 可以包含頭文件+資源信息

14.對於 oc 來講,他的最大優缺點是什麼? 如何缺點如何繞過這些不足

優點:

OC是C語言的超集, 在C語言基礎上增加了面向對象特性, 開發使用起來會方便高效. 分類可以快速擴展類的方法.擴展模塊之間相互不影響 運行時特性,動態特性(動態類型,動態綁定,動態加載),提高了編程的靈活性 OC可以與C / C++進行混編 缺點:

不支持多繼承,多繼承可以使用分類,協議,消息轉發來彌補 不支持運算符重載 使用動態運行時類型,所有的方法都是函數調用,所以很多編譯時優化方法都用不到,如內聯函數等,性能低劣。 執行效率比C低,語法怪異

15. OC與 JS交互方式有哪些?

通過攔截URL 使用MessageHandler(WKWebView) JavaScriptCore (UIWebView) 使用三方庫WebViewJavascriptBridge,可提供 js 調OC,以及OC掉JS

16. 通過JS調用OC代碼(url攔截)一

通過攔截url(適用於UIWebView和WKWebView)

  • (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *url = request.URL.absoluteString; if ([url rangeOfString:@"需要跳轉源生界面的URL判斷"].location != NSNotFound) {

//跳轉原生界面 return NO; } return YES; }

17. JS調用OC代碼(messageHander)二

當JS端想傳一些數據給iOS.那它們會調用下方方法來發送. window.webkit.messageHandlers.<方法名>.postMessage(<數據>)上方代碼在JS端寫會報錯,導致網頁後面業務不執行.可使用try-catch執行. 那麼在OC中的處理方法如下.它是WKScriptMessageHandler的代理方法.name和上方JS中的方法名相對應.

  • (void)addScriptMessageHandler:(id )scriptMessageHandler name:(NSString *)name;

18 JS調用OC代碼(WebViewJavascriptBridge)三

1. 設置 webViewBridge

_bridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView]; [_bridge setWebViewDelegate:self];

2. 註冊handler方法,需要和 前段協商好 方法名字,是供 JS調用Native 使用的。

[_bridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {

複製代碼

// OC調用 NSString *scanResult = @"www.baidu.com";

// js 回調傳參 responseCallback(scanResult); }];

3. OC掉用JS

[_bridge callHandler:@"testJSFunction" data:@"一個字符串" responseCallback:^(id responseData) { NSLog(@"調用完JS後的回調:%@",responseData); }];

19.OC調用JS代碼

// 直接運行 使用 NSString *jsStr = @"執行的JS代碼"; [webView stringByEvaluatingJavaScriptFromString:jsStr]; // 使用JavaScriptCore框架 #import <JavaScriptCore/JavaScriptCore.h>

  • (void)webViewDidFinishLoad:(UIWebView *)webView {

//獲取webview中的JS內容 JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; NSString *runJS = @"執行的JS代碼";

//準備執行的JS代碼 [context evaluateScript:runJS]; }

20. 遇到過BAD_ACCESS的錯誤嗎?你是怎樣調試的?

BAD_ACCESS 報錯屬於內存訪問錯誤,會導致程序崩潰,錯誤的原因是訪問了野指針(懸掛指針)。

設置全局斷點快速定位問題代碼所在行。 開啓殭屍對象診斷 Analyze分析 重寫object的respondsToSelector方法,現實出現EXEC_BAD_ACCESS前訪問的最後一個object。 Xcode 7 已經集成了BAD_ACCESS捕獲功能:Address Sanitizer。 用法如下:在配置中勾選✅Enable Address Sanitizer。

21.什麼是函數式編程?鏈式

函數式編程是一種編程模型,他將計算機運算看做是數學中函數的計算,並且避免了狀態以及變量的概念。函數式編程就像流水線一樣,一順順的把問題解決完,從一個起點開始,一個個的調用函數,因爲上一個函數有返回值是工具類本身,所以一個函數執行完之後,可以用上一個函數繼續調用,有點鏈式思維在裏面。 Masonry 就是我們最常見的函數式編程,通過對象.方法1().方法2…..

[view mas_makeConstraints:^(MASConstraintMaker *make){
make.top.bottom.left.right.equalTo(self.view); }]; 鏈式編程?

top.bottom.left.right.equalTo(self.view)通過”.”語法,將需要執行的代碼連續的書寫,就叫做鏈式編程,它使得代碼簡單易懂。

22. 響應式編程?

響應式編程是一種面向數據流和變化傳播的編程範式。 例如,在命令式編程環境中,a:=b+c表示將表達式的結果賦給a,而之後改變b或c的值不會影響a。但在響應式編程中,a的值會隨着b或c的更新而更新。 Reactive Cocoa就是一個響應式編程的經典作品!

23.Block和Protocol的區別,Block是爲了解決什麼問題而使用的。

“代理和block的共同特性是回調機制,不同的是,代理的方法比較多,比較分散,公共接口,方法較多也選擇用delegate進行解耦 使用block的代碼比較集中統一,異步和簡單的回調用block更好” Block是爲了解決什麼問題而使用的? 個人認爲:

block爲了多線程之間調度產生的; block 也是一個OC對象,可以當參數傳遞,使用方便簡單,靈活,很少的代碼就可以實現代碼回調.比協議省很多代碼

24.說一下ios代碼簽名

確保從app store下載的app是沒被惡意篡改,如果修改則無法安裝, 以及驗證app開發者身份;

25.什麼是app thinning(app 瘦身)

App Thinning可以譯成“應用瘦身”。指的是App store 和操作系統在安裝iOS或者watchOS的 app 的時候通過一些列的優化,儘可能減少安裝包的大小,使得 app 以最小的合適的大小被安裝到你的設備上。而這個過程包括了三個過程:slicing, bitcode, and on-demand resources。

slicing 可以打包對應的 app 資源文件 Bitcode 蘋果會對包含Bitcode的二進制app進行二次優化,而不需要提交一個新的app版本到app store中。 On-Demand Resources 按需加載

26. 如果沒有instruments,該如何檢測memory leak, zombie object 之類的問題。

查看MLeaksFinder源碼分析,國內三方

27.字典的工作原理 ?怎100w箇中是怎麼快速去取value?

NSDictionary(字典)是使用 hash表來實現key和value之間的映射和存儲的,hash函數設計的好壞影響着數據的查找訪問效率。 (void)setObject:(id)anObject forKey:(id )aKey; Objective-C 中的字典 NSDictionary 底層其實是一個哈希表,實際上絕大多數語言中字典都通過哈希表實現,

哈希表的本質是一個數組,數組中每一個元素稱爲一個箱子(bin),箱子中存放的是鍵值對。數組長度即箱子數。 哈希表還有一個重要的屬性: 負載因子(load factor),它用來衡量哈希表的 空/滿 程度,一定程度上也可以體現查詢的效率,計算公式爲: 負載因子 = 總鍵值對數 / 箱子個數 負載因子越大,意味着哈希表越滿,越容易導致衝突,性能也就越低。因此,一般來說,當負載因子大於某個常數(可能是 1,或者 0.75 等)時,哈希表將自動擴容。 參考: www.jianshu.com/p/88dfc8f40…

28. isEquel和hash的關係

isEquel 用於比較2個對象是否相等, 與==(地址比較)不一樣, 可以重寫isEquel方法,來進行2個對象的比較 hash 是一個類方法,任何類都可以調用這個方法,返回的結果是一個NSInteger值(如果兩個對象相等,那麼他們的hash值一定相等,但是,如果兩個對象的哈希值相等是不能一定推出來這兩個對象是相等的)

29.isEquel 和 isEquelToString

isEquel 比較的是2個NSObject的方法, isEquelToString是比較2個字符串值是否相等 isEquel 首先比較2個對象地址,如果相同就返回YES,如果不同,就比較對象類型,以及屬性的值,一般重寫 isEquel 來比較2個對象

30. iOS 9 以後通知不再需要手動移除

通知 NSNotification 在註冊者被回收時需要手動移除,是一直以來的使用準則。 原因是在 MRC 時代,通知中心持有的是註冊者的 unsafe_unretained 指針,在註冊者被回收時若不對通知進行手動移除,則指針指向被回收的內存區域,變爲野指針。此時發送通知會造成 crash 。 而在 iOS 9 以後,通知中心持有的是註冊者的 weak 指針,這時即使不對通知進行手動移除,指針也會在註冊者被回收後自動置空。因爲向空指針發送消息是不會有問題的。

31. 如何對 NSMutableArray 進行 KVO

一般情況下只有通過調用 set 方法對值進行改變纔會觸發 KVO。但是在調用NSMutableArray的 addObject或removeObject 系列方法時,並不會觸發它的 set 方法。所以爲了實現NSMutableArray的 KVO,官方爲我們提供瞭如下方法:

@property (nonatomic, strong) NSMutableArray *arr; //添加元素操作 [[self mutableArrayValueForKey:@"arr"] addObject:item]; //移除元素操作 [[self mutableArrayValueForKey:@"arr"] removeObjectAtIndex:0];

32. 編譯過程做了哪些事情;

  • Objective,Swift都是編譯語言。編譯語言在執行的時候,必須先通過編譯器生成機器碼,機器碼可以直接在CPU上執行,所以執行效率較高。Objective,Swift二者的編譯都是依賴於Clang + LLVM. OC和Swift因爲原理上大同小異,知道一個即可!
  • iOS編譯 不管是OC還是Swift,都是採用Clang作爲編譯器前端,LLVM(Low level vritual machine)作爲編譯器後端。
  • 編譯器前端 :編譯器前端的任務是進行:語法分析,語義分析,生成中間代碼(intermediate representation )。在這個過程中,會進行類型檢查,如果發現錯誤或者警告會標註出來在哪一行
  • 編譯器後端 :編譯器後端會進行機器無關的代碼優化,生成機器語言,並且進行機器相關的代碼優化。LVVM優化器會進行BitCode的生成,鏈接期優化等等,LLVM機器碼生成器會針對不同的架構,比如arm64等生成不同的機器碼。

33. 容錯處理你們一般是注意哪些?

在團隊協作開發當中,由於每個團隊成員的水平不一,很難控制代碼的質量,保證代碼的健壯性,經常會發生由於後臺返回異常數據造成app崩潰閃退的情況,爲了避免這樣的情況項目中做一些容錯處理,顯得格外重要,極大程度上降低了因爲數據容錯不到位產生崩潰閃退的概率。

例如: 1.字典 2.數組; 3.野指針; 4.NSNull 等~ // AvoidCrash github 三方不錯

34. 項目開始容錯處理沒做?如何防止攔截潛在的崩潰?

例: 1、category給類添加方法用來替換掉原本存在潛在崩潰的方法。 2、利用runtime方法交換技術,將系統方法替換成類添加的新方法。 3、利用異常的捕獲來防止程序的崩潰,並且進行相應的處理。 4、使用 @try__Catch__方法進行攔截

總結:

1、不要過分相信服務器返回的數據會永遠的正確。

2、在對數據處理上,要進行容錯處理,進行相應判斷之後再處理數據,這是一個良好的編程習慣。

35.@try @catch異常機制

Objective-C 異常機制 : — 作用 : 開發者將引發異常的代碼放在 @try 代碼塊中, 程序出現異常 使用 @catch 代碼塊進行捕捉; — 每個代碼塊作用 : @try 代碼塊存放可能出現異常的代碼, @catch 代碼塊 異常處理邏輯, @finally 代碼塊回收資源; — 語法示例 :

try{ //..執行的代碼,其中可能有異常。一旦發現異常,則立即跳到catch執行。否則不會執行catch裏面的內容 }catch(){ //...除非try裏面執行代碼發生了異常,否則這裏的代碼不會執行 }finally{ //..不管什麼情況都會執行,包括try catch 裏面用了return ,可以理解爲只要執行了try或者catch,就一定會執行 finally } 可以用於查找 bug,或者調試,防止崩潰使用

36.單元測試是什麼?

單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。

1. 單元測試可以進行 邏輯測試/異步測試/性能測試

2. 單元測試是以代碼測試代碼。不是靠 NSLog 來測試,而是使用斷言來測試的,提前預判條件必須滿足。XCTAssert(條件, 不滿足條件的描述)

3. 可以在單元測試類中編寫單獨的測試用例方法。這些方法與普通的方法類似,但是方法名稱必須以 test 開頭,且不能有參數,不然不會識別爲測試方法。

4. 不是所有的方法都需要測試。例如私有方法不需要測試,只有暴露在 .h 中的方法需要測試。

一般而言,代碼的覆蓋度大概在 50% ~ 70%。從 github 上得知:YYModel 測試覆蓋度爲 83%,AFNetworking 測試覆蓋度爲 77%,兩者都是比較高的。

總結: 單元測試可以根據項目需要,針對一些關鍵業務,編寫一些測試用例,可以方便的排查業務邏輯可能出現的問題.在後續改動時候也可以方便的測試等等.

37. 一個上線的項目,知道這個方法可能會出問題,在不破壞改方法前提下,怎麼搞?

做一些容錯處理,防止崩潰 加一些日誌收集,收集問題再具體分析 try_catch

38.Xcode編譯器發展簡史

Xcode3 以前: GCC; Xcode3: 增加LLVM,GCC(前端) + LLVM(後端); Xcode4.2: 出現Clang - LLVM 3.0成爲默認編譯器; Xcode4.6: LLVM 升級到4.2版本; Xcode5: GCC被廢棄,新的編譯器是LLVM 5.0,從GCC過渡到Clang-LLVM的時代正式完成

39.代碼從 Git 上拉下來到生成 .ipa 都有哪些過程,期間都生成了什麼文件。

git clone 遠程地址到本地 pod 三方集成 配置證書信息,簽名 打包 ipa

40.Pods的原理

簡單理解:快速的搜索多第三方框架,然後自動集成多工程裏面。並編譯成一個libPod.a的靜態庫給我們的項目用。

41. 函數指針和 Block區別

相同點:

二者都可以看成是一個代碼片段。 函數指針類型和 Block 類型都可以作爲變量和函數參數的類型 不同點:

函數指針只能指向預先定義好的函數代碼塊,函數地址是在編譯鏈接時就已經確定好的。從內存的角度看,函數指針只不過是指向代碼區的一段可執行代碼 block 本質是 OC對象,是 NSObject的子類,是程序運行過程中在棧內存動態創建的對象,可以向其發送copy消息將block對象拷貝到堆內存,以延長其生命週期。

42. 符號表

iOS 構建時產生的符號表,是內存地址、函數名、文件名和行號的映射表。格式大概是:

<起始地址> <結束地址> <函數> [<文件名:行號>] Crash 時的堆棧信息,全是二進制的地址信息。如果利用這些二進制的地址信息來定位問題是不可能的,因此我們需要將這些二進制的地址信息還原成源代碼種的函數以及行號,這時候符號表就起作用了。利用符號表將原始的 Crash 的二進制堆棧信息還原成包含行號的源代碼文件信息,可以快速定位問題。iOS 中的符號表文件(DSYM) 是在編譯源代碼後,處理完 Asset Catalog 資源和 info.plist 文件後開始生成,生成符號表文件(DSYM)之後,再進行後續的鏈接、打包、簽名、校驗等步驟。

43. 應用瘦身(Thinning)

App Thinning“應用瘦身”,iOS9之後發佈的新特性。它能對App store 和操作系統在安裝iOS app 的時候通過一些列的優化,儘可能減少安裝包的大小,使得 app 以最小的合適的大小被安裝到你的設備上。而這個過程包括了三個過程: slicing, bitcode, on-demand resources,

  • slicing

appStore 會根據用戶的設備型號創建相應的應用變體,這些變體只包含可執行的結構和資源必要部分,不需要用戶下載完整的安裝包

  • bitcode

bitcode系統會對編譯後的二進制文件進行二次優化, 使用最新的編譯器自動編譯app並且針對特定架構進行優化。不會下載應用針對不同架構的優化,而僅下載與特定設備相關的優化,使得下載量更小,

  • On Demand Resources

按需加載資源是在 app 第一次安裝後可下載的文件。舉例說明,當玩家解鎖遊戲的特定關卡後可以下載新關卡(和這個關卡相關的特定內容)。此外,玩家已經通過的關卡可以被移除以便節約設備上的存儲空間。 ##44.埋點處理 埋點是什麼? 用戶行爲統計,俗稱埋點

埋點分爲兩種:

頁面統計,即在進入頁面和離開頁面的時候埋點,統計停留頁面時長 交互事件統計 無痕埋點(自動埋點)解決方案: 技術原理:Method-Swizzling

對於一個給定的事件,UIControl會調用sendAction:to:forEvent:來將行爲消息轉發到UIApplication對象,再由UIApplication對象調用其sendAction:to:fromSender:forEvent:方法來將消息分發到指定的target上,那麼,我們寫一個UIControl的類別,通過替換它的sendAction:to:forEvent:方法,結合本地配置的埋點json或者plist文件(若埋點需要額外的參數,需要給UIControl的類別通過Runtime添加屬性),便可以實現自動埋點的功能。

如果你正在跳槽或者正準備跳槽不妨動動小手,添加一下咱們的交流羣1012951431來獲取一份詳細的大廠面試資料爲你的跳槽多添一份保障。

文末推薦:iOS熱門文集

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章