招一個靠譜的iOS實習生(附參考答案)

以下是我列出來的能夠幫助大家招到一個 靠譜的iOS實習生 需要掌握的點,再次說明下情況:

  1. 此份題適用於電面和face to face,更加偏向於電面;
  2. 能夠較為流暢的說到每道題的點上,基本上可以認為是掌握了;
  3. 考慮到電面過程中,對被電面者心理素質考驗非常大,所以,我本人抵制電面過程中考演算法(這是一個流氓行為)此套題不涉及任何關於演算法方面知識。若有此需求,推薦找專門的在線OJ進行測評。

概念部分

struct和class的區別?

  • struct中不能定義函數(針對面向過程語言,例如C)// 可不用
  • 使用大括弧進行初始化 class和struct如果定義了構造函數,就不能用大括弧初始化,若沒有,則struct可以,class只有在所有成員變數均為public時才行。 // 可不用
  • 默認訪問許可權 class默認成員訪問許可權為private;struct默認訪問你許可權為public。 // 重點
  • 繼承方式 class默認private;struct默認public。 // 重點

說出以下指針的含義:

int **a : 指向一個指針的指針,該指針指向一個整數。int *a[10] : 指向一個有10個指針的數組,每個指針指向一個整數。int (*a)[10] : 指向一個有10個整數數組的指針。int (*a)(int) : 指向一個函數的指針,該函數有一個整數參數,並返回一個整數。

int 和 NSInterger 的區別:

  • 以C語言舉例,int和long的位元組數和當前操作系統中的指針所佔位數是相等的,也就是說,long的長度永遠 ≥ int,並且我們需要去考慮此時是使用int還是long比較合適,會不會因為一時疏忽選擇了int而導致位數不夠造成溢出。
  • 而在OC中使用 NSInterger ,蘋果對其進行了一個宏定義的判斷(cmd+滑鼠左鍵進去看吧),這個宏定義會自動判斷當前App運行的硬體環境,到底是32位機還是64位機等等,從而自動返回最大的類型,而不用我們去思考此時到底應該是用int還是long。

深拷貝和淺拷貝的區別:

  • 淺拷貝:又稱「指針拷貝」。不增加新內存,只增加一個指針指向原來的內存區域。
  • 深拷貝:又稱「內容拷貝」。同時拷貝指正和指針所指向的內存,新增指針指向新增內存。
  • // 可對OC中的可變對象和不可變對象做拓展,此問題只是單純的概念。

內存中的區域是怎麼劃分的?

  • 用之前做的一張圖進行描述:

語言部分

nil、Nil、NULL和NSNULL的區別:

  • nil: 把對象置空,置空後是一個空對象且完全從內存中釋放;
  • Nil: 用nil的地方均可用Nil替換,Nil表示置空一個類;
  • NULL: 表示把一個指針置空。(空指針)
  • NSNULL: 把一個OC對象置空,但想保留其容器(大小)。

category和extension的區別:

  • category:為已知類增加新方法。

    • 新增方法被子類集成;
    • 新增的方法比原有類具備更高的優先順序,且不可重名,防止被覆蓋;
    • 不能增加成員變數。
  • extension: 為當前類增加私有變數和私有方法,添加的方法是必須實現的。

@Property關鍵詞及其相關關鍵字的理解:

  • 根據被修改的可能性,、@Property中關鍵字的排列推薦為:原子性、讀寫性、內存管理特性;
  • 原子性: automatic和nonautomatic。決定了該屬性是否為原子性的,即在多線程的操作中,不能被其它線程打斷的特性,一旦使用了該變數的操作不能被完整執行時,將會回到該變數操作之前的狀態,但原子性即automatic因為是原語操作(保證setter/getter的原語執行),會損耗性能,在iOS開發中一般不用,而在macOS開發中隨意。
  • 讀寫性: readOnly和readWrite。默認為readWrite,編譯器會幫助生成serter/getter方法,而readOnly只會幫助生成getter方法。 // 此處可拓展,非要修改readOnly修飾的變數怎麼辦,可用KVC,又可繼續拓展KVC相關知識。
  • 內存管理特性: assign、weak、strong、unsafe_unretained。

    • assign:一般用於值類型,比如int、BOOL等(還可用於修飾OC對象);
    • weak:用於修飾引用類型(弱引用,只能修飾OC對象);
    • strong:用於修飾引用類型(強引用);
    • unsafeunretained:只用於修飾引用類型(弱引用),與weak的區別在於,被unsafeunretained修飾的對象被銷毀後,其指針並不會被自動置空,此時指向了一個野地址。

OC中如何定義一個枚舉?

  • 在OC中定義一個枚舉有三種做法:
    • 因為OC是兼容C的,所以可以使用C語言風格的enum進行定義。
    • 使用NS_ENUM宏進行定義;
    • 使用NS_OPTIONS宏進行定義;
  • NS_ENUM為定義通用性枚舉,只能單選,NS_OPTIONS為定義位移枚舉,可多選。 // 枚舉為啥要這麼分?因為涉及到是否使用C++模式進行編譯有關。

Block和函數的關係(對Block的理解)?

  • Block與函數指針非常類似,但Block能夠訪問函數以外、詞法作用域以外的外部變數的值;
  • Block不僅實現了函數的功能,還攜帶了函數的執行環境;
  • Block實際上是指向結構體的指針;(可參考這篇文章)
  • Block會把進入其內部的基本數據類型變數當做常量處理。】
  • Block執行的是一個回調,並不知道其中的對象合適被釋放,所以為了防止在使用對象之前就被釋放掉了,會自動給其內部所使用的對象進行retain一次。
  • Block使用copy修飾符進行修飾,且不能使用retain進行修飾,因為retain只是進行了一次回調,但block的內存還是放在了棧空間中,在棧上的變數隨時會被系統回收,且Block在創建的時候內存默認就已經分配在棧空間中,其本身的作用域限於其創建時,一旦在超出其創建時的作用域之外使用,則會導致程序的崩潰,故使用copy修飾,使其拷貝到堆空間中,block有時還會用到一些本地變數,只有將其copy到堆空間中,才能使用這些變數。

deletegate需要weak修飾的原因?

  • 以圖說明,圖中所表示的是VC對tableView的持有,如果此時的tableView.deletegate對VC也是強引用,會導致循環引用,同時也給了我們敲了警鐘,當出現兩個對象都是強引用時,萬分小心!

解釋一下這段代碼的輸出:

  • 簡寫:

Computer : NSObjectMac : Computer@implementation MacNSLog(@"%@", [self class]);NSLog(@"%@", [super class])@end

  • 二者都會輸出Mac。
    • [self class]:當使用self調用方法時,從當前類方法列表中找,若沒有則再去父類中找。調用[self class]時,會轉化成objc_msgSend函數,其定義為id objc_msgSend(id self, SEL op, ...),第一個參數是Mac實例,但其並無-(Class)class方法,此時去父類Computer中尋找,發現也沒有,再去其父類NSObject中找,找到了!返回的就是self其本身,可猜測其方法實現如下:

- (Class)class {

return object_getClass(self);

}

    • [super class]:從父類方法列表中開始找,調用父類方法。當調用[super class]時,轉換成objc_msgSendSuper函數,其定義為id objc_msgSendSuper(struct objc_super *super, SEL op, ...),而struct objc_super結構體的定義為:

struct objc_super {

__unsafe_unretained id receiver;

__unsafe_unretained Class super_class;

}

所以轉換成objc_msgSendSuper函數後,第一步要先去構造objc_super結構體,結構的第一個成員receiver就是self,第二個成員是(id)class_getSuperclass(object_getClass("Mac")),該函數輸出的結果為super_class值,即Computer,第二步,則去Computer類中去找- (Class)class,發現並未找到,接著去NSObject中找,找到了!最後是使用了objc_msgSend(objc_super->receiver, @selector(class))去調用了,這個時候已經跟之前的[self class]調用輸出結果重複了,返回結果還是Mac

iOS部分

UITableView性能調優的方法:

  • Cell重用:

    • 數據源方法優化:創建一個靜態變數重用ID,例如:static NSString *cellID = @"cellID";防止因為調用次數過多,static保證只創建一次,提高性能(感覺性能的提升可以忽略不記emmm)
    • 緩存池獲取可重用Cell的兩個方法:dequeueReusableCellWithIdentifier:(NSString *)ID會查詢可重用Cell,若註冊了原型Cell則能夠查詢到,否則為nil,故需要先判斷if(cell == nil)
    • dequeueReusableCellWithIdentifier:(NSString *)ID indexPath:(NSIndexPath *)indexPath,使用之前必須通過SB/class進行可重用Cell的註冊(registerNib/registerClass),不需要判斷nil,一定會返回cell,若緩衝區Cell不存在,會使用原型Cell重新實例化一個新Cell。
  • 盡量使用一種類型的Cell:能夠減少代碼量,減小Nib文件的數量;保證只有一種類型的Cell,實際上App運後只有N個Cell,但若有M種Cell,則實際上運行最多卻可能會是MxN 個。
  • 善用hidden隱藏subview:把所有不同類型的view都定義好,通過cell的枚舉類型變數及hidden顯示/隱藏不同類型的內容,因為在實際快速滑動中,單純的顯示/隱藏subview比實時創建快得多。
  • 提前計算並緩存Cell的高度。如果我們不預估行高,則優先調用heightForRowAtIndexPath獲取每個Cell即將顯示的高度,實際上就是要確定總的tableView.contenSize,最後才又接著調用cellForRowAtIndexPath,可以建一個frame模型,保存下提前計算好的cell高度。
  • 非同步繪製:這是目前最火的tableView性能調優方法,新浪微博是這麼做的,可以使用ASDK這個庫進行。
  • tableView滑動時,按需載入:識別tableView靜止或減速滑動結束後,非同步載入,在快速滑動過程中,只按需載入目標方位內的Cell。
  • 避免大量使用圖片縮放、顏色漸變、透明圖層、CALayer特效(陰影)等操作,盡量顯示大小剛好合適的圖片資源。

內存優化方案:

  • 首先ARC。但要注意防止循環引用,避免內存泄露;
  • 懶載入。延遲創建對象,用時再創建;
  • 復用。比如tableView、collectionView單元格的復用;
  • 巧妙使用單例,而不是全都使用單例!

單例的寫法?

static User *user;+ (User *)shareInstance { if (user == nil) { @synchronized(self) { // 加鎖 user = [User alloc] init]; } } return user;}+ (User *)shareInstance { static dispatch_onec_t onecToken; dispatch_onece(&onceToken, ^{ user = [User alloc] init; }) return user;}

iOS的遠程推送過程?

  • 以圖講解:

iOS中多線程的概念:(單問概念)

  • 多線程優點:提高程序執行效率。缺點:開啟線程需要一定的內存控制項。
  • 同步和非同步:決定了要不要開啟新的線程。同步:在當前線程中執行任務,不具備開啟新線程能力;非同步:在新線程中執行任務,具備開啟新線程的能力。
  • 並行和串列:決定了任務的執行方式。並行:多個任務並發(同時)執行,類似迅雷多任務同時下載;串列:一個任務執行完畢後,再執行下一個任務,類似一個一個下載。
  • 重點: 必須要明確iOS中只有一個主線程——UI線程,且不可將耗時任務放在主線程執行,否則會造成卡頓。

總結:再次說明,實際電面過程中,本套題只可作為參考,而且在電面過程中會有追問和等待的過程,所以最佳電面時間為三十分鐘內最佳,嫌短?記好了!這招的是實習生,而且這還是電面!!!別學啥大公司的戾氣,一個實習生的電面你要搞一個小時甚至快兩個小時,沒有必要。如果是face to face隨你問一兩個小時,但重點是這是電面!不要把這種「隔空喊話」的面試方式作為考核一個人是否具備對應的工作能力的最終標準,除非能夠確定以後的工作方式就是「隔空喊話」和「隔空擼碼」。

也不推薦把本套題中的內容作為一面,一面應該是作為了解應聘者的職業狀況,基礎水平(也是就是邏輯能力),推薦本套題作為二面,涵蓋基本的iOS開發基礎知識,而且時間能夠較好的把握在三十分鐘內,三面應該側重項目實際情況,而且最好face to face。四面就HR面了吧,盡量不要有五面了,太難受了。其實三面是最佳的。

記住!不要套題,而應該是觸類旁通,引出其它問題。

推薦閱讀:

TAG:iOS開發 | 實習 | 移動應用 |