歡迎光臨
每天分享高質量文章

2018收集iOS筆試題(僅供參考學習使用)

作者:翀鷹女孩

鏈接:https://www.jianshu.com/p/ac44602d7dab


第一波:

1.jpg


自答:

說實話,剛剛看到這套面試題,我覺得還是很難的,這是要招聘大牛的節奏啊…好吧,菜鳥一枚的我來試著回答一下.

1.什麼是函式式編程?

說起鏈式編程和函式式編程,小伙伴們千萬不要緊張。

聽著很高大尚,其實也就那麼回事。相信有過swift/C#開發經驗的,或者其他編程經驗的,只要不是OC,一看就知道。 通過一個小例子來解釋.

看兩行代碼:


 Person *person = [[Person alloc] init];
 person.run(9.2).eat(@"香蕉").run(1.2).eat(@"麵條");


上面的就是鏈式編程+函式式編程.


來個大白話解釋:看到括號裡面的引數了吧,跟swift的函式呼叫是不是很相似,包括別的語言,都用小括號傳參,只有OC是冒號傳參。

再看方法呼叫用的是”.”,而OC用的是[]+空格。

這幾個方法呼叫,如果要按OC的打法,估計要整4行,物件一個一個的呼叫方法,但鏈式就是這麼一行搞定.

分析一下,因為Block可以通過()來傳值,我們推斷run(para)和eat(para)這兩個方法,肯定傳回值是一個Block,而且是帶一個引數的Block

2.什麼是ABI?

嗚嗚~~~(>_


3.什麼是MVC,請結合CocoaTouch說明?

M: model V:View C:controller O(∩_∩)O哈哈~ 自行展開,自圓其說吧.


4.什麼是MVVM,請設計View moled需要考慮哪些?

M: model V:View VM:ViewModel是View和Model之間的中介

MVVM的出現主要是為瞭解決在開發過程中Controller越來越龐大的問題,變得難以維護,所以MVVM把資料加工的任務從Controller中解放了出來,使得Controller只需要專註於資料調配的工作,ViewModel則去負責資料加工並通過通知機制讓View響應ViewModel的改變。

MVVM是基於胖Model的架構思路建立的,然後在胖Model中拆出兩部分:Model和ViewModel。ViewModel本質上算是Model層(因為是胖Model裡面分出來的一部分),所以View並不適合直接持有ViewModel,因為ViewModel有可能並不是只服務於特定的一個View,使用更加鬆散的系結關係能夠降低ViewModel和View之間的耦合度。

其實MVVM是一定需要Controller的參與的,雖然MVVM在一定程度上弱化了Controller的存在感,並且給Controller做了減負瘦身(這也是MVVM的主要目的)。但是,這並不代表MVVM中不需要Controller.嚴格來說MVVM其實是MVCVM。從中可以得知,Controller夾在View和ViewModel之間做的其中一個主要事情就是將View和ViewModel進行系結。在邏輯上,Controller知道應當展示哪個View,Controller也知道應當使用哪個ViewModel,然而View和ViewModel它們之間是互相不知道的,所以Controller就負責控制他們的系結關係,所以叫Controller/控制器就是這個原因。


5.swift相對於OC有哪些優點?

Swift容易閱讀

Swift更容易維護

Swift更加安全

Swift代碼更少

Swift速度更快


6.什麼是泛型,swift在哪些地方使用了泛型?

兩個整型數相加和兩個浮點數相加的程式看起來應該非常類似,甚至一模一樣才對。唯一的區別就是變數的型別不同。

在強型別語言中,你需要去定義諸如addInts, addFloats, addDoubles 等方法來正確地處理引數及傳回值.

例如:


 func swapTwoValue<T>(a: inout T, b: inout T){  
     let tempValue = a  
     a = b  
     b = tempValue  
 } 


這個函式用 T 占位符來代替實際的型別。並沒有指定具體的型別,但是傳入的a ,b 必須是同一型別T。在呼叫這個函式的時候才能指定 T 是那種具體的型別。

7.defer、guard的作用?

defer 譯為延緩、推遲之意

比如,讀取某目錄下的檔案內容並處理資料,你需要首先定位到檔案目錄,打開檔案夾,讀取檔案內容以及處理資料,關閉檔案以及檔案夾。倘若一切順利,只需按照設定好的程式流程走一輪即可;不過考慮事情要面面俱到,倘若中間某個環節失敗,比如讀取檔案內容失敗、處理資料失敗等等,還需要進行一些後續收尾工作,即關閉檔案或關閉檔案夾(當然就算順利執行,也是要關閉的)。


func doSomethingWithDefer(){
 // 1 
openDirectory() 
// 2 
defer{closeDirectory()} 
// 3 
openFile() 
// 4 
defer{closeFile()} 
// 做其他雜七雜八事情… 
}

guard 有控制、警戒之意,語法簡單,只需兩個示例代碼即可明白。

// 這裡使用if 和 guard進行對比 你會懂的更多

if age < 13 {

return //當年齡小於13時 程式傳回 不進行之後的操作

}

guard 改寫

guard age >= 13 else{

return

}

可以看到代碼的意思是保證(guard)age值大於等於13 否則(else)傳回,不執行下麵程式。


8.swift語法糖?!的本質(實現原理)

相信大家在學習和使用Swift的時候,肯定會被 ! 和 ? 搞瘋過, 糾結這兩個符號到底是個什麼鬼 ?鬼知道什麼時候使用!,什麼時候使用?

? 和 ! 其實分別是Swift語言中對一種可選型別( Optional) 操作的語法糖。 那可選型別是乾什麼的呢? Swift中是可以宣告一個沒有初始值的屬性, Swift中引入了可選型別(Optional)來解決這一問題。它的定義是通過在型別生命後加加一個 ? 運算子完成的。

例如: var name: String?

Optional其實是個enum,裡面有None和Some兩種型別。其實所謂的nil就是Optional.None , 非nil就是Optional.Some, 然後會通過Some(T)包裝(wrap)原始值,這也是為什麼在使用Optional的時候要拆包(從enum里取出來原始值)的原因。


9.舉例swift中樣式匹配的作用?

Swift有一個很好的特性,那就是樣式匹配的擴展。樣式是用於匹配的規則值,如switch陳述句的case,do陳述句的catch子句,以及if、while、guard、for-in陳述句的條件。

假設你想判斷一個整數是大於、小於還是等於零,你可以用if-else if-else陳述句,儘管這並不美觀:


let x = 10
if x > 0 {
    print("大於零")
else if x 0 {
    print("小於零")
else {
    print("等於零")
}

switch陳述句會好很多,我理想的代碼是這樣:
// 偽代碼
switch x {
case > 0:
    print("大於零")
case 0:
    print("小於零")
case 0:
    print("等於零")
}


但樣式匹配預設並不支持不等式。所以我們要實現我們自己的~=

我們知道這個方法必須傳回一個Bool,那正是我們需要的,我們需要知道這個值是否匹配樣式。

  func greaterThan(a: T)(_ b: T) -> Bool {
    return b > a
}
func lessThan(a: T)(_ b: T) -> Bool {
    return b   }
這樣我們有了第一個版本的switch陳述句:
switch x {
case greaterThan(0):
    print("大於零")
case lessThan(0):
    print("小於零")
case 0:
    print("等於零")
default:
    fatalError("不會發生")
}

10.swift中clousure與OC中block的區別?


swift中的閉包傳值

clousure-1.jpg

clousure-2.png


clousure-3.png


OC中的閉包傳值

block.jpg


11.什麼是capture list,舉例說明用處?

寶寶心裡苦,這個寶寶也不知道,百度了還是不知道,希望大佬留言解答.~~~(>_


12.swift中private與fileprivate的區別?

1,private private 訪問級別所修飾的屬性或者方法只能在當前類里訪問。

(註意:Swift4 中,extension 里也可以訪問 private 的屬性。)

2,fileprivate fileprivate 訪問級別所修飾的屬性或者方法在當前的 Swift 源檔案里可以訪問。(比如上面樣例把 private 改成 fileprivate 就不會報錯了)

3,internal(預設訪問級別,internal修飾符可寫可不寫)

internal 訪問級別所修飾的屬性或方法在原始碼所在的整個模塊都可以訪問。

如果是框架或者庫代碼,則在整個框架內部都可以訪問,框架由外部代碼所取用時,則不可以訪問。

如果是 App 代碼,也是在整個 App 代碼,也是在整個 App 內部可以訪問

4,public 可以被任何人訪問。但其他 module 中不可以被 override 和繼承,而在 module 內可以被 override 和繼承。

5,open 可以被任何人使用,包括 override 和繼承。

總結

現在的訪問權限則依次為:open,public,internal,fileprivate,private。


13.REST、HTTP、JSON是什麼?

REST(Representational State Transfer)含狀態傳輸是一種軟體架構風格。

HTTP:網絡傳輸協議

JSON:是一種輕量級的資料交換格式


14.delegate解決了什麼問題,Notification與它有什麼不同?

區別:

  1. 效率肯定是delegate比nsnotification高。

  2. delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要關註傳回值

1)兩個模塊之間聯繫不是很緊密,就用notification傳值,例如多執行緒之間傳值用notificaiton。

2)delegate只是一種較為簡單的回呼,且主要用在一個模塊中.例如說 NavgationController 從 B 界面到A 點傳回按鈕 (呼叫popViewController方法) 可以用delegate比較好。


15.描述一個ViewController的生命周期

當我們呼叫UIViewControlller的view時,

系統首先判斷當前的 UIViewControlller是否存在view,如果存在直接傳回view,

如果不存在的話,會呼叫loadview方法,

然後判斷loadview方法是否是自定義方法,

如果是自定義方法,就執行自定義方法,

如果不是自定義方法,判斷當時視圖控制器是否有xib、stroyboard。

如果有xib、stroyboard 就加載xib、stroyboard。

如果沒有創建一個空白的view。

呼叫viewDidLoad方法。

最後傳回view


16.LLVM與Clang的區別

這兩個都是編譯器.

簡單的說,編譯器有兩個職責:把 Objective-C 代碼轉化成低級代碼,以及對代碼做分析,確保代碼中沒有任何明顯的錯誤。


17.LLVM與Clang的區別

  • 物件方法 [實體物件 方法名]呼叫

  • 代表實體方法,它在類的一個具體實體範圍內執行,也就是說,你在呼叫這個方法之前必須先創建一個類的實體;

  • 類方法 [類名 方法名]呼叫

  • 代表類方法,可以通過類名直接呼叫,不需要創建一個類的實體。


———————————————————————

第二波:

2.jpg

自答: 這套面試題相較於第一波的面試題來說,難度有所降低.嘗試解答一下:


1、什麼是kvc和kvo? 2、kvo的缺陷?

Key value Coding是cocoa的一個標準組成部分,它能讓我們可以通過name(key)的方法訪問property,不必呼叫明確的property accesser(set/get方法);

KVC(鍵-值編碼)是一個用於間接訪問物件屬性的機制(一種使用字串而不是訪問器方法去訪問一個物件實體變數的機制。),使用該機制不需要呼叫set或者get方法以及-》來訪問成員變數,它通過setValue:forkey:valueForkey:方法。

KVC的機制是啥樣的呢?它是以字串的形式向物件發送訊息字串是要關註屬性的關鍵。是否存在setter,getter方法,如果不存在,它將在內部查找名為_key或key的實體變數,如果沒有會呼叫setValueForUndefindedKey:,如果也沒有,則會運行時報錯;

註意: 如果是基本資料型別,則需要封裝一下(NSNumber)。

KVC的使用環境無論是property還是普通的全域性屬性變數,都可以用KVC;

KVC的優缺點:

優點:

1.主要的好處就是來減少代碼量

2.沒有property的變數(private)也能通過KVC來設置;

KVC的缺點:如果key寫錯時,編寫時不會報錯,運行時會報錯;


例子:

@interface LPProduct : NSObject
@property (nonatomiccopyNSString *name;
@property (nonatomicassignCGFloat price;
@property (nonatomicstrong) LPFactory *factory;
@end

@interface LPFactory : NSObject
@property (nonatomiccopyNSString *name;
@end

// 例1:
LPProduct *product1 = [LPProduct new];
product1.price = 1.0;
NSLog(@"price 1: %@", [product1 valueForKeyPath:@"price"]);
[product1 setValue:@100 forKeyPath:@"price"];
NSLog(@"price 1 change: %@", [product1 valueForKeyPath:@"price"]);

KVCDemo[1148:56717] price 1 change: 100

2:
product1.factory = [LPFactory new];
[product1 setValue:@"make in japan" forKeyPath:@"[factory.name](http://factory.name)"];
NSLog(@"price 1 factory name: %@", [product1 valueForKeyPath:@"[factory.name](http://factory.name)"]);

KVCDemo[1148:56717] price 1 factory name: make in japan

KVO 是一個物件能夠觀察另外一個物件的屬性值,並且能夠發現值的變化。KVO 更加適合任何型別的物件偵聽另外一個任意物件的改變,或者是一個物件與另外一個物件保持同步的一種方法,即當另外一種物件的狀態發生改變時,觀察物件馬上作出反應。它只能用來對屬性作出反應,而不會用來對方法或者動作作出反應。

KVO的優點:

1. 能夠提供一種簡單的方法實現兩個物件間的同步;

2. 能夠對非我們創建的物件,即內部物件的狀態改變作出響應,而且不需要改變內部物件(SDK物件)的實現;

3. 能夠獲得觀察的屬性的最新值以及先前值;

4. 用key path來觀察屬性,因此也可以觀察嵌套物件(也就是可以觀察一個物件內部物件的屬性的變化,可以無限嵌套觀察,前提是物件的屬性支持KVO);

5. 完成了對觀察物件的抽象,因為不需要額外的代碼來允許觀察值能夠被觀察(不需要像通知一樣還需要發送通知,KVO屬性的改變,外部可以直接觀察)。

KVO的註意事項:

我們註冊KVO的時候,要觀察哪個屬性,在呼叫註冊方法的時候,addObserver:forKey:options:context: forKey處填寫的屬性是以字串形式,萬一屬性名字寫錯,因為是字串,編譯器也不會出現警告以及檢查;

KVO的使用: 被觀察者發出addObserver:forKey:options:context:方法來添加觀察者。然後只要被觀察者的keyPath的值變化(註意:單純改變其值不會呼叫此方法,只有通過getters和setters來改變值才會觸發KVO),就會在觀察者里呼叫方法observeValueForKeyPath:ofObject:change:context: 因此觀察者需要實現方法observeValueForKeyPath:ofObject:change:context; 來對KVO發出的通知做出響應。

這些代碼只需要在觀察者里進行實現,被觀察者不用添加任何代碼,所以誰要監聽誰註冊,然後對響應進行處理即可,使得觀察者與被觀察者完全解藕,運用很靈活很簡便;但是KVO只能檢測類中的屬性,並且屬性名都是通過NSSTring來查找,編譯器不會幫你檢錯和補全,純手敲所以比較容易出錯。


例子:

    // 3.kvo:添加當前控制器為鍵路徑major.majorName的一個觀察者,如果major.majorName的值改變會通知當前控制器從而自動呼叫下麵的observeValueForKeyPath函式,這裡傳遞舊值和新值
    [_student addObserver:self forKeyPath:@"major.majorName" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];

    // 3s後改變major.majorName的值
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [_student setValue:@"Software Engineer" forKeyPath:@"major.majorName"];
    });

 /// 監聽keyPath值的變化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqual:@"major.majorName"]) {
        // 獲取變化前後的值並打印
        NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
        NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];
        NSLog(@"major.majorName value changed:oldValue:%@ newValue:%@", oldValue,newValue);
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
控制台輸出:oldValue:Computer Science newValue:Software Engineer

/// 移除觀察者釋放資源,防止資源泄漏
- (void)dealloc {
    [_student removeObserver:self forKeyPath:@"major.majorName"];
}

3、Swfit和Objective-C的聯繫,Swift比Objective-C有什麼優勢?

兩者各有優缺點。但是有一點是客觀存在的!——Swift生於Objective-C,長於Objective-C,沒有Objective-C,沒有Cocoa framework,便也沒有Swift.

1.swift句尾不需要分號 ,除非你想在一行中寫三行代碼就加分號隔開

2.swift不分.h和.m檔案 ,一個類只有.swift一個檔案

3.swift資料型別都會自動判斷 , 只區分變數var 和常量let

……


4、舉例說明Swfit裡面有哪些是Objective-C中沒有的?

1).swift獨有的範圍運算子

a…b 表示 [a,b] 如3…5 就是範圍取3,4,5

2).swift獨有的元組型別

var point = (x:15,y:20.2)

就是元組名是 point ,裡面有兩個元素x和y。 有點類似於結構體.

3).函式的預設引數值

func addStudent (name:string,age:Int = 20) –>string{}

設置了預設的年齡為20 所以再呼叫時只需要寫個名字

addStudent(“james”)

要註意的是,使用了預設引數值, 系統會自動生成一個外部引數名。

想改名字也就要寫外部引數名了 即 addStudent(“james”,age:18)

4).swift中使用let定義常量,var定義變數

使用常量,更加安全,不能夠被修改,在需要對物件進行修改的時候 只能用var修飾.

5).if let 、 guard let 的用法

縮減代碼量,安全處理資料邏輯。

……


5、如何對iOS設備進行性能測試?

Instruments 是應用程式用來動態跟蹤和分析 Mac OS X 和 iOS 代碼的實用工具。這是一個靈活而強大的工具,它讓你可以跟蹤一個或多個行程,並檢查收集的資料。


6、使用過CocoPods嗎?它是什麼?CocoaPods的原理?

CocoaPods 是Mac OS X 和 iOS 應用程式開發的一個第三方庫依賴的管理工具,你可以用它來 幫助集中匯入、配置以及更新所用到的第三方。

CocoaPods 的原理是將所有的依賴庫都放到另一個名為Pods的專案中, 然而讓主專案依賴Pods專案,

這樣,原始碼管理工作任務從主專案移到了Pods專案中.

  1.Pods專案最終會編譯成一個名為libPods.a的檔案, 主專案只要依賴這個.a檔案即可.

  2.對於資源檔案, CocoaPods提供了一個名為Pods-resources.sh的bash腳步, 該腳本在每次專案

   編譯的時候都會執行,將第三方庫的各種資源檔案複製到標的目錄中.

  3.CocoaPods通過一個名為Pods.xcconfig的檔案在編譯設置所有的依賴和引數


7、集成三方框架有哪些方法?

1.手動集成

2.cocoapods集成


8、SDWebImage的原理實現機制,如何解決TableView卡的問題?

1). SDWebImage的實現原理

① 從記憶體(字典)中找圖片(當這個圖片在本次使用程式的過程中已經被加載過),找到直接使用。

② 從沙盒中找(當這個圖片在之前使用程式的過程中被加載過),找到使用,快取到記憶體中。

③ 從網絡上獲取,使用,快取到記憶體,快取到沙盒

2).如何解決TableView卡的問題

①.復用單元格

②.單元格中的視圖儘量都使用不透明的,單元格中儘量少使用動畫

③.圖片加載使用異步加載

④.滑動時不加載圖片,停止滑動時開始加載

⑤.單元格中的內容可以在自定義cell類中的drawRect方法內自己繪製

⑥.如非必要,減少reloadData全部cell,只reloadRowsAtIndexPaths

⑦.如果cell是動態行高,計算出高度後快取

⑧.cell高度固定的話直接使用cell.rowHeight設置高度


9、一個動畫怎麼實現?


CoreAnimation(核心動畫)

coreAnimation.png


10、iOS中常用的資料儲存方式有哪些?(資料持久化)每種儲存方式各有什麼特點?每種儲存方式各自在什麼場景下使用?

所有的本地持久化資料儲存的本質都是寫檔案,而且只能存到沙盒中。

沙盒機制是蘋果的一項安全機制,本質就是系統給每個應用分配了一個檔案夾來儲存資料,而且每個應用只能訪問分配給自己的那個檔案夾,其他應用的檔案夾是不能訪問的。

資料儲存的核心都是寫檔案。主要有四種持久化方式:屬性串列,物件序列化,SQLite 資料庫, CoreData

屬性串列:應用於少量資料儲存,比如登陸的用戶信息,應用程式配置信息等。只有NSString ,NSArray,NSDictory,NSData,可以WriteToFile;儲存的依舊是plist檔案,plist檔案可以儲存的7種資料型別:array,dictory,string,bool,data,date,number。

物件序列化:最終也是存為屬性串列檔案,如果程式中,需要儲存的時候,直接儲存物件比較方便,例如有一個設置類,我們可以把設置類的物件直接儲存,就沒必要再把裡面的每一個屬性單獨存到檔案中。物件序列化是將一個實現了NSCoding協議的物件,通過序列化(NSKeydArchiver)的形式,將物件中的屬性抽取出來,轉化成二進制流,也就是NSData,NSData可以選擇write to file 或者儲存到NSUserdefault中。 必須實現的兩個方法 encodeWithCoder,initWithCoder。物件序列化的本質就是 物件NSData。

SQLite: 適合大量,重覆,有規律的資料儲存。而且頻繁的讀取,刪除,過濾資料,這種適合使用資料庫

CoreData: Sqlite叫做關係型資料庫,CoreData 是一中OR-Mapping的思想 ,O代表物件Object,R代表relationship,Mapping代表映射,直譯過來就是物件關係映射,其實就是把物件的屬性和表中的欄位自動映射,簡化程式員的負擔,以面向物件的方式運算元據庫。ORMapping是一種思想,CoreData實現了這種思想,在Java中,hibernate 也是對ORMapping的一種實現,只是利用java實現的。

CoreData 本質還是資料庫,只不過使用起來更加面向物件,不關註二維的表結構,而是只需要關註物件,純面向物件的資料操作方式。我們直接使用資料庫的時候,如果向資料庫中插入資料,一般是把一個物件的屬性和資料庫中某個表的欄位一一對應,然後把物件的屬性儲存到具體的表欄位中.取一條資料的時候,把表中的一行資料取出,同樣需要再封裝到物件的屬性中,這樣的方式有點繁瑣,不面向物件。CoreData解決的問題就是不需要這個中間的轉換過程,看起來是直接把物件儲存進去,並且取出來,不關心表的存在,實際內部幫你做好了映射關係。


11、說一說你對SQLite的認識?(見10)


12、runloop和執行緒有什麼關係?

一般來講,一個執行緒一次只能執行一個任務,執行完成後執行緒就會退出。如果我們需要一個機制,讓執行緒能隨時處理事件但並不退出,通常的代碼邏輯是這樣的:


function loop({
    initialize();
    do {
        var message = get_next_message();
        process_message(message);
    } while (message != quit);
}


這種模型通常被稱作 Event Loop。 Event Loop 在很多系統和框架里都有實現,比如 Node.js 的事件處理,比如 Windows 程式的訊息迴圈,再比如 OSX/iOS 里的 RunLoop。實現這種模型的關鍵點在於:如何管理事件/訊息,如何讓執行緒在沒有處理訊息時休眠以避免資源占用、在有訊息到來時立刻被喚醒。

所以,RunLoop 實際上就是一個物件,這個物件管理了其需要處理的事件和訊息,並提供了一個入口函式來執行上面 Event Loop 的邏輯。執行緒執行了這個函式後,就會一直處於這個函式內部 “接受訊息->等待->處理” 的迴圈中,直到這個迴圈結束(比如傳入 quit 的訊息),函式傳回

基本作用: 

a 保持程式的持續運行(ios程式為什麼能一直活著不會死)

b 處理app中的各種事件(比如觸摸事件、定時器事件【NSTimer】、selector事件【選擇器·performSelector···】)

c 節省CPU資源,提高程式性能,有事情就做事情,沒事情就休息

重要說明:

(1)如果沒有Runloop,那麼程式一啟動就會退出,什麼事情都做不了。

(2)如果有了Runloop,那麼相當於在內部有一個死迴圈,能夠保證程式的持續運行

(3)main函式中的Runloop a 在UIApplication函式內部就啟動了一個Runloop 該函式傳回一個int型別的值 b 這個預設啟動的Runloop是跟主執行緒相關聯的


13、runloop的mode作用是什麼?(略)


14、你一般是如何除錯Bug的?


1.在運行過程中,如果出現EXC_BAD_ACCESS 異常,往往提示的信息很少或者沒有提示,啟用NSZombieEnabled後在控制台能打印出更多的提示信息,便於debug,請註意,僵屍樣式下的除錯工作只能在模擬器中實現,我們無法在物理設備上完成這一診斷流程.

2.異常斷點,一般程式crash時Xcode一般會定位到main函式中,得不到詳細的crash信息,打上異常斷點後就極大可能定位到程式的crash處,利於debug。

3.一般來說,在創建工程的時候,應該在Build Settings啟用Analyze During ‘Build’,這樣每次編譯時都會自動靜態分析。這樣的話,寫完一小段代碼之後,就馬上知道是否存在記憶體泄露或其他bug問題,並且可以修bugs。

4.如果你想在運行的時候查看APP是否存在記憶體泄露,你可以使用Xcode上instruments工具上的Leaks模塊進行記憶體分析。但是有些記憶體泄露是很難檢查出來,有時只有通過手動改寫dealloc方法,看它最終有沒有呼叫。


15、描述一個ViewController的生命周期 (同第一波15題)


———————————————————————

第三波

1.死鎖的四個必要條件

產生死鎖的四個必要條件:

(1) 互斥條件:一個資源每次只能被一個行程使用。

(2) 請求與保持條件:一個行程因請求資源而阻塞時,對已獲得的資源保持不放。

(3) 不剝奪條件:行程已獲得的資源,在末使用完之前,不能強行剝奪。

(4) 迴圈等待條件:若干行程之間形成一種頭尾相接的迴圈等待資源關係。

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之

一不滿足,就不會發生死鎖。


死鎖的經典代碼:


lock.png


比如這個最簡單的OC命令列程式就會導致死鎖,運行後不會看到任何結果。

回到上面的死鎖代碼中:首先明確的是:執行這個dispatch_get_main_queue佇列的是主執行緒。執行了dispatch_sync函式後,將block添加到了main_queue中,同時呼叫dispatch_syn這個函式的執行緒(也就是主執行緒)被阻塞,等待block執行完成,而執行主執行緒佇列任務的執行緒正是主執行緒,此時他處於阻塞狀態,所以block永遠不會被執行,因此主執行緒一直處於阻塞狀態。因此這段代碼運行後,並非卡在block中無法傳回,而是根本無法執行到這個block。


修改方法:
dispatch_async(dispatch_get_global_queue(0,0), ^(void){
NSLog(@"這就不死鎖了");
});


2.行程與執行緒

這個其實是操作系統的問題,在網上看到個通俗精辟的例子:

開個QQ,開了一個行程;開了迅雷,開了一個行程。在QQ的這個行程里,傳輸文字開一個執行緒、傳輸語音開了一個執行緒、彈出對話框又開了一個執行緒。所以運行某個軟體,相當於開了一個行程。在這個軟體運行的過程里(在這個行程里),多個工作支撐的完成QQ的運行,那麼這“多個工作”分別有一個執行緒。所以一個行程管著多個執行緒。通俗的講:“行程是爹媽,管著眾多的執行緒兒子”


3.OC 和 js 交互 OC怎麼呼叫js函式, JS怎麼呼叫OC方法 ?

WebViewJavascriptBridge


4.代理和block的區別 ? block迴圈取用產生的原因, 以及怎麼處理?

在定義一個類的property時候,為property選擇strong還是copy特別註意和研究明白的,如果property是NSString或者NSArray及其子類的時候,最好選擇使用copy屬性修飾。為什麼呢?這是為了防止賦值給它的是可變的資料,如果可變的資料發生了變化,那麼該property也會發生變化。


代碼示例

@interface Person : NSObject
@property (strongnonatomicNSArray *bookArray1;
@property (copynonatomicNSArray *bookArray2;
@end

//Person呼叫
main(){
    NSMutableArray *books = [@[@"book1"] mutableCopy];
    Person *person = [[Person alloc] init];
    person.bookArray1 = books;
    person.bookArray2 = books;
    [books addObject:@"book2"];
    NSLog(@"bookArray1:%@",person.bookArray1);
    NSLog(@"bookArray2:%@",person.bookArray2);
}

我們看到,使用strong修飾的person.bookArray1輸出是[book1,book2],而使用copy修飾的person.bookArray2輸出是[book1]。這下可以看出來區別了吧。

備註:使用strong,則person.bookArray1與可變陣列books指向同一塊記憶體區域,books內容改變,導致person.bookArray1的內容改變,因為兩者是同一個東西;而使用copy,person.bookArray2在賦值之前,將books內容複製,創建一個新的記憶體區域,所以兩者不是一回事,books的改變不會導致person.bookArray2的改變。


5.類別與擴展的區別?

擴展寫法上跟類別一致,只是括號中沒有類別描述。

“擴展”可以添加屬性、變數,類別不能。

分類運用場景舉例:想要收集每個頁面的啟動時間。

問題1:

專案中已經有上百個頁面了,如果一個一個的加,浪費時間不說,以後增加了新頁面,還需要添加方法

解決方法:

我們可以發現頁面都繼承了UIViewController,想要在每個頁面都執行的代碼,可以寫在這些頁面的父類中。我們可以把代碼寫在UIViewController中。UIViewController是官方類,我們只能呼叫期接口,並不能修改他的實現。所以需要使用分類(category).

1.分類(category)的作用

1.1作用:可以在不修改原來類的基礎上,為一個類擴展方法。

1.2最主要的用法:給系統自帶的類擴展方法。

2.分類中能寫點啥?

2.1分類中只能添加“方法”,不能增加成員變數。

2.2分類中可以訪問原來類中的成員變數,但是只能訪問@protect和@public形式的變數。如果想要訪問本類中的私有變數,分類和子類一樣,只能通過方法來訪問。

分類(category)和類擴展(extension)的關係

1.類擴展(extension)是category的一個特例,有時候也被稱為匿名分類。他的作用是為一個類添加一些私有的成員變數和方法。

2.類擴展能寫點啥?和分類不同,類擴展即可以宣告成員變數又可以宣告方法。

3.類擴展聽上去很複雜,但其實我們很早就認識他了。你記得繼承自UIViewController的ViewController吧.


 @interface ViewController()//這就是類擴展的寫法
 @end


嚴格意義上來說,oc是沒有私有變數或者方法這一說的,不過我們可以通過延展來實現這個私有方法或者變數。類擴展可以定義在.m檔案中,這種擴展方式中定義的變數都是私有的,也可以定義在.h檔案中,這樣定義的代碼就是共有的,類擴展在.m檔案中宣告私有方法是非常好的方式。

類擴展中添加的新方法,一定要實現。categorygory中沒有這種限制。


6.怎麼實現一個精準的Timer


    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(doAnything) userInfo:nil repeats:NO];
    // 將定時器添加到runloop中
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // 在執行緒中使用定時器,如果不啟動run loop,timer的事件是不會響應的,而子執行緒中runloop預設沒有啟動
    // 讓執行緒執行一個周期性的任務,如果不啟動run loop, 執行緒跑完就可能被系統釋放了
    [[NSRunLoop currentRunLoop] run];// 如果沒有這句,doAnything將不會執行!!

runloopmode是一個集合,包括監聽:事件源,定時器,以及需通知的runloop observers

樣式包括:

default樣式:幾乎包括所有輸入源(除NSConnection) NSDefaultRunLoopMode樣式

mode樣式:處理modal panels

connection樣式:處理NSConnection事件,屬於系統內部,用戶基本不用

event tracking樣式:如組件拖動輸入源 UITrackingRunLoopModes 不處理定時事件

common modes樣式:NSRunLoopCommonModes 這是一組可配置的通用樣式。將input sources與該樣式關聯則同時也將input sources與該組中的其它樣式進行了關聯。


7.當app越做越大時, 會發現App冷啟動時, 速度很慢, 你有沒有遇到過? 你是怎麼優化的?

一般而言,啟動時間是指從用戶點擊 APP 那一刻開始到用戶看到第一個界面這中間的時間。我們進行優化的時候,我們將啟動時間分為 pre-main 時間和 main 函式到第一個界面渲染完成時間這兩個部分。

大家都知道 APP 的入口是 main 函式,在 main 之前,我們自己的代碼是不會執行的。而進入到 main 函式以後,我們的代碼都是從didFinishLaunchingWithOptions開始執行的,所以很明顯,優化這兩部分的思路是不一樣的。

為了方便起見,我們將 pre-main 時間成為 t1 時間,而將main 函式到第一個界面渲染完成這段時間稱為 t2 時間。 然後用profile 工具來分析出哪些代碼是耗時的。

Profiler.jpg


8.在適配 iOS 11時, 經常出現的問題

都有哪些? 以及你是怎麼解決的

適配點一:專案中使用狀態欄中圖標判斷當前網絡的具體狀態


networkStatus.png

此時可以看到運行崩潰了,因為從iPhone X取出來之後只有view層級的信息,所以採用以下方法確定2G/3G/4G


NSArray *typeStrings2G = @[CTRadioAccessTechnologyEdge,
                               CTRadioAccessTechnologyGPRS,
                               CTRadioAccessTechnologyCDMA1x];

    NSArray *typeStrings3G = @[CTRadioAccessTechnologyHSDPA,
                               CTRadioAccessTechnologyWCDMA,
                               CTRadioAccessTechnologyHSUPA,
                               CTRadioAccessTechnologyCDMAEVDORev0,
                               CTRadioAccessTechnologyCDMAEVDORevA,
                               CTRadioAccessTechnologyCDMAEVDORevB,
                               CTRadioAccessTechnologyeHRPD];

    NSArray *typeStrings4G = @[CTRadioAccessTechnologyLTE];
    // 該 API 在 iOS7 以上系統才有效
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CTTelephonyNetworkInfo *teleInfo= [[CTTelephonyNetworkInfo alloc] init];
        NSString *accessString = teleInfo.currentRadioAccessTechnology;
        if ([typeStrings4G containsObject:accessString]) {
            NSLog(@"4G網絡");
        } else if ([typeStrings3G containsObject:accessString]) {
            NSLog(@"3G網絡");
        } else if ([typeStrings2G containsObject:accessString]) {
            NSLog(@"2G網絡");
        } else {
            NSLog(@"未知網絡");
        }
    } else {
        NSLog(@"未知網絡");
    }

適配點二:解決這個問題後專案跑起來發現,整個app界面上下各空出大概40pt的高度.造成這個的原因是啟動圖使用 Launch Images Source 設置的時候沒有勾選並設置對應的圖片.

但是即使按照上面的操作進行之後,會發現底部 UITabBar 依舊是高出一些高度,查看層級關係後發現,同樣是由於安全區的原因,UITabBar 高度由49pt變成了83pt,因此這裡也要對iPhone X 及其模擬器進行適配

適配點三:iPhone X 只有 faceID,沒有touchID,如果in的應用有使用到 touchID 解鎖的地方,這裡要根據機型進行相應的適配

適配點四: iPhone X更大的坑是屏幕的適配Safe area.


9.談談instancetype和id的異同

1、相同點

都可以作為方法的傳回型別

2、不同點

①instancetype可以傳回和方法所在類相同型別的物件,id只能傳回未知型別的物件;

②instancetype只能作為傳回值,不能像id那樣作為引數


10.isKindOfClass和isMemberOfClass的區別

isKindOfClass來確定一個物件是否是一個類的成員,或者是派生自該類的成員

isMemberOfClass只能確定一個物件是否是當前類的成員


第四波


這套題目是我在知乎上看到的,是MrPeak大大出的題目,好開森哦,嘗試著來回答下,還希望各位大佬多多指教.

一份”有點難”的iOS面試題 .


編號272,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂