第一波:
自答:
說實(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中的閉包傳值
OC中的閉包傳值
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,internal柒爵,fileprivate役电,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ū)別:
- 效率肯定是delegate比nsnotification高麻掸。
- 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í)例笨奠。
---------------------------------------------------------------------
第二波:
自答: 這套面試題相較于第一波的面試題來說,難度有所降低.嘗試解答一下:
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(核心動畫)
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ū)ο螅魂P(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ū)ο蟮蜕ⅰoreData解決的問題就是不需要這個中間的轉(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)典代碼:
比如這個最簡單的OC命令行程序就會導(dǎo)致死鎖,運(yùn)行后不會看到任何結(jié)果蹈垢。
回到上面的死鎖代碼中:首先明確的是:執(zhí)行這個dispatch_get_main_queue隊列的是主線程慷吊。執(zhí)行了dispatch_sync函數(shù)后,將block添加到了main_queue中曹抬,同時調(diào)用dispatch_syn這個函數(shù)的線程(也就是主線程)被阻塞溉瓶,等待block執(zhí)行完成,而執(zhí)行主線程隊列任務(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í)行G诨椤摹量!
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 工具來分析出哪些代碼是耗時的坐榆。
8.在適配 iOS 11時, 經(jīng)常出現(xiàn)的問題
都有哪些? 以及你是怎么解決的
適配點(diǎn)一:項(xiàng)目中使用狀態(tài)欄中圖標(biāo)判斷當(dāng)前網(wǎng)絡(luò)的具體狀態(tài)
此時可以看到運(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簡介.