iOS跳槽寶典-面試技術基礎篇

Mars_iOS發表於2018-03-22

序言

又是一年金三銀四時,相信很多人會選擇這個時機考慮跳槽,但又對輪輪面試望而生畏。其實面試只是對個人技術及應變能力的一次考驗,通常面試第一輪為技術面試,二面一般是跟HR交流,進一步瞭解你(嚴格的大公司還會有三面、四面)。第一輪技術面試中遇到的問題通常都是一些基礎性的知識,平時可能忙於夜以繼日的工作而疏於整理和總結,匆忙去面試可能被面試官層層緊逼的提問問的暈頭轉向,或者筆試題答的一塌糊塗。技術過關後忠厚老實的你在二面時察覺不到HR問題的意圖,答非所問最終敗下陣來或者被HR硬生生的往下壓了千元預期薪資。

跳槽不是簡單的找工作或換一家公司,是一個雙向選擇的機會。如何將自己完美的呈現出來從而獲得一個自己滿意的offer?整理一些自己經歷的、看到的問題,與君共勉。

技術基礎篇

1、為什麼說Objective-C是一門動態的語言?

靜態、動態是相對的,這裡引用一下網上的定義:

①動態型別語言:動態型別語言是指在執行期間才去做資料型別檢查的語言,也就是說,在用動態型別的語言程式設計時,永遠也不用給任何變數指定資料型別,該語言會在你第一次賦值給變數時,在內部將資料型別記錄下來。

②靜態型別語言:靜態型別語言與動態型別語言剛好相反,它的資料型別是在編譯其間檢查的,也就是說在寫程式時要宣告所有變數的資料型別,C/C++是靜態型別語言的典型代表

Objective-C是C的超集,在C語言的基礎上新增了物件導向的特性,可以通過Runtime執行時機制在執行時動態的新增變數、方法、類等,所以說OC是一門動態語言。

延伸

編譯時與執行時

編譯時:即編譯器對語言的編譯階段,編譯時只是對語言進行最基本的檢查報錯,包括詞法分析、語法分析等。將程式程式碼翻譯成計算機能夠識別的語言,編譯通過並不意味程式就可以執行成功。

執行時:即程式通過了編譯這一關之後編譯好的程式碼被裝載到記憶體中跑起來的階段。這個時候會具體對型別進行檢查,而不是僅僅對程式碼進行簡單的掃描分析,此時如果出錯的話程式就會崩潰。

可以說編譯時是一個靜態的階段,型別錯誤很明顯的可以直接檢查出來,可讀性好;而執行時則是動態的階段,開始具體與執行環境結合起來。

Objective-C語言的動態性

OC語言的動態性主要體現在三個方面:動態型別(Dynamic typing)、動態繫結(Dynamic binding)、動態載入(Dynamic loading)。

<一>、動態型別

動態型別指的是物件指標型別的動態性。具體是指使用id任意型別將物件的型別確定推遲到執行時,由賦給它的物件型別來決定物件指標型別。另外型別確定推遲到執行時之後,可以通過NSObject的isKindOfClass方法動態的判斷物件最後的型別(動態型別識別)。也就是說id任意型別修飾的物件為動態型別物件,其他在編譯器指明型別的物件為靜態物件。

示例:

對於語句 NSString* testObject = [[NSData alloc] init];  testObject在編譯時和執行時分別是什麼型別的物件?

首先,testObject是一個指向某個物件的指標,不論何時指標的空間大小都是固定的
編譯時:指標的型別為NSString,即編譯時會被當成一個NSString例項來處理,編譯器在型別檢查時如果發現型別不匹配就會給出黃色警告。該語句給指標賦值用的是一個NSData物件,則編譯時編譯器會給出型別不匹配警告。但是編譯時如果testObject呼叫NSString的方法,編譯器會認為是正確的,既不會警告也不會報錯

執行時:執行時指標指向的實際是一個NSData物件,因此如果指標呼叫了NSString的方法,雖然編譯時通過額,但執行時也會崩潰,因為NSData物件沒有該方法。另外雖然執行時指標實際指向的是NSData,但編譯時編譯器並不知道,因此如果試圖用這個指標呼叫NSData的方法會直接編譯不通過,給出紅色報錯程式也執行不起來。

下面給出測試示例:

 // 1.編譯時編譯器認為testObject是一個NSString物件,這裡賦給它一個NSData物件編譯器給出黃色型別錯誤警告,但執行時卻是指向一個NSData物件
    NSString* testObject = [[NSData alloc] init];
 // 2.編譯器認為testObject是NSString物件,所以允許其呼叫NSString的方法,這裡編譯通過無警告和錯誤
    [testObject stringByAppendingString:@"string"];
  // 3.但不允許其呼叫NSData的方法,下面這裡編譯不通過給出紅色報錯
    [testObject base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];

將上面第三句編譯不通過的註釋掉,然後在第二句打斷點,編譯後跑程式到斷點後會看到testObject指標型別是_NSZeroData物件。繼續執行程式會崩潰,因為NSData物件沒有NSString的stringByAppendingString這個方法。

動態型別測試

如果我們假設testObject是id型別會怎麼樣呢?

 // 1.id任意型別,編譯器就不會把testObject在當成NSString物件了
    id testObject = [[NSData alloc] init];
    // 2.呼叫NSData的方法編譯通過
    [testObject base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength];
    // 3.呼叫NSString的方法編譯也通過
    [testObject stringByAppendingString:@"string"];

結果是編譯完全通過,編譯時編譯器把testObject指標當做任意型別,執行時才確定testObject為NSData物件,因此執行NSData的方法正常。但是執行NSString的方法時還是崩潰了。通過這個例子我們就可以很清楚的知道id型別的作用了,將型別的確定推遲到執行時,這就提現了OC的一種動態性:動態型別。

延伸

動態型別的識別方法(面嚮物件語言的內省Introspeciton特性)

1.Class型別:

  • Class class = [NSObject class];//通過類名得到對應的Class動態型別
  • Class class = [obj class];//通過例項物件得到對應的Class動態型別
  • if([obj1 class] == [obj2 class])//判斷是不是相同型別的例項

2.Class動態型別和類名字串的相互轉換:

  • NSClassFromString(@”NSObject”);//由類名字串得到Class動態型別
  • NSStringFromClass([NSObject class]);//由類名的動態型別得到類名字串
  • NSStringFromClass([obj class]);//由物件的動態型別得到類名字串

3.判斷物件是否屬於某種動態型別:

  • -(BOOL)isKindOfClass:class//判斷某個物件是否是動態型別class的例項或其子類的例項
  • -(BOOL)isMemberOfClass:Class//只判斷某個物件是否是class型別的例項,不考慮其子類

4.判斷類中是否有對應的方法:

  • -(BOOL)respondsToSelector:(SEL)selector//類中是否有這個類方法
  • -(BOOL)instancesRespondToSelector:(SEL)selector // 類中是否有這個例項方法

5.方法名字串和SEL型別的轉換

在編譯時,編譯器會根據方法的名字和引數序列生成唯一標識方法的ID,這個ID為SEL型別。到了執行時編譯器通過SEL型別的ID來查詢對應的方法,放法的名字和引數序列相同,那麼它們的ID就都是相同的。另外可以通過@select()指示符獲得方法的ID。常用方法如下:

SEL funcID = @select(func);// 這個註冊事件回撥時常用,將方法轉成SEL型別
SEL funcID = NSSelectorFromString(@"func"); // 根據方法名得到方法標識
NSString *funcName = NSStringFromSelector(funcID); // 根據SEL型別得到方法名字串

<二>、動態繫結

動態繫結指的是方法確定的動態性。具體指的是利用OC的訊息傳遞機制將要執行的方法的確定推遲到執行時,可以動態新增方法。也就是說一個OC物件是否呼叫某個方法不是由編譯器決定的,而是由執行時決定的。另外,關於動態繫結的關鍵一點是基於訊息傳遞機制的訊息轉發機制,主要處理應對一些接受者無法處理的訊息,此時有機會將訊息轉發給其他接收者處理。

動態繫結是基於動態型別的,在執行時物件的型別確定後,那麼物件的屬性和方法也就確定了(包括類中原來的屬性和方法以及執行時動態新加入的屬性和方法),這也就是所謂的動態繫結了。動態繫結的核心就是在執行時動態的為類新增屬性和方法,以及方法的最後處理和轉發,主要用法C語言語法,因為涉及到執行時,因此要引入執行時標頭檔案#include <objc/runtime.h>

延伸

訊息傳遞機制

在OC中,方法的呼叫不再理解為物件呼叫起方法,而是要理解成物件接受訊息,訊息的傳送採用動態繫結機制,具體會呼叫哪個方法直到執行時才能確定。確定之後才回去執行繫結的程式碼。方法的呼叫實際就是告訴物件要幹什麼,給物件傳送一個訊息,物件為接收者(receiver),呼叫的方法及其引數即訊息(message),給一個物件傳訊息表達為:[receiver message];接收者的型別可以通過動態型別識別,於執行時確定。

在訊息傳遞機制中,當開發者編寫[receiver message];語句傳送訊息後,編譯器都會將其轉換成對應的一條objc_msgSend C語言訊息傳送原語,具體格式為:void objc_msgSend (id self, SEL cmd, ...)

這個原語函式引數可變,第一個引數是訊息的接收者,第二個引數是訊息的選擇子,後面跟著可選的訊息的引數。有了這些引數,objc_msgSend就可以通過接收者的isa指標,到其類物件中的方法列表中以選擇子的名稱為尋找對應的方法,找到則轉發哦其實現程式碼執行,找不到則繼續根據繼承關係從父類中尋找。如果找到了跟類還是無法找到對應的方法,說明該接收者物件無法響應該訊息,則會出發訊息轉發機制,給開發者最後一次挽救程式的機會

<三>、動態載入

動態載入主要包括兩個方面,一個是動態載入資源,一個是一些可執行程式碼模組的載入。這些資源在執行時根據需要動態的選擇性的加入到程式中,是一種程式碼和資源的“懶載入“模式,可以降低記憶體需求,提高整個程式的效能。

相關文章