常見面試題鏈接: http://www.reibang.com/p/25324d04797d
高級iOS面試題:http://www.reibang.com/p/c10a4701ac28
進(jìn)階版:
1.介紹下App啟動的完成過程澡罚?
1. App啟動過程
? 解析Info.plist
? 加載相關(guān)信息睛廊,例如如閃屏
? 沙箱建立谭梗、權(quán)限檢查
Mach-O加載
? 如果是胖二進(jìn)制文件启泣,尋找合適當(dāng)前CPU類別的部分
? 加載所有依賴的Mach-O文件(遞歸調(diào)用Mach-O加載的方法)
? 定位內(nèi)部养叛、外部指針引用煮甥,例如字符串鬓照、函數(shù)等
? 執(zhí)行聲明為__attribute__((constructor))的C函數(shù)
? 加載類擴展(Category)中的方法
? C++靜態(tài)對象加載、調(diào)用ObjC的 +load 函數(shù)
程序執(zhí)行
· 1.main函數(shù)
· 2.執(zhí)行UIApplicationMain函數(shù)
· 1.創(chuàng)建UIApplication對象
· 2.創(chuàng)建UIApplicationDelegate對象并復(fù)制
· 3.讀取配置文件info.plist,設(shè)置程序啟動的一些屬性当娱,(關(guān)于info.plist的內(nèi)容可網(wǎng)上搜索下)
· 4.創(chuàng)建應(yīng)用程序的Main Runloop循環(huán)
· 3.UIApplicationDelegate對象開始處理監(jiān)聽到的事件
· 1.程序啟動成功之后吃既,首先調(diào)用application:didFinishLaunchingWithOptions:方法,
· 如果info.plist文件中配置了啟動storyboard文件名,則加載storyboard文件跨细。
· 如果沒有配置鹦倚,則根據(jù)代碼來創(chuàng)建UIWindow--->UIWindow的rootViewController-->顯示
2.比如App啟動過慢,你可能想到的因素有哪些冀惭?
利用DYLD_PRINT_STATISTICS分析main()函數(shù)之前的耗時
? 重新梳理架構(gòu)震叙,減少動態(tài)庫、ObjC類的數(shù)目云头,減少Category的數(shù)目
? 定期掃描不再使用的動態(tài)庫捐友、類淫半、函數(shù)溃槐,例如每兩個迭代一次
? 用dispatchonce()代替所有的__attribute__((constructor))函數(shù)、C++靜態(tài)對象初始化科吭、ObjC的+load
? 在設(shè)計師可接受的范圍內(nèi)壓縮圖片的大小昏滴,會有意外收獲
? 利用錨點分析applicationWillFinishLaunching的耗時
? 將不需要馬上在applicationWillFinishLaunching執(zhí)行的代碼延后執(zhí)行
? rootViewController的加載对人,適當(dāng)將某一級的childViewController或subviews延后加載
? 如果你的App可能會被后臺拉起并冷啟動,可考慮不加載rootViewController
? 不應(yīng)放過的一些小細(xì)節(jié)
? 異步操作并不影響指標(biāo),但有可能影響交互體驗,例如大量網(wǎng)絡(luò)請求導(dǎo)致數(shù)據(jù)擁堵
? 有時候一些交互上的優(yōu)化比技術(shù)手段效果更明顯,視覺上的快決不是冰冷的數(shù)據(jù)可以解釋的萍恕,好好和你們的設(shè)計師談?wù)剟赢?
3.多線程有哪幾種?你更傾向于哪一種绳姨?
image.png
NSOperation相對于GCD:
1购撼,NSOperation擁有更多的函數(shù)可用,具體查看api。NSOperationQueue 是在GCD基礎(chǔ)上實現(xiàn)的,只不過是GCD更高一層的抽象。
2,在NSOperationQueue中,可以建立各個NSOperation之間的依賴關(guān)系。
3绩郎,NSOperationQueue支持KVO状植。可以監(jiān)測operation是否正在執(zhí)行(isExecuted)津畸、是否結(jié)束(isFinished)振定,是否取消(isCanceld)
4,GCD 只支持FIFO 的隊列肉拓,而NSOperationQueue可以調(diào)整隊列的執(zhí)行順序(通過調(diào)整權(quán)重)后频。NSOperationQueue可以方便的管理并發(fā)、NSOperation之間的優(yōu)先級暖途。
使用NSOperation的情況:各個操作之間有依賴關(guān)系卑惜、操作需要取消暫停、并發(fā)管理驻售、控制操作之間優(yōu)先級露久,限制同時能執(zhí)行的線程數(shù)量.讓線程在某時刻停止/繼續(xù)等。
使用GCD的情況:一般的需求很簡單的多線程操作欺栗,用GCD都可以了匾鸥,簡單高效甜孤。
從編程原則來說燕侠,一般我們需要盡可能的使用高等級厉萝、封裝完美的API眶痰,在必須時才使用底層API瘤旨。
4.TCP UDP區(qū)別?
1.基于連接與無連接竖伯;
2.對系統(tǒng)資源的要求(TCP較多存哲,UDP少);
3.UDP程序結(jié)構(gòu)較簡單七婴;
4.流模式與數(shù)據(jù)報模式 祟偷;
5.TCP保證數(shù)據(jù)正確性,UDP可能丟包打厘,TCP保證數(shù)據(jù)順序修肠,UDP不保證
TCP:面向連接、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)户盯、用于傳輸大量數(shù)據(jù)(流模式)嵌施、速度慢,建立連接需要開銷較多(時間莽鸭,系統(tǒng)資源)吗伤。
UDP:面向非連接、傳輸不可靠硫眨、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)足淆、速度快。
5.哈希表是如何實現(xiàn)的?如何解決地址沖突巧号?
哈希表是也是通過數(shù)組來實現(xiàn)的族奢,首先對key值進(jìn)行哈希化得到一個整數(shù)丹鸿,然后對整數(shù)進(jìn)行計算歹鱼,得到一個數(shù)組中的下標(biāo),
然后進(jìn)行存取卜高,解決地址沖突常用方法有開放定址法和鏈表法弥姻。
runtime源碼的存放weak指針哈希表使用的就是開放定址法,Java里的HashMap使用的是鏈表法掺涛。
6 Runloop
?為什么只有主線程的runloop是開啟的
?為什么只在主線程刷新UI
?PerformSelector和runloop的關(guān)系
?如何使線程蓖ザ兀活
1.為什么只有主線程的runloop是開啟的
mian()函數(shù)中調(diào)用UIApplicationMain,這里會創(chuàng)建一個主線程薪缆,用于UI處理秧廉,為了讓程序可以
一直運行并接收事件,所以在主線程中開啟一個runloop拣帽,讓主線程常駐.
2.為什么只在主線程刷新UI
我們所有用到的UI都是來自于UIKit這個基礎(chǔ)庫.因為objc不是一門線程安全的語言所以存在多
線程讀寫不同步的問題,如果使用加鎖的方式操作系統(tǒng)開銷很大,會耗費大量的系統(tǒng)資源(內(nèi)存疼电、
時間片輪轉(zhuǎn)、cpu處理速度…)减拭,加上上面講到的系統(tǒng)事件的接收處理都在主線程,如果UI異步線
程的話 還會存在 同步處理事件的問題,所以多點觸摸手勢等一些事件要保持和UI在同一個線程
相對是最優(yōu)解.
另一方面是 屏幕的渲染是 60幀(60Hz/秒), 也就是1秒鐘回調(diào)60次的頻率,(iPad Pro 是120Hz/
秒),我們的runloop 理想狀態(tài)下也會按照時鐘周期 回調(diào)60次(iPad Pro 120次), 這么高頻率的調(diào)用
是為了 屏幕圖像顯示能夠垂直同步 不卡頓.在異步線程的話是很難保證這個處理過程的同步更
新. 即便能保證的話 相對主線程而言 系統(tǒng)資源開銷 線程調(diào)度等等將會占據(jù)大部分資源和在同一
個線程只專門干一件事有點得不償失.
3.PerformSelector和runloop的關(guān)系
當(dāng)調(diào)用NSObect的 performSelector:相關(guān)的時候,內(nèi)部會創(chuàng)建一個timer定時器添加到當(dāng)前線程的
runloop中,如果當(dāng)前線程沒有啟動runloop,則該方法不會被調(diào)用.
開發(fā)中遇到最多的問題就是這個performSelector: 導(dǎo)致對象的延遲釋放,這里開發(fā)過程中注意一
下,可以用單次的NSTimer替代.
4.如何使線程北尾颍活?
想要線程迸》啵活的話就開啟該線程的runloop即可,注意:在NSThread執(zhí)行的方法中添加while(true)
{}修陡,這樣是模擬runloop的運行原理,結(jié)合GCD的信號量可霎,在{}代碼塊中處理任務(wù).
但是注意 開啟runloop的方法要正確
如下代碼
//測試開啟線程
- (void)memoryTest {
for (int i = 0; i < 100000; ++i) {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
[self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
}
}
//線程停止
- (void)stopThread {
CFRunLoopStop(CFRunLoopGetCurrent());
NSThread *thread = [NSThread currentThread];
[thread cancel];
}
//運行線程的runloop 注意 意添加的那個空port,否則會出現(xiàn)內(nèi)存泄露
- (void)run {
@autoreleasepool {
NSLog(@"current thread = %@", [NSThread currentThread]);
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
if (!self.emptyPort) {
self.emptyPort = [NSMachPort port];
}
[runLoop addPort:self.emptyPort forMode:NSDefaultRunLoopMode];
[runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
}
}
//下列代碼用于模擬線程內(nèi)部做的一些耗時任務(wù)
- (void)printSomething {
NSLog(@"current thread = %@", [NSThread currentThread]);
[self performSelector:@selector(printSomething) withObject:nil afterDelay:1];
}
//模擬手動點擊按鈕 讓 runloop停掉
- (void)stopButtonDidClicked:(id)sender {
[self performSelector:@selector(stopRunloop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)stopRunloop {
CFRunLoopStop(CFRunLoopGetCurrent());
}