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

第一波:
1.jpg

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

1.什么是函數(shù)式編程?
說起鏈?zhǔn)骄幊毯秃瘮?shù)式編程,小伙伴們千萬不要緊張。
聽著很高大尚,其實(shí)也就那么回事。相信有過swift/C#開發(fā)經(jīng)驗(yàn)的,或者其他編程經(jīng)驗(yàn)的,只要不是OC,一看就知道。 通過一個小例子來解釋.
看兩行代碼

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

上面的就是鏈?zhǔn)骄幊?函數(shù)式編程.

來個大白話解釋:看到括號里面的參數(shù)了吧,跟swift的函數(shù)調(diào)用是不是很相似,包括別的語言,都用小括號傳參,只有OC是冒號傳參。
再看方法調(diào)用用的是".",而OC用的是[]+空格。
這幾個方法調(diào)用,如果要按OC的打法,估計要整4行,對象一個一個的調(diào)用方法,但鏈?zhǔn)骄褪沁@么一行搞定.
分析一下,因?yàn)锽lock可以通過()來傳值,我們推斷run(para)和eat(para)這兩個方法,肯定返回值是一個Block,而且是帶一個參數(shù)的Block

2.什么是ABI?
嗚嗚~~~(>_<)~~~,這個我真的不知道是什么鬼,不知道和iOS有什么關(guān)系,百度也了無法理解..... 希望知道的小伙伴們留言告知.

3.什么是MVC,請結(jié)合CocoaTouch說明?
M: model V:View C:controller O(∩_∩)O哈哈~ 自行展開,自圓其說吧.

4.什么是MVVM,請設(shè)計View moled需要考慮哪些?
M: model V:View VM:ViewModel是View和Model之間的中介
MVVM的出現(xiàn)主要是為了解決在開發(fā)過程中Controller越來越龐大的問題,變得難以維護(hù),所以MVVM把數(shù)據(jù)加工的任務(wù)從Controller中解放了出來,使得Controller只需要專注于數(shù)據(jù)調(diào)配的工作,ViewModel則去負(fù)責(zé)數(shù)據(jù)加工并通過通知機(jī)制讓View響應(yīng)ViewModel的改變。
MVVM是基于胖Model的架構(gòu)思路建立的,然后在胖Model中拆出兩部分:Model和ViewModel。ViewModel本質(zhì)上算是Model層(因?yàn)槭桥諱odel里面分出來的一部分),所以View并不適合直接持有ViewModel,因?yàn)閂iewModel有可能并不是只服務(wù)于特定的一個View,使用更加松散的綁定關(guān)系能夠降低ViewModel和View之間的耦合度。
其實(shí)MVVM是一定需要Controller的參與的,雖然MVVM在一定程度上弱化了Controller的存在感,并且給Controller做了減負(fù)瘦身(這也是MVVM的主要目的)。但是,這并不代表MVVM中不需要Controller.嚴(yán)格來說MVVM其實(shí)是MVCVM。從中可以得知,Controller夾在View和ViewModel之間做的其中一個主要事情就是將View和ViewModel進(jìn)行綁定。在邏輯上,Controller知道應(yīng)當(dāng)展示哪個View,Controller也知道應(yīng)當(dāng)使用哪個ViewModel,然而View和ViewModel它們之間是互相不知道的,所以Controller就負(fù)責(zé)控制他們的綁定關(guān)系,所以叫Controller/控制器就是這個原因。

5.swift相對于OC有哪些優(yōu)點(diǎn)?
Swift容易閱讀
Swift更容易維護(hù)
Swift更加安全
Swift代碼更少
Swift速度更快

6.什么是泛型,swift在哪些地方使用了泛型?
兩個整型數(shù)相加和兩個浮點(diǎn)數(shù)相加的程序看起來應(yīng)該非常類似,甚至一模一樣才對。唯一的區(qū)別就是變量的類型不同。
在強(qiáng)類型語言中,你需要去定義諸如addInts, addFloats, addDoubles 等方法來正確地處理參數(shù)及返回值.
例如:

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

這個函數(shù)用 T 占位符來代替實(shí)際的類型。并沒有指定具體的類型,但是傳入的a ,b 必須是同一類型T。在調(diào)用這個函數(shù)的時候才能指定 T 是那種具體的類型。

7.defer、guard的作用?
defer 譯為延緩、推遲之意
比如,讀取某目錄下的文件內(nèi)容并處理數(shù)據(jù),你需要首先定位到文件目錄,打開文件夾,讀取文件內(nèi)容以及處理數(shù)據(jù),關(guān)閉文件以及文件夾。倘若一切順利,只需按照設(shè)定好的程序流程走一輪即可;不過考慮事情要面面俱到,倘若中間某個環(huán)節(jié)失敗,比如讀取文件內(nèi)容失敗、處理數(shù)據(jù)失敗等等,還需要進(jìn)行一些后續(xù)收尾工作,即關(guān)閉文件或關(guān)閉文件夾(當(dāng)然就算順利執(zhí)行,也是要關(guān)閉的)。

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

guard 有控制、警戒之意,語法簡單,只需兩個示例代碼即可明白。
// 這里使用if 和 guard進(jìn)行對比 你會懂的更多
if age < 13 {
return //當(dāng)年齡小于13時 程序返回 不進(jìn)行之后的操作
}
guard 改寫
guard age >= 13 else{
return
}
可以看到代碼的意思是保證(guard)age值大于等于13 否則(else)返回,不執(zhí)行下面程序。

8.swift語法糖?!的本質(zhì)(實(shí)現(xiàn)原理)
相信大家在學(xué)習(xí)和使用Swift的時候,肯定會被 ! 和 ? 搞瘋過, 糾結(jié)這兩個符號到底是個什么鬼 ?鬼知道什么時候使用!,什么時候使用?
?! 其實(shí)分別是Swift語言中對一種可選類型( Optional) 操作的語法糖。 那可選類型是干什么的呢? Swift中是可以聲明一個沒有初始值的屬性, Swift中引入了可選類型(Optional)來解決這一問題。它的定義是通過在類型生命后加加一個 ? 操作符完成的。
例如: var name: String?
Optional其實(shí)是個enum,里面有None和Some兩種類型。其實(shí)所謂的nil就是Optional.None , 非nil就是Optional.Some, 然后會通過Some(T)包裝(wrap)原始值,這也是為什么在使用Optional的時候要拆包(從enum里取出來原始值)的原因。

9.舉例swift中模式匹配的作用?
Swift有一個很好的特性,那就是模式匹配的擴(kuò)展。模式是用于匹配的規(guī)則值,如switch語句的case,do語句的catch子句,以及if、while、guard、for-in語句的條件。
假設(shè)你想判斷一個整數(shù)是大于、小于還是等于零,你可以用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("等于零")
}

但模式匹配默認(rèn)并不支持不等式。所以我們要實(shí)現(xiàn)我們自己的~=
我們知道這個方法必須返回一個Bool,那正是我們需要的,我們需要知道這個值是否匹配模式。

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

10.swift中clousure與OC中block的區(qū)別?

swift中的閉包傳值
clousure-1.jpg
clousure-2.png
clousure-3.png
OC中的閉包傳值
block.jpg

11.什么是capture list,舉例說明用處?
寶寶心里苦,這個寶寶也不知道,百度了還是不知道,希望大佬留言解答.~~~(>_<)~~~

12.swift中private與fileprivate的區(qū)別?
1,private private 訪問級別所修飾的屬性或者方法只能在當(dāng)前類里訪問。
(注意:Swift4 中,extension 里也可以訪問 private 的屬性。)
2,fileprivate fileprivate 訪問級別所修飾的屬性或者方法在當(dāng)前的 Swift 源文件里可以訪問。(比如上面樣例把 private 改成 fileprivate 就不會報錯了)
3,internal(默認(rèn)訪問級別,internal修飾符可寫可不寫)
internal 訪問級別所修飾的屬性或方法在源代碼所在的整個模塊都可以訪問。
如果是框架或者庫代碼,則在整個框架內(nèi)部都可以訪問,框架由外部代碼所引用時,則不可以訪問。
如果是 App 代碼,也是在整個 App 代碼,也是在整個 App 內(nèi)部可以訪問
4,public 可以被任何人訪問。但其他 module 中不可以被 override 和繼承,而在 module 內(nèi)可以被 override 和繼承。
5,open 可以被任何人使用,包括 override 和繼承。
總結(jié)
現(xiàn)在的訪問權(quán)限則依次為:open,public,internalfileprivate,private

13.REST、HTTP、JSON是什么?
REST(Representational State Transfer)含狀態(tài)傳輸是一種軟件架構(gòu)風(fēng)格。
HTTP:網(wǎng)絡(luò)傳輸協(xié)議
JSON:是一種輕量級的數(shù)據(jù)交換格式

14.delegate解決了什么問題,Notification與它有什么不同?
區(qū)別:

  1. 效率肯定是delegate比nsnotification高。
  2. delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要關(guān)注返回值
    1)兩個模塊之間聯(lián)系不是很緊密,就用notification傳值,例如多線程之間傳值用notificaiton。
    2)delegate只是一種較為簡單的回調(diào),且主要用在一個模塊中.例如說 NavgationController 從 B 界面到A 點(diǎn)返回按鈕 (調(diào)用popViewController方法) 可以用delegate比較好。

15.描述一個ViewController的生命周期
當(dāng)我們調(diào)用UIViewControlller的view時,
系統(tǒng)首先判斷當(dāng)前的 UIViewControlller是否存在view,如果存在直接返回view,
如果不存在的話,會調(diào)用loadview方法,
然后判斷l(xiāng)oadview方法是否是自定義方法,
如果是自定義方法,就執(zhí)行自定義方法,
如果不是自定義方法,判斷當(dāng)時視圖控制器是否有xib、stroyboard。
如果有xib、stroyboard 就加載xib、stroyboard。
如果沒有創(chuàng)建一個空白的view。
調(diào)用viewDidLoad方法。
最后返回view

16.LLVM與Clang的區(qū)別
這兩個都是編譯器.
簡單的說,編譯器有兩個職責(zé):把 Objective-C 代碼轉(zhuǎn)化成低級代碼,以及對代碼做分析,確保代碼中沒有任何明顯的錯誤。

17.LLVM與Clang的區(qū)別

  • 對象方法 [實(shí)例對象 方法名]調(diào)用
  • 代表實(shí)例方法,它在類的一個具體實(shí)例范圍內(nèi)執(zhí)行,也就是說,你在調(diào)用這個方法之前必須先創(chuàng)建一個類的實(shí)例;
  • 類方法 [類名 方法名]調(diào)用
  • 代表類方法,可以通過類名直接調(diào)用,不需要創(chuàng)建一個類的實(shí)例。

---------------------------------------------------------------------

第二波:

2.jpg

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

1、什么是kvc和kvo? 2、kvo的缺陷?
Key value Coding是cocoa的一個標(biāo)準(zhǔn)組成部分,它能讓我們可以通過name(key)的方法訪問property,不必調(diào)用明確的property accesser(set/get方法);
KVC(鍵-值編碼)是一個用于間接訪問對象屬性的機(jī)制(一種使用字符串而不是訪問器方法去訪問一個對象實(shí)例變量的機(jī)制。),使用該機(jī)制不需要調(diào)用set或者get方法以及-》來訪問成員變量,它通過setValue:forkey:valueForkey:方法。
KVC的機(jī)制是啥樣的呢?它是以字符串的形式向?qū)ο蟀l(fā)送消息字符串是要關(guān)注屬性的關(guān)鍵。是否存在setter,getter方法,如果不存在,它將在內(nèi)部查找名為_key或key的實(shí)例變量,如果沒有會調(diào)用setValueForUndefindedKey:,如果也沒有,則會運(yùn)行時報錯;

注意: 如果是基本數(shù)據(jù)類型,則需要封裝一下(NSNumber)。
KVC的使用環(huán)境:無論是property還是普通的全局屬性變量,都可以用KVC;
KVC的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
1.主要的好處就是來減少代碼量
2.沒有property的變量(private)也能通過KVC來設(shè)置;
KVC的缺點(diǎn):如果key寫錯時,編寫時不會報錯,運(yùn)行時會報錯;

例子:

@interface LPProduct : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat price;
@property (nonatomic, strong) LPFactory *factory;
@end

@interface LPFactory : NSObject
@property (nonatomic, copy) NSString *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 是一個對象能夠觀察另外一個對象的屬性值,并且能夠發(fā)現(xiàn)值的變化。KVO 更加適合任何類型的對象偵聽另外一個任意對象的改變,或者是一個對象與另外一個對象保持同步的一種方法,即當(dāng)另外一種對象的狀態(tài)發(fā)生改變時,觀察對象馬上作出反應(yīng)。它只能用來對屬性作出反應(yīng),而不會用來對方法或者動作作出反應(yīng)。

KVO的優(yōu)點(diǎn):
1. 能夠提供一種簡單的方法實(shí)現(xiàn)兩個對象間的同步;
2. 能夠?qū)Ψ俏覀儎?chuàng)建的對象,即內(nèi)部對象的狀態(tài)改變作出響應(yīng),而且不需要改變內(nèi)部對象(SDK對象)的實(shí)現(xiàn);
3. 能夠獲得觀察的屬性的最新值以及先前值;
4. 用key path來觀察屬性,因此也可以觀察嵌套對象(也就是可以觀察一個對象內(nèi)部對象的屬性的變化,可以無限嵌套觀察,前提是對象的屬性支持KVO);
5. 完成了對觀察對象的抽象,因?yàn)椴恍枰~外的代碼來允許觀察值能夠被觀察(不需要像通知一樣還需要發(fā)送通知,KVO屬性的改變,外部可以直接觀察)。

KVO的注意事項(xiàng):
我們注冊KVO的時候,要觀察哪個屬性,在調(diào)用注冊方法的時候,addObserver:forKey:options:context: forKey處填寫的屬性是以字符串形式,萬一屬性名字寫錯,因?yàn)槭亲址幾g器也不會出現(xiàn)警告以及檢查;

KVO的使用: 被觀察者發(fā)出addObserver:forKey:options:context:方法來添加觀察者。然后只要被觀察者的keyPath的值變化(注意:單純改變其值不會調(diào)用此方法,只有通過getters和setters來改變值才會觸發(fā)KVO),就會在觀察者里調(diào)用方法observeValueForKeyPath:ofObject:change:context: 因此觀察者需要實(shí)現(xiàn)方法observeValueForKeyPath:ofObject:change:context; 來對KVO發(fā)出的通知做出響應(yīng)。
這些代碼只需要在觀察者里進(jìn)行實(shí)現(xiàn),被觀察者不用添加任何代碼,所以誰要監(jiān)聽誰注冊,然后對響應(yīng)進(jìn)行處理即可,使得觀察者與被觀察者完全解藕,運(yùn)用很靈活很簡便;但是KVO只能檢測類中的屬性,并且屬性名都是通過NSSTring來查找,編譯器不會幫你檢錯和補(bǔ)全,純手敲所以比較容易出錯。

例子:

    // 3.kvo:添加當(dāng)前控制器為鍵路徑major.majorName的一個觀察者,如果major.majorName的值改變會通知當(dāng)前控制器從而自動調(diào)用下面的observeValueForKeyPath函數(shù),這里傳遞舊值和新值
    [_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"];
    });

 /// 監(jiān)聽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的聯(lián)系,Swift比Objective-C有什么優(yōu)勢?
兩者各有優(yōu)缺點(diǎn)。但是有一點(diǎn)是客觀存在的!——Swift生于Objective-C,長于Objective-C,沒有Objective-C,沒有Cocoa framework,便也沒有Swift.
1.swift句尾不需要分號 ,除非你想在一行中寫三行代碼就加分號隔開
2.swift不分.h和.m文件 ,一個類只有.swift一個文件
3.swift數(shù)據(jù)類型都會自動判斷 , 只區(qū)分變量var 和常量let
......

4、舉例說明Swfit里面有哪些是Objective-C中沒有的?
1).swift獨(dú)有的范圍運(yùn)算符
a…b 表示 [a,b] 如3…5 就是范圍取3,4,5
2).swift獨(dú)有的元組類型
var point = (x:15,y:20.2)
就是元組名是 point ,里面有兩個元素x和y。 有點(diǎn)類似于結(jié)構(gòu)體.
3).函數(shù)的默認(rèn)參數(shù)值
func addStudent (name:string,age:Int = 20) –>string{}
設(shè)置了默認(rèn)的年齡為20 所以再調(diào)用時只需要寫個名字
addStudent(“james”)
要注意的是,使用了默認(rèn)參數(shù)值, 系統(tǒng)會自動生成一個外部參數(shù)名。
想改名字也就要寫外部參數(shù)名了 即 addStudent(“james”,age:18)
4).swift中使用let定義常量,var定義變量
使用常量,更加安全,不能夠被修改,在需要對對象進(jìn)行修改的時候 只能用var修飾.
5).if let 、 guard let 的用法
縮減代碼量,安全處理數(shù)據(jù)邏輯。
......

5、如何對iOS設(shè)備進(jìn)行性能測試?
Instruments 是應(yīng)用程序用來動態(tài)跟蹤和分析 Mac OS X 和 iOS 代碼的實(shí)用工具。這是一個靈活而強(qiáng)大的工具,它讓你可以跟蹤一個或多個進(jìn)程,并檢查收集的數(shù)據(jù)。

6、使用過CocoPods嗎?它是什么?CocoaPods的原理?
CocoaPods 是Mac OS X 和 iOS 應(yīng)用程序開發(fā)的一個第三方庫依賴的管理工具,你可以用它來 幫助集中導(dǎo)入、配置以及更新所用到的第三方。
CocoaPods 的原理是將所有的依賴庫都放到另一個名為Pods的項(xiàng)目中, 然而讓主項(xiàng)目依賴Pods項(xiàng)目,
這樣,源碼管理工作任務(wù)從主項(xiàng)目移到了Pods項(xiàng)目中.
  1.Pods項(xiàng)目最終會編譯成一個名為libPods.a的文件, 主項(xiàng)目只要依賴這個.a文件即可.
  2.對于資源文件, CocoaPods提供了一個名為Pods-resources.sh的bash腳步, 該腳本在每次項(xiàng)目
   編譯的時候都會執(zhí)行,將第三方庫的各種資源文件復(fù)制到目標(biāo)目錄中.
  3.CocoaPods通過一個名為Pods.xcconfig的文件在編譯設(shè)置所有的依賴和參數(shù)

7、集成三方框架有哪些方法?
1.手動集成
2.cocoapods集成

8、SDWebImage的原理實(shí)現(xiàn)機(jī)制,如何解決TableView卡的問題?
1). SDWebImage的實(shí)現(xiàn)原理
① 從內(nèi)存(字典)中找圖片(當(dāng)這個圖片在本次使用程序的過程中已經(jīng)被加載過),找到直接使用。
② 從沙盒中找(當(dāng)這個圖片在之前使用程序的過程中被加載過),找到使用,緩存到內(nèi)存中。
③ 從網(wǎng)絡(luò)上獲取,使用,緩存到內(nèi)存,緩存到沙盒

2).如何解決TableView卡的問題
①.復(fù)用單元格
②.單元格中的視圖盡量都使用不透明的,單元格中盡量少使用動畫
③.圖片加載使用異步加載
④.滑動時不加載圖片,停止滑動時開始加載
⑤.單元格中的內(nèi)容可以在自定義cell類中的drawRect方法內(nèi)自己繪制
⑥.如非必要,減少reloadData全部cell,只reloadRowsAtIndexPaths
⑦.如果cell是動態(tài)行高,計算出高度后緩存
⑧.cell高度固定的話直接使用cell.rowHeight設(shè)置高度

9、一個動畫怎么實(shí)現(xiàn)?

CoreAnimation(核心動畫)

coreAnimation.png

10、iOS中常用的數(shù)據(jù)存儲方式有哪些?(數(shù)據(jù)持久化)每種存儲方式各有什么特點(diǎn)?每種存儲方式各自在什么場景下使用?
所有的本地持久化數(shù)據(jù)存儲的本質(zhì)都是寫文件,而且只能存到沙盒中。
沙盒機(jī)制是蘋果的一項(xiàng)安全機(jī)制,本質(zhì)就是系統(tǒng)給每個應(yīng)用分配了一個文件夾來存儲數(shù)據(jù),而且每個應(yīng)用只能訪問分配給自己的那個文件夾,其他應(yīng)用的文件夾是不能訪問的。
數(shù)據(jù)存儲的核心都是寫文件。主要有四種持久化方式:屬性列表,對象序列化,SQLite 數(shù)據(jù)庫, CoreData

屬性列表:應(yīng)用于少量數(shù)據(jù)存儲,比如登陸的用戶信息,應(yīng)用程序配置信息等。只有NSString ,NSArray,NSDictory,NSData,可以WriteToFile;存儲的依舊是plist文件,plist文件可以存儲的7種數(shù)據(jù)類型:array,dictory,string,bool,data,date,number。

對象序列化:最終也是存為屬性列表文件,如果程序中,需要存儲的時候,直接存儲對象比較方便,例如有一個設(shè)置類,我們可以把設(shè)置類的對象直接存儲,就沒必要再把里面的每一個屬性單獨(dú)存到文件中。對象序列化是將一個實(shí)現(xiàn)了NSCoding協(xié)議的對象,通過序列化(NSKeydArchiver)的形式,將對象中的屬性抽取出來,轉(zhuǎn)化成二進(jìn)制流,也就是NSData,NSData可以選擇write to file 或者存儲到NSUserdefault中。 必須實(shí)現(xiàn)的兩個方法 encodeWithCoder,initWithCoder。對象序列化的本質(zhì)就是 對象NSData。

SQLite: 適合大量,重復(fù),有規(guī)律的數(shù)據(jù)存儲。而且頻繁的讀取,刪除,過濾數(shù)據(jù),這種適合使用數(shù)據(jù)庫

CoreData: Sqlite叫做關(guān)系型數(shù)據(jù)庫,CoreData 是一中OR-Mapping的思想 ,O代表對象Object,R代表relationship,Mapping代表映射,直譯過來就是對象關(guān)系映射,其實(shí)就是把對象的屬性和表中的字段自動映射,簡化程序員的負(fù)擔(dān),以面向?qū)ο蟮姆绞讲僮鲾?shù)據(jù)庫。ORMapping是一種思想,CoreData實(shí)現(xiàn)了這種思想,在Java中,hibernate 也是對ORMapping的一種實(shí)現(xiàn),只是利用java實(shí)現(xiàn)的。
CoreData 本質(zhì)還是數(shù)據(jù)庫,只不過使用起來更加面向?qū)ο?,不關(guān)注二維的表結(jié)構(gòu),而是只需要關(guān)注對象,純面向?qū)ο蟮臄?shù)據(jù)操作方式。我們直接使用數(shù)據(jù)庫的時候,如果向數(shù)據(jù)庫中插入數(shù)據(jù),一般是把一個對象的屬性和數(shù)據(jù)庫中某個表的字段一一對應(yīng),然后把對象的屬性存儲到具體的表字段中.取一條數(shù)據(jù)的時候,把表中的一行數(shù)據(jù)取出,同樣需要再封裝到對象的屬性中,這樣的方式有點(diǎn)繁瑣,不面向?qū)ο?。CoreData解決的問題就是不需要這個中間的轉(zhuǎn)換過程,看起來是直接把對象存儲進(jìn)去,并且取出來,不關(guān)心表的存在,實(shí)際內(nèi)部幫你做好了映射關(guān)系。

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

12、runloop和線程有什么關(guān)系?
一般來講,一個線程一次只能執(zhí)行一個任務(wù),執(zhí)行完成后線程就會退出。如果我們需要一個機(jī)制,讓線程能隨時處理事件但并不退出,通常的代碼邏輯是這樣的:

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

這種模型通常被稱作 Event Loop。 Event Loop 在很多系統(tǒng)和框架里都有實(shí)現(xiàn),比如 Node.js 的事件處理,比如 Windows 程序的消息循環(huán),再比如 OSX/iOS 里的 RunLoop。實(shí)現(xiàn)這種模型的關(guān)鍵點(diǎn)在于:如何管理事件/消息,如何讓線程在沒有處理消息時休眠以避免資源占用、在有消息到來時立刻被喚醒。
所以,RunLoop 實(shí)際上就是一個對象,這個對象管理了其需要處理的事件和消息,并提供了一個入口函數(shù)來執(zhí)行上面 Event Loop 的邏輯。線程執(zhí)行了這個函數(shù)后,就會一直處于這個函數(shù)內(nèi)部 "接受消息->等待->處理" 的循環(huán)中,直到這個循環(huán)結(jié)束(比如傳入 quit 的消息),函數(shù)返回

基本作用:
a 保持程序的持續(xù)運(yùn)行(ios程序?yàn)槭裁茨芤恢被钪粫?
b 處理app中的各種事件(比如觸摸事件、定時器事件【NSTimer】、selector事件【選擇器·performSelector···】)
c 節(jié)省CPU資源,提高程序性能,有事情就做事情,沒事情就休息

重要說明:
(1)如果沒有Runloop,那么程序一啟動就會退出,什么事情都做不了。
(2)如果有了Runloop,那么相當(dāng)于在內(nèi)部有一個死循環(huán),能夠保證程序的持續(xù)運(yùn)行
(3)main函數(shù)中的Runloop a 在UIApplication函數(shù)內(nèi)部就啟動了一個Runloop 該函數(shù)返回一個int類型的值 b 這個默認(rèn)啟動的Runloop是跟主線程相關(guān)聯(lián)的

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

14、你一般是如何調(diào)試Bug的?
1.在運(yùn)行過程中,如果出現(xiàn)EXC_BAD_ACCESS 異常,往往提示的信息很少或者沒有提示,啟用NSZombieEnabled后在控制臺能打印出更多的提示信息,便于debug,請注意,僵尸模式下的調(diào)試工作只能在模擬器中實(shí)現(xiàn),我們無法在物理設(shè)備上完成這一診斷流程.
2.異常斷點(diǎn),一般程序crash時Xcode一般會定位到main函數(shù)中,得不到詳細(xì)的crash信息,打上異常斷點(diǎn)后就極大可能定位到程序的crash處,利于debug。
3.一般來說,在創(chuàng)建工程的時候,應(yīng)該在Build Settings啟用Analyze During 'Build',這樣每次編譯時都會自動靜態(tài)分析。這樣的話,寫完一小段代碼之后,就馬上知道是否存在內(nèi)存泄露或其他bug問題,并且可以修bugs。
4.如果你想在運(yùn)行的時候查看APP是否存在內(nèi)存泄露,你可以使用Xcode上instruments工具上的Leaks模塊進(jìn)行內(nèi)存分析。但是有些內(nèi)存泄露是很難檢查出來,有時只有通過手動覆蓋dealloc方法,看它最終有沒有調(diào)用。

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

---------------------------------------------------------------------

第三波

1.死鎖的四個必要條件
產(chǎn)生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進(jìn)程使用。
(2) 請求與保持條件:一個進(jìn)程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進(jìn)程已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪。
(4) 循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
這四個條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之
一不滿足,就不會發(fā)生死鎖。

死鎖的經(jīng)典代碼:

lock.png

比如這個最簡單的OC命令行程序就會導(dǎo)致死鎖,運(yùn)行后不會看到任何結(jié)果。

回到上面的死鎖代碼中:首先明確的是:執(zhí)行這個dispatch_get_main_queue隊(duì)列的是主線程。執(zhí)行了dispatch_sync函數(shù)后,將block添加到了main_queue中,同時調(diào)用dispatch_syn這個函數(shù)的線程(也就是主線程)被阻塞,等待block執(zhí)行完成,而執(zhí)行主線程隊(duì)列任務(wù)的線程正是主線程,此時他處于阻塞狀態(tài),所以block永遠(yuǎn)不會被執(zhí)行,因此主線程一直處于阻塞狀態(tài)。因此這段代碼運(yùn)行后,并非卡在block中無法返回,而是根本無法執(zhí)行到這個block。

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

2.進(jìn)程與線程
這個其實(shí)是操作系統(tǒng)的問題,在網(wǎng)上看到個通俗精辟的例子:
開個QQ,開了一個進(jìn)程;開了迅雷,開了一個進(jìn)程。在QQ的這個進(jìn)程里,傳輸文字開一個線程、傳輸語音開了一個線程、彈出對話框又開了一個線程。所以運(yùn)行某個軟件,相當(dāng)于開了一個進(jìn)程。在這個軟件運(yùn)行的過程里(在這個進(jìn)程里),多個工作支撐的完成QQ的運(yùn)行,那么這“多個工作”分別有一個線程。所以一個進(jìn)程管著多個線程。通俗的講:“進(jìn)程是爹媽,管著眾多的線程兒子”...

3.OC 和 js 交互 OC怎么調(diào)用js函數(shù), JS怎么調(diào)用OC方法 ?
WebViewJavascriptBridge

4.代理和block的區(qū)別 ? block循環(huán)引用產(chǎn)生的原因, 以及怎么處理?
在定義一個類的property時候,為property選擇strong還是copy特別注意和研究明白的,如果property是NSString或者NSArray及其子類的時候,最好選擇使用copy屬性修飾。為什么呢?這是為了防止賦值給它的是可變的數(shù)據(jù),如果可變的數(shù)據(jù)發(fā)生了變化,那么該property也會發(fā)生變化。

代碼示例

@interface Person : NSObject
@property (strong, nonatomic) NSArray *bookArray1;
@property (copy, nonatomic) NSArray *bookArray2;
@end

//Person調(diào)用
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]。這下可以看出來區(qū)別了吧。

備注:使用strong,則person.bookArray1與可變數(shù)組books指向同一塊內(nèi)存區(qū)域,books內(nèi)容改變,導(dǎo)致person.bookArray1的內(nèi)容改變,因?yàn)閮烧呤峭粋€東西;而使用copy,person.bookArray2在賦值之前,將books內(nèi)容復(fù)制,創(chuàng)建一個新的內(nèi)存區(qū)域,所以兩者不是一回事,books的改變不會導(dǎo)致person.bookArray2的改變。

5.類別與擴(kuò)展的區(qū)別?
擴(kuò)展寫法上跟類別一致,只是括號中沒有類別描述。
“擴(kuò)展”可以添加屬性、變量,類別不能。

分類運(yùn)用場景舉例:想要收集每個頁面的啟動時間。
問題1:
項(xiàng)目中已經(jīng)有上百個頁面了,如果一個一個的加,浪費(fèi)時間不說,以后增加了新頁面,還需要添加方法
解決方法:
我們可以發(fā)現(xiàn)頁面都繼承了UIViewController,想要在每個頁面都執(zhí)行的代碼,可以寫在這些頁面的父類中。我們可以把代碼寫在UIViewController中。UIViewController是官方類,我們只能調(diào)用期接口,并不能修改他的實(shí)現(xiàn)。所以需要使用分類(category).
1.分類(category)的作用
1.1作用:可以在不修改原來類的基礎(chǔ)上,為一個類擴(kuò)展方法。
1.2最主要的用法:給系統(tǒng)自帶的類擴(kuò)展方法。
2.分類中能寫點(diǎn)啥?
2.1分類中只能添加“方法”,不能增加成員變量。
2.2分類中可以訪問原來類中的成員變量,但是只能訪問@protect和@public形式的變量。如果想要訪問本類中的私有變量,分類和子類一樣,只能通過方法來訪問。

分類(category)和類擴(kuò)展(extension)的關(guān)系
1.類擴(kuò)展(extension)是category的一個特例,有時候也被稱為匿名分類。他的作用是為一個類添加一些私有的成員變量和方法。
2.類擴(kuò)展能寫點(diǎn)啥?和分類不同,類擴(kuò)展即可以聲明成員變量又可以聲明方法。
3.類擴(kuò)展聽上去很復(fù)雜,但其實(shí)我們很早就認(rèn)識他了。你記得繼承自UIViewController的ViewController吧.

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

嚴(yán)格意義上來說,oc是沒有私有變量或者方法這一說的,不過我們可以通過延展來實(shí)現(xiàn)這個私有方法或者變量。類擴(kuò)展可以定義在.m文件中,這種擴(kuò)展方式中定義的變量都是私有的,也可以定義在.h文件中,這樣定義的代碼就是共有的,類擴(kuò)展在.m文件中聲明私有方法是非常好的方式。
類擴(kuò)展中添加的新方法,一定要實(shí)現(xiàn)。categorygory中沒有這種限制。

6.怎么實(shí)現(xiàn)一個精準(zhǔn)的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的事件是不會響應(yīng)的,而子線程中runloop默認(rèn)沒有啟動
    // 讓線程執(zhí)行一個周期性的任務(wù),如果不啟動run loop, 線程跑完就可能被系統(tǒng)釋放了
    [[NSRunLoop currentRunLoop] run];// 如果沒有這句,doAnything將不會執(zhí)行?。?

runloopmode是一個集合,包括監(jiān)聽:事件源,定時器,以及需通知的runloop observers
模式包括:
default模式:幾乎包括所有輸入源(除NSConnection) NSDefaultRunLoopMode模式
mode模式:處理modal panels
connection模式:處理NSConnection事件,屬于系統(tǒng)內(nèi)部,用戶基本不用
event tracking模式:如組件拖動輸入源 UITrackingRunLoopModes 不處理定時事件
common modes模式:NSRunLoopCommonModes 這是一組可配置的通用模式。將input sources與該模式關(guān)聯(lián)則同時也將input sources與該組中的其它模式進(jìn)行了關(guān)聯(lián)。

7.當(dāng)app越做越大時, 會發(fā)現(xiàn)App冷啟動時, 速度很慢, 你有沒有遇到過? 你是怎么優(yōu)化的?
一般而言,啟動時間是指從用戶點(diǎn)擊 APP 那一刻開始到用戶看到第一個界面這中間的時間。我們進(jìn)行優(yōu)化的時候,我們將啟動時間分為 pre-main 時間和 main 函數(shù)到第一個界面渲染完成時間這兩個部分。
大家都知道 APP 的入口是 main 函數(shù),在 main 之前,我們自己的代碼是不會執(zhí)行的。而進(jìn)入到 main 函數(shù)以后,我們的代碼都是從didFinishLaunchingWithOptions開始執(zhí)行的,所以很明顯,優(yōu)化這兩部分的思路是不一樣的。
為了方便起見,我們將 pre-main 時間成為 t1 時間,而將main 函數(shù)到第一個界面渲染完成這段時間稱為 t2 時間。 然后用profile 工具來分析出哪些代碼是耗時的。

Profiler.jpg

8.在適配 iOS 11時, 經(jīng)常出現(xiàn)的問題
都有哪些? 以及你是怎么解決的

適配點(diǎn)一:項(xiàng)目中使用狀態(tài)欄中圖標(biāo)判斷當(dāng)前網(wǎng)絡(luò)的具體狀態(tài)

networkStatus.png

此時可以看到運(yùn)行崩潰了,因?yàn)閺膇Phone X取出來之后只有view層級的信息,所以采用以下方法確定2G/3G/4G

NSArray *typeStrings2G = @[CTRadioAccessTechnologyEdge,
                               CTRadioAccessTechnologyGPRS,
                               CTRadioAccessTechnologyCDMA1x];
     
    NSArray *typeStrings3G = @[CTRadioAccessTechnologyHSDPA,
                               CTRadioAccessTechnologyWCDMA,
                               CTRadioAccessTechnologyHSUPA,
                               CTRadioAccessTechnologyCDMAEVDORev0,
                               CTRadioAccessTechnologyCDMAEVDORevA,
                               CTRadioAccessTechnologyCDMAEVDORevB,
                               CTRadioAccessTechnologyeHRPD];
     
    NSArray *typeStrings4G = @[CTRadioAccessTechnologyLTE];
    // 該 API 在 iOS7 以上系統(tǒng)才有效
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        CTTelephonyNetworkInfo *teleInfo= [[CTTelephonyNetworkInfo alloc] init];
        NSString *accessString = teleInfo.currentRadioAccessTechnology;
        if ([typeStrings4G containsObject:accessString]) {
            NSLog(@"4G網(wǎng)絡(luò)");
        } else if ([typeStrings3G containsObject:accessString]) {
            NSLog(@"3G網(wǎng)絡(luò)");
        } else if ([typeStrings2G containsObject:accessString]) {
            NSLog(@"2G網(wǎng)絡(luò)");
        } else {
            NSLog(@"未知網(wǎng)絡(luò)");
        }
    } else {
        NSLog(@"未知網(wǎng)絡(luò)");
    }

適配點(diǎn)二:解決這個問題后項(xiàng)目跑起來發(fā)現(xiàn),整個app界面上下各空出大概40pt的高度.造成這個的原因是啟動圖使用 Launch Images Source 設(shè)置的時候沒有勾選并設(shè)置對應(yīng)的圖片.
但是即使按照上面的操作進(jìn)行之后,會發(fā)現(xiàn)底部 UITabBar 依舊是高出一些高度,查看層級關(guān)系后發(fā)現(xiàn),同樣是由于安全區(qū)的原因,UITabBar 高度由49pt變成了83pt,因此這里也要對iPhone X 及其模擬器進(jìn)行適配

適配點(diǎn)三:iPhone X 只有 faceID,沒有touchID,如果in的應(yīng)用有使用到 touchID 解鎖的地方,這里要根據(jù)機(jī)型進(jìn)行相應(yīng)的適配

適配點(diǎn)四: iPhone X更大的坑是屏幕的適配Safe area.

9.談?wù)刬nstancetype和id的異同
1、相同點(diǎn)
都可以作為方法的返回類型
2、不同點(diǎn)
①instancetype可以返回和方法所在類相同類型的對象,id只能返回未知類型的對象;
②instancetype只能作為返回值,不能像id那樣作為參數(shù)

10.isKindOfClass和isMemberOfClass的區(qū)別
isKindOfClass來確定一個對象是否是一個類的成員,或者是派生自該類的成員
isMemberOfClass只能確定一個對象是否是當(dāng)前類的成員

第四波
這套題目是我在知乎上看到的,是MrPeak大大出的題目,好開森哦,嘗試著來回答下,還希望各位大佬多多指教.
一份"有點(diǎn)難"的iOS面試題 .

其它知識點(diǎn)匯總:
iOS面試--GCD常見用法.
iOS面試-Runloop簡單介紹.
iOS面試-Runtime簡介.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容