前言
在投遞簡(jiǎn)歷之前,就是所謂的寒冬將至,開個(gè)年會(huì)都是守望寒冬,然后我身邊的準(zhǔn)備跳槽的大佬們,都是有幾分涼意,不過(guò)我還好,總感覺(jué)一個(gè)人吃飽,全家不餓,??O(∩_∩)O哈!沒(méi)想那么多,直接就全身投入,找工作?,F(xiàn)在做個(gè)回顧吧,為自己,也為路過(guò)的各位大俠。
先說(shuō)一個(gè)問(wèn)題,是寒冬嗎?我真沒(méi)覺(jué)得,說(shuō)自己的一個(gè)親身體會(huì),不夸張的說(shuō),基本上是每天2家,且持續(xù)一個(gè)月,當(dāng)然是距離可以接受,公司小中大都有的,我感覺(jué)不是互聯(lián)網(wǎng)的寒冬,是自己的寒冬,有一句說(shuō)的很好,人生就兩季,努力是旺季,不努力是淡季!我感覺(jué)很有道理??~~~~
現(xiàn)在面試要求高在要會(huì)各種語(yǔ)言,另外要很深入,要夠底層,要懂?dāng)?shù)據(jù)結(jié)構(gòu)與算法之美(面試過(guò)的都會(huì)體會(huì)什么是真是一言難盡吧),看一些大佬,進(jìn)入一個(gè)大廠,也寫了自己的準(zhǔn)備,我感覺(jué)真是有付出有回報(bào)的,也看出自己的一些不足吧!so,革命尚未成功,同志們?nèi)孕枧Ψィ。?/p>
知識(shí)點(diǎn)總結(jié)
因?yàn)樽约核接邢?,可能有些路過(guò)的大佬感覺(jué)比較簡(jiǎn)單,我也總結(jié)了下,請(qǐng)飄過(guò)~~還有一些答案僅供參考,如有錯(cuò)誤,請(qǐng)不吝賜教,在此謝過(guò)??---->
+(void)initinstance 與 +(void)load兩個(gè)方法的區(qū)別于比較//小紅書面試問(wèn)題\
先看下面表格兩者的區(qū)別,后續(xù)會(huì)繼續(xù)介紹
| +load | +initialize | |
|---|---|---|
| 調(diào)用時(shí)機(jī) | 被添加runtime時(shí) | 收到第一條消息時(shí),可能永遠(yuǎn)不調(diào)用 |
| 調(diào)用順序 | 父類->子類->分類 | 父類->子類 |
| 調(diào)用次數(shù) | 1次 | 多次 |
| 是否需要顯示調(diào)用父類實(shí)現(xiàn) | 否 | 否 |
| 是否沿用父類的實(shí)現(xiàn) | 否 | 是 |
| 分類中的實(shí)現(xiàn) | 類和分類都執(zhí)行 | |
相同點(diǎn):
- 系統(tǒng)都執(zhí)行一次。
- 假如父類和子類都被調(diào)用,父類在子類之前被調(diào)用
不同點(diǎn):
- load 方法會(huì)在加載類的時(shí)候就被調(diào)用,也就是 ios 應(yīng)用啟動(dòng)的時(shí)候,就會(huì)加載所有的類,就會(huì)調(diào)用每個(gè)類的 + load 方法。
- +initialize 這個(gè)方法會(huì)在 第一次初始化這個(gè)類之前 被調(diào)用,我們用它來(lái)初始化靜態(tài)變量
- load 會(huì)在main()函數(shù)之前調(diào)用。initialize 則在類實(shí)例化 或 類方法被調(diào)用時(shí)調(diào)用;
- 如果子類中沒(méi)有initialize方法,則會(huì)再次調(diào)用父類的initialize方法,類別會(huì)覆蓋主類的initialize,load則不會(huì)被覆蓋
- load順序在 initialize之前;
- initialize 方法的調(diào)用看起來(lái)會(huì)更合理,通常在它里面寫代碼比在 + load 里寫更好,因?yàn)樗菓姓{(diào)用的,也有可能完全不被調(diào)用。類第一次被加載時(shí),
- 類接收消息時(shí),運(yùn)行時(shí)會(huì)先檢查 + initialize 有沒(méi)有被調(diào)用過(guò)。如果沒(méi)有,會(huì)在消息被處理前調(diào)用
--->>>>
initialize 最終是通過(guò) objc_msgSend 來(lái)執(zhí)行的,objc_msgSend 會(huì)執(zhí)行一系列方法查找,并且 Category 的方法會(huì)覆蓋類中的方法
load 是在被添加到 runtime 時(shí)開始執(zhí)行,父類最先執(zhí)行,然后是子類,最后是 Category。又因?yàn)槭侵苯荧@取函數(shù)指針來(lái)執(zhí)行,不會(huì)像 objc_msgSend 一樣會(huì)有方法查找的過(guò)程。
---->>>>
怎么實(shí)現(xiàn)單例, 2種方法實(shí)現(xiàn)//喜馬拉雅面試問(wèn)題\
//第一種方式: 線程安全的單例2(不推薦 效率低)
+ (instancetype)shareSingleton2 {
@synchronized(self) {
if (!singleton) {
singleton = [[self alloc]init];
}
}
return singleton;
}
//第二種方式 線程安全的單例
+ (instancetype)shareSingleton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc]init];
});
return singleton;
}
然而僅僅知道這些是不夠的,說(shuō)了上面的,面試官會(huì)繼續(xù)問(wèn)單例,怎么實(shí)現(xiàn)的,加鎖了嗎?單例什么時(shí)候釋放?然后你就會(huì)一臉懵~有同感的舉個(gè)手????????????
-
單例,怎么實(shí)現(xiàn)的,加鎖了嗎?單例什么時(shí)候釋放
其實(shí)在上面的兩個(gè)單例的創(chuàng)建中,@synchronized是一個(gè)鎖,后面會(huì)講到,就是說(shuō)第一種是通過(guò)加鎖的方式來(lái)實(shí)現(xiàn),而第二種解析如下:
GCD創(chuàng)建:dispatch_once中dispatch_once_t類型為typedef long
? onceToken= 0,線程執(zhí)行dispatch_once的block中代碼
? onceToken= -1,線程跳過(guò)dispatch_once的block中代碼不執(zhí)行
? onceToken= 其他值,線程被線程被阻塞,等待onceToken值改變
用途:限制創(chuàng)建,提供全局調(diào)用,節(jié)約資源和提高性能。參考
常見的應(yīng)用場(chǎng)景:
? UIApplication
? NSNotificationCenter
? NSFileManager
? NSUserDefaults
? NSURLCache
? NSHTTPCookieStorage
那么單例是怎么銷毀的呢?如下:
方法一:
+(void)attemptDealloc{
[_instance release]; //mrc 需要釋放,當(dāng)然你就不能重寫release的方法了.
_instance = nil;
}
方法二:
1. 必須把static dispatch_once_t onceToken; 這個(gè)拿到函數(shù)體外,成為全局的.
2.
+(void)attempDealloc{
onceToken = 0; // 只有置成0,GCD才會(huì)認(rèn)為它從未執(zhí)行過(guò).它默認(rèn)為0.這樣才能保證下次再次調(diào)用shareInstance的時(shí)候,再次創(chuàng)建對(duì)象.
[_instance release];
_instance = nil;
}
數(shù)據(jù)持久化
下面說(shuō)下數(shù)據(jù)持久化吧?如果是在2年前,你說(shuō)了數(shù)據(jù)持久化有NSUserDefaults,plist,歸檔,CoreData巴拉巴拉,感覺(jué)這位童靴還闊以,但是現(xiàn)在就有點(diǎn)low了??,你懂得~
面試大佬會(huì)問(wèn)有幾種?然后每種有什么不同?什么能存儲(chǔ)什么不能存儲(chǔ)?每個(gè)在具體使用應(yīng)該注意什么?等等,問(wèn)到你懷疑人生????????
屬性列表(plist存儲(chǔ))通常叫做plist文件,用于存儲(chǔ)在程序中不經(jīng)常修改、數(shù)據(jù)量小的數(shù)據(jù),不支持自定義對(duì)象存儲(chǔ),支持?jǐn)?shù)據(jù)存儲(chǔ)的類型為:Array,Dictionary,String,Number,Data,Date,Boolean,通常用來(lái)存放接口名、城市名、銀行名稱、表情名等極少修改的數(shù)據(jù)
plist文件是將某些特定的類,通過(guò)xml的方式保存在目錄中。偏好設(shè)置(NSUserDefaults)
用于存儲(chǔ)用戶的偏好設(shè)置,同樣適合于存儲(chǔ)輕量級(jí)的用戶數(shù)據(jù),數(shù)據(jù)會(huì)自動(dòng)保存在沙盒的Libarary/Preferences目錄下,本質(zhì)上就是一個(gè)plist文件,所以同樣的不支持自定義對(duì)象存儲(chǔ),支持?jǐn)?shù)據(jù)存儲(chǔ)的類型為:Array,Dictionary,String,Number,Data,Date,Boolean,可以用做檢查版本是否更新、是否啟動(dòng)引導(dǎo)頁(yè)、自動(dòng)登錄、版本號(hào)等等,需要注意的是NSUserDefaults是定時(shí)的將緩存中的數(shù)據(jù)寫入磁盤,并不是即時(shí)寫入,為了防止在寫完NSUserDefaults后,程序退出導(dǎo)致數(shù)據(jù)的丟失,可以在寫入數(shù)據(jù)后使用synchronize強(qiáng)制立即將數(shù)據(jù)寫入磁盤
如果這里你沒(méi)有調(diào)用synchronize方法的話,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用synchronize方法。
PS: 在這里說(shuō)了小問(wèn)題,就是有面試官會(huì)問(wèn),你在開發(fā)中用NSUserDefaults有沒(méi)有什么坑?你可以這樣答:比如你存儲(chǔ)一個(gè)值時(shí),沒(méi)有進(jìn)行及時(shí)的調(diào)用synchronize方法,然后此時(shí)程序就crash了或者強(qiáng)制殺死,那么你再下次去取值的時(shí)候,就會(huì)取不到你之前存儲(chǔ)的值,路過(guò)的大佬可以試下~~??????
歸檔序列化存儲(chǔ)
歸檔可以直接將對(duì)象存儲(chǔ)為文件,也可將文件直接解歸檔為對(duì)象,相對(duì)于plist文件與偏好設(shè)置數(shù)據(jù)的存儲(chǔ)更加多樣,支持自定義的對(duì)象存儲(chǔ),歸檔后的文件是加密的,也更加的安全,文件存儲(chǔ)的位置可以自定義。
遵守NSCoding或者NSSecureCoding協(xié)議沙盒存儲(chǔ)
可以提高程序的體驗(yàn)度,為用戶節(jié)約數(shù)據(jù)流量,主要在用戶閱讀書籍、聽音樂(lè)、看視頻等,在沙盒中做數(shù)據(jù)的存儲(chǔ),主要包含文件夾:Documents: 最常用的目錄,存放重要的數(shù)據(jù),iTunes同步時(shí)會(huì)備份該目錄Library/Caches: 一般存放體積大,不重要的數(shù)據(jù),iTunes同步時(shí)不會(huì)備份該目錄Library/Preferences: 存放用戶的偏好設(shè)置,iTunes同步時(shí)會(huì)備份該目錄tmp: 用于存放臨時(shí)文件,在程序未運(yùn)行時(shí)可能會(huì)刪除該文件夾中的數(shù)據(jù),iTunes同步時(shí)不會(huì)備份該目錄Core Data
Core Data是框架,并不是數(shù)據(jù)庫(kù),該框架提供了對(duì)象關(guān)系的映射功能,使得能夠?qū)C對(duì)象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)庫(kù)中的數(shù)據(jù)還原成OC對(duì)象,在轉(zhuǎn)換的過(guò)程中不需要編寫任何的SQL語(yǔ)句,在Core Data中有三個(gè)重要的概念:
NSPersistentStoreCoordinator:持久化存儲(chǔ)協(xié)調(diào)器,在NSPersistentStoreCoordinator中包含了持久化存儲(chǔ)區(qū),在持久化存儲(chǔ)區(qū)中包含了數(shù)據(jù)表中的很多數(shù)據(jù),持久化存儲(chǔ)區(qū)的設(shè)置通常選擇NSSQLiteStoreType,也就是選擇SQLite數(shù)據(jù)庫(kù)
NSManagedObjectModel:托管對(duì)象模型,用于描述數(shù)據(jù)結(jié)構(gòu)的模型SQLite3
SQLite是輕量級(jí)的數(shù)據(jù)庫(kù),占用資源很少,最初是用于嵌入式的系統(tǒng),在iOS中使用SQLite,需要加入"libsqlite3.tbd"依賴庫(kù)并導(dǎo)入頭文件。不應(yīng)該頻繁的打開關(guān)閉數(shù)據(jù)庫(kù),有可能會(huì)影響性能, 應(yīng)在啟動(dòng)程序時(shí)打開數(shù)據(jù)庫(kù),在退出程序是關(guān)閉數(shù)據(jù)庫(kù)FMDB
FMDB以O(shè)C的方式封裝了SQLite的C語(yǔ)言API,減去了冗余的C語(yǔ)言代碼,使得API更具有OC的風(fēng)格,更加的面向?qū)ο?,相?duì)于Core Data框架更加的輕量級(jí),F(xiàn)MDB還提供了多線程安全的數(shù)據(jù)庫(kù)操作方法,在FMDB中有三個(gè)重要的概念:
FMDatabase:一個(gè)FMDatabase就代表一個(gè)SQLite數(shù)據(jù)庫(kù),執(zhí)行sql語(yǔ)句
FMResultSet:執(zhí)行查詢后的結(jié)果集
FMDatabaseQueue:用于在多線程中執(zhí)行多個(gè)查詢或更新,安全的
===
緊接著說(shuō)下CoreData吧?它總是比你知道的還要多?
CoreData中的多線程問(wèn)題
主要推薦的實(shí)施方案,也是最優(yōu)方案,如下:
1.使用一個(gè)NSPersistentStoreCoordinator,以及兩個(gè)獨(dú)立的Contexts,一個(gè)context負(fù)責(zé)主線程與UI協(xié)作,一個(gè)context在后臺(tái)負(fù)責(zé)耗時(shí)的處理,用Notifications的方式通知主線程的NSManagedObjectContext進(jìn)行mergeChangesFromContextDidSaveNotification操作
2.后臺(tái)線程做讀寫更新,而主線程只讀
3.CoreData中的NSManagedObjectContext在多線程中不安全,如果想要多線程訪問(wèn)CoreData的話,最好的方法是一個(gè)線程一個(gè)NSManagedObjectContext,每個(gè)NSManagedObjectContext對(duì)象實(shí)例都可以使用同一個(gè)NSPersistentStoreCoordinator實(shí)例,這個(gè)實(shí)例可以很安全的順序訪_問(wèn)永久存儲(chǔ),這是因?yàn)镹SManagedObjectContext會(huì)在便用NSPersistentStoreCoordinator前上鎖。ios5.0為NSManagedObjectContext提供了initWithConcurrentcyType方法,其中的一個(gè)NSPrivateQueueConcurrencyType,會(huì)自動(dòng)的創(chuàng)建一個(gè)新線程來(lái)存放NSManagedObjectContext而且它還會(huì)自動(dòng)創(chuàng)建NSPersistentStoreCoordinator,
CoreData里面還帶有一個(gè)通知NSManagedObjectContextDidSaveNotification,主要監(jiān)聽NSManagedObjectContext的數(shù)據(jù)是否改變,并合并數(shù)據(jù)改變到相應(yīng)context。
面試官問(wèn)的Context是那兩種?這個(gè)面試官問(wèn)的應(yīng)該是用到的那兩個(gè)Type?
答:NSConfinementConcurrencyType NSMainQueueConcurrencyType
//創(chuàng)建并行的NSManagedObjectContext對(duì)象
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
ps:NSConfinementConcurrencyType (或者不加參數(shù),默認(rèn)就是這個(gè))NSMainQueueConcurrencyType (表示只會(huì)在主線程中執(zhí)行)
接著談?wù)剶?shù)據(jù)庫(kù)的優(yōu)化問(wèn)題,可以通過(guò)以下幾點(diǎn)進(jìn)行優(yōu)化
FMDB事務(wù)批量更新數(shù)據(jù)庫(kù)速度問(wèn)題。(親測(cè)可以呀---740條數(shù)據(jù)用和不用事務(wù)效率差別20倍+)
寫同步(synchronous)
在SQLite中,數(shù)據(jù)庫(kù)配置的參數(shù)都由編譯指示(pragma)來(lái)實(shí)現(xiàn)的,而其中synchronous選項(xiàng)有三種可選狀態(tài),分別是full、normal、off
設(shè)置為synchronous OFF (0)時(shí),SQLite在傳遞數(shù)據(jù)給系統(tǒng)以后直接繼續(xù)而不暫停一條SQL語(yǔ)句插入多條數(shù)據(jù)
在事務(wù)中進(jìn)行插入處理。
數(shù)據(jù)有序插入。
再說(shuō)下什么是事務(wù)?\英語(yǔ)流利說(shuō)總監(jiān)面試問(wèn)題//
事務(wù):
- 作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,而這些邏輯工作單元需要具有原子性,一致性,隔離性和持久性
- 是并發(fā)控制的基本單元。所謂的事務(wù),它是一個(gè)操作序列,這些操作要么都執(zhí)行,要么都不執(zhí)行,它是一個(gè)不可分割的工作單元。例如,銀行轉(zhuǎn)賬工作:從一個(gè)賬號(hào)扣款并使另一個(gè)賬號(hào)增款,這兩個(gè)操作要么都執(zhí)行,要么都不執(zhí)行。所以,應(yīng)該把它們看成一個(gè)事務(wù)。
- 事務(wù)是一種機(jī)制,用于維護(hù)數(shù)據(jù)庫(kù)的完整性
事務(wù)基本特征:
原子性(Atomicity):事務(wù)的個(gè)元素是不可分的,事務(wù)是一個(gè)完整的操作,一個(gè)操作序列,要么都執(zhí)行,要么都不執(zhí)行
一致性(Consistemcy):事務(wù)完成時(shí),數(shù)據(jù)必須是一致的,保證數(shù)據(jù)的無(wú)損
隔離性(Isolation):多個(gè)事務(wù)彼此隔離,事務(wù)必須是獨(dú)立的,任何事務(wù)都不應(yīng)該受影響
持久性(Durability):事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久的,該修改即使出現(xiàn)系統(tǒng)故障也將一直保留,真實(shí)的修改了數(shù)據(jù)庫(kù)
五種 Mach-O 類型的淺要分析
這個(gè)面試題針對(duì)我自己的簡(jiǎn)歷,可略過(guò)~
在制作Framework時(shí),可以設(shè)置framework中的Mach-O Type,不手動(dòng)修改的默認(rèn)配置即為 Dynamic Library,在SDK中默認(rèn)使用的是 Relocatable Object File

Executable: 可執(zhí)行二進(jìn)制文件
dynamic Library 動(dòng)態(tài)庫(kù)
Bundle : 非獨(dú)立二進(jìn)制文件,顯示加載
static Library 靜態(tài)庫(kù)
Relocatable Object File: 可重定位的目標(biāo)文件,中間結(jié)果
Relocatable Object File 是組裝靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的零件,而靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)就是可執(zhí)行二進(jìn)制文件的組件。這里用了零件和組件的概念,零件是不可缺少的,組件則是可選的
Dynamic Library 更靈活;復(fù)用性更強(qiáng);且就安全來(lái)說(shuō),統(tǒng)一放置在 Payload/Framework 目錄下的自建的動(dòng)態(tài)庫(kù),不參與應(yīng)用的加殼操作,安全性稍遜一籌
Relocatable Object File 以及 Static Library 都是在編譯后直接合并到最后的可執(zhí)行文件中的,缺點(diǎn)相對(duì)不夠靈活,但安全性稍強(qiáng)。
如果要偏向靜態(tài)的方案,應(yīng)該選擇 Relocatable Object File 還是 Static Library?
使用 Relocatable Object File 可以減少二進(jìn)制文件的大小
動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)的區(qū)別:
如果使用動(dòng)態(tài)庫(kù),需要考慮的是:
- 對(duì)于啟動(dòng)速度的影響。
- 對(duì)于保密要求高的線下渠道 SDK,可能會(huì)被從 .app/ 中單獨(dú)拿出來(lái),反編譯研究具體實(shí)現(xiàn)。靜態(tài)庫(kù)則比較安全一點(diǎn)。
內(nèi)存管理
Objective-C的內(nèi)存管理主要有三種方式ARC(自動(dòng)內(nèi)存計(jì)數(shù))、手動(dòng)內(nèi)存計(jì)數(shù)、內(nèi)存池。
1). 自動(dòng)內(nèi)存計(jì)數(shù)ARC:由Xcode自動(dòng)在App編譯階段,在代碼中添加內(nèi)存管理代碼。
2). 手動(dòng)內(nèi)存計(jì)數(shù)MRC:遵循內(nèi)存誰(shuí)申請(qǐng)、誰(shuí)釋放;誰(shuí)添加,誰(shuí)釋放的原則。
3). 內(nèi)存釋放池Release Pool:把需要釋放的內(nèi)存統(tǒng)一放在一個(gè)池子中,當(dāng)池子被抽干后(drain),池子中所有的內(nèi)存空間也被自動(dòng)釋放掉。內(nèi)存池的釋放操作分為自動(dòng)和手動(dòng)。自動(dòng)釋放受runloop機(jī)制影響。
有一個(gè)很經(jīng)典的面試題,考察自動(dòng)釋放池的如下:
for (int i = 0; i < MAXFLOAT; i++) {
NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}
上述的這種寫法,會(huì)使內(nèi)存慢慢增加,如何解決呢,面試官想要的答案就是用自動(dòng)釋放池,你也可以改成其他的,但不是面試官要的,你懂的[??],修改如下:
for (int i = 0; i < MAXFLOAT; i++) {
@autoreleasepool {
NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}
}
- 什么時(shí)間會(huì)創(chuàng)建自動(dòng)釋放池?*
從程序啟動(dòng)到加載完成是一個(gè)完整的運(yùn)行循環(huán),然后會(huì)停下來(lái),等待用戶交互,用戶的每一次交互都會(huì)啟動(dòng)一次運(yùn)行循環(huán),來(lái)處理用戶所有的點(diǎn)擊事件、觸摸事件,運(yùn)行循環(huán)檢測(cè)到事件并啟動(dòng)后,就會(huì)創(chuàng)建自動(dòng)釋放池。
子線程的 runloop 默認(rèn)是不工作,無(wú)法主動(dòng)創(chuàng)建,必須手動(dòng)創(chuàng)建。
自定義的 NSOperation 和 NSThread 需要手動(dòng)創(chuàng)建自動(dòng)釋放池。比如: 自定義的 NSOperation 類中的 main 方法里就必須添加自動(dòng)釋放池。否則出了作用域后,自動(dòng)釋放對(duì)象會(huì)因?yàn)闆](méi)有自動(dòng)釋放池去處理它,而造成內(nèi)存泄露。
但對(duì)于 blockOperation 和 invocationOperation 這種默認(rèn)的Operation ,系統(tǒng)已經(jīng)幫我們封裝好了,不需要手動(dòng)創(chuàng)建自動(dòng)釋放池。
@autoreleasepool 當(dāng)自動(dòng)釋放池被銷毀或者耗盡時(shí),會(huì)向自動(dòng)釋放池中的所有對(duì)象發(fā)送 release 消息,釋放自動(dòng)釋放池中的所有對(duì)象。
如果在一個(gè)vc的viewDidLoad中創(chuàng)建一個(gè) Autorelease對(duì)象,那么該對(duì)象會(huì)在 viewDidAppear 方法執(zhí)行前就被銷毀了。
什么會(huì)造成離屏渲染
GPU屏幕渲染有兩種方式:
(1)On-Screen Rendering (當(dāng)前屏幕渲染)
指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行。
(2)Off-Screen Rendering (離屏渲染)
指的是在GPU在當(dāng)前屏幕緩沖區(qū)以外開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。
下面的情況或操作會(huì)引發(fā)離屏渲染:
- 為圖層設(shè)置遮罩(layer.mask)
- 將圖層的layer.masksToBounds / view.clipsToBounds屬性設(shè)置為true
- 將圖層layer.allowsGroupOpacity屬性設(shè)置為YES和layer.opacity小于1.0
- 為圖層設(shè)置陰影(layer.shadow *)。
- 為圖層設(shè)置layer.shouldRasterize=true
- 具有l(wèi)ayer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的圖層
- 文本(任何種類,包括UILabel,CATextLayer,Core Text等)。
- 使用CGContext在drawRect :方法中繪制大部分情況下會(huì)導(dǎo)致離屏渲染,甚至僅僅是一個(gè)空的實(shí)現(xiàn)。
優(yōu)化:
1、圓角優(yōu)化
方案1 : 使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個(gè)圓角
方案2 : 使用CAShapeLayer和UIBezierPath設(shè)置圓角
2、shadow優(yōu)化
對(duì)于shadow,如果圖層是個(gè)簡(jiǎn)單的幾何圖形或者圓角圖形,我們可以通過(guò)設(shè)置shadowPath來(lái)優(yōu)化性能,能大幅提高性能
其他優(yōu)化:
當(dāng)我們需要圓角效果時(shí),可以使用一張中間透明圖片蒙上去使用ShadowPath指定layer陰影效果路徑
使用異步進(jìn)行l(wèi)ayer渲染(Facebook開源的異步繪制框架AsyncDisplayKit)
設(shè)置layer的opaque值為YES,
減少?gòu)?fù)雜圖層合成盡量使用不包含透明(alpha)通道的圖片資源
盡量設(shè)置layer的大小值為整形值
直接讓美工把圖片切成圓角進(jìn)行顯示,這是效率最高的一種方案很多情況下用戶上傳圖片進(jìn)行顯示,
可以讓服務(wù)端處理圓角使用代碼手動(dòng)生成圓角Image設(shè)置到要顯示的View上,
利用UIBezierPath(CoreGraphics框架)畫出來(lái)圓角圖片
網(wǎng)絡(luò)通信
? 1、應(yīng)用層 協(xié)議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
? 2、表示層 數(shù)據(jù)的表示、安全、壓縮,格式有:JPEG、ASCll、DECOIC、加密格式等(數(shù)據(jù)格式化,代碼轉(zhuǎn)換,數(shù)據(jù)加密),沒(méi)有協(xié)議
? 3、會(huì)話層 建立、管理、終止會(huì)話,沒(méi)有協(xié)議
? 4、傳輸層 定義傳輸數(shù)據(jù)的協(xié)議端口號(hào),以及流控和差錯(cuò)校驗(yàn)。協(xié)議有:TCP UDP,數(shù)據(jù)包一旦離開網(wǎng)卡即進(jìn)入網(wǎng)絡(luò)傳輸層
? 5、網(wǎng)絡(luò)層 進(jìn)行邏輯地址尋址,實(shí)現(xiàn)不同網(wǎng)絡(luò)之間的路徑選擇。協(xié)議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP
? 6、數(shù)據(jù)鏈路層 建立邏輯連接、進(jìn)行硬件地址尋址、差錯(cuò)校驗(yàn) 等功能。(由底層網(wǎng)絡(luò)定義協(xié)議)將比特組合成字節(jié)進(jìn)而組合成幀,用MAC地址訪問(wèn)介質(zhì),錯(cuò)誤發(fā)現(xiàn)但不能糾正。協(xié)議有:SLIP CSLIP PPP MTU ARP[鏈接:https://baike.baidu.com/item/A ... addin]RARP
? 7、物理層 建立、維護(hù)、斷開物理連接。以二進(jìn)制數(shù)據(jù)形式在物理媒體上傳輸數(shù)據(jù)(由底層網(wǎng)絡(luò)定義協(xié)議)協(xié)議有:ISO2110 IEEE802 IEEE802.2
===
TPC/IP協(xié)議是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸
HTTP是應(yīng)用層協(xié)議,主要解決如何包裝數(shù)據(jù)
我們?cè)趥鬏敂?shù)據(jù)時(shí),可以只使用(傳輸層)TCP/IP協(xié)議,但是那樣的話,如果沒(méi)有應(yīng)用層,便無(wú)法識(shí)別數(shù)據(jù)內(nèi)容,如果想要使傳輸?shù)臄?shù)據(jù)有意義,則必須使用到應(yīng)用層協(xié)議,應(yīng)用層協(xié)議有很多,比如HTTP、FTP、TELNET等,也可以自己定義應(yīng)用層協(xié)議
TCP和UDP使用該協(xié)議從一個(gè)網(wǎng)絡(luò)傳送數(shù)據(jù)包到另一個(gè)網(wǎng)絡(luò)。把IP想像成一種高速公路,它允許其它協(xié)議在上面行駛并找到到其它電腦的出口。TCP和UDP是高速公路上的“卡車”,它們攜帶的貨物就是像HTTP,文件傳輸協(xié)議FTP這樣的協(xié)議等。
===========
什么是Socket?
Socket其實(shí)并不是一個(gè)協(xié)議 而是一個(gè)通信模型。它是為了方便大家直接使用更底層協(xié)議(TCP | UDP)而存在的抽象層
Socket是對(duì) TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議,而是一個(gè)調(diào)用的接口(API),主要用來(lái)一臺(tái)電腦的兩個(gè)進(jìn)程通信,
Socket在網(wǎng)絡(luò)通信中,它涵蓋了網(wǎng)絡(luò)層、傳輸層、會(huì)話層、表示層、應(yīng)用層,因?yàn)槠湫艜r(shí)候用到了IP和端口,僅這兩個(gè)就表明了它用到了網(wǎng)絡(luò)層和傳輸層,而且它無(wú)視多臺(tái)電腦通信的系統(tǒng)差別,所以它涉及了表示層,一般Socket都是基于一個(gè)應(yīng)用程序的,所以會(huì)涉及到會(huì)話層和應(yīng)用層
什么是WebSocket,解決了什么問(wèn)題?//英語(yǔ)流利說(shuō)面\
WebSocket是應(yīng)用層第七層上的一個(gè)應(yīng)用層協(xié)議,它必須依賴 HTTP 協(xié)議進(jìn)行一次握手 ,握手成功后,數(shù)據(jù)就直接從 TCP 通道傳輸,與 HTTP 無(wú)關(guān)了
Websocket的數(shù)據(jù)傳輸是frame形式傳輸?shù)?,比如?huì)將一條消息分為幾個(gè)frame,按照先后順序傳輸出去。這樣做會(huì)有幾個(gè)好處:
? 1) 大數(shù)據(jù)的傳輸可以分片傳輸,不用考慮到數(shù)據(jù)大小導(dǎo)致的長(zhǎng)度標(biāo)志位不足夠的情況。
? 2 )和http的chunk一樣,可以邊生成數(shù)據(jù)邊傳遞消息,即提高傳輸效率。總之:WebSocket 的實(shí)現(xiàn)分為握手,數(shù)據(jù)發(fā)送/讀取,關(guān)閉連接。
什么是心跳?
- 心跳就是用來(lái)檢測(cè)TCP連接的雙方是否可用
- 客戶端發(fā)起心跳Ping(一般都是客戶端),假如設(shè)置在10秒后如果沒(méi)有收到回調(diào),那么說(shuō)明服務(wù)器或者客戶端某一方出現(xiàn)問(wèn)題,這時(shí)候我們需要主動(dòng)斷開連接。
HTTP 的幾種請(qǐng)求方式?以及區(qū)別 \英語(yǔ)流利說(shuō)//
英語(yǔ)流利說(shuō)總監(jiān)問(wèn)了一個(gè)HTTP的PUT請(qǐng)求,下面看下各個(gè)請(qǐng)求的不同之處吧
HTTP1.0定義了三種請(qǐng)求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五種請(qǐng)求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
HTTP協(xié)議使用的是URI,是一種表示資源標(biāo)志,那么對(duì)應(yīng)的HTTP Verb就是各種對(duì)資源的操作,GET,PUT,DELETE等,明確這些,再往下看。可參考
HTTP: Hyper Text Transfer Protocol,超文本傳輸協(xié)議URI: Universal Resource Identifier,統(tǒng)一資源標(biāo)識(shí)符URL: Universal Reversource Locator,統(tǒng)一資源定位符
簡(jiǎn)單地說(shuō),URI是在某一規(guī)則下能把資源獨(dú)一無(wú)二地標(biāo)識(shí)出來(lái),URL是特殊的URI,即用定位的方式實(shí)現(xiàn)URI
GET 請(qǐng)求指定的頁(yè)面信息,并返回實(shí)體主體。
HEAD 類似于get請(qǐng)求,只不過(guò)返回的響應(yīng)中沒(méi)有具體的內(nèi)容,用于獲取報(bào)頭。
PUT: 從客戶端向服務(wù)器傳送的數(shù)據(jù)取代指定的文檔的內(nèi)容, 用PUT來(lái)達(dá)到更改資源,需要client提交資源全部信息,如果只有部分信息,不應(yīng)該使用PUT
DELETE: 請(qǐng)求服務(wù)器刪除指定的頁(yè)面。
OPTIONS: 允許客戶端查看服務(wù)器的性能。
HTTPS
一般面試官問(wèn)了你HTTP之后就會(huì)問(wèn)你HTTPS了,真是一個(gè)都不能少伐?
- HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer), 是以安全為目標(biāo)的HTTP通道,簡(jiǎn)單講是HTTP的安全版。 即HTTP下加入SSL層,HTTPS的安全基礎(chǔ)是SSL,因此加密的詳細(xì)內(nèi)容就需要SSL
-
HTTPS的通信過(guò)程,盜一張圖
15559995700152.jpg
HTTPS通信過(guò)程:
- 客戶端請(qǐng)求https鏈接,服務(wù)端返回公鑰
- 客戶端產(chǎn)生隨機(jī)對(duì)稱密鑰
- 客戶端用公鑰對(duì)對(duì)稱密鑰加密
- 客戶端發(fā)送加密后的對(duì)稱密鑰
- 客戶端發(fā)送通過(guò)對(duì)稱密鑰加密的密文通信
===
HTTPS與HTTP的區(qū)別:
超文本傳輸協(xié)議HTTP協(xié)議被用于在Web瀏覽器和網(wǎng)站服務(wù)器之間傳遞信息。
HTTP協(xié)議以明文方式發(fā)送內(nèi)容,不提供任何方式的數(shù)據(jù)加密
HTTPS: 安全套接字層超文本傳輸協(xié)議HTTPS, 在HTTP的基礎(chǔ)上加入SSL協(xié)議,SSL依靠證書來(lái)驗(yàn)證服務(wù)器的身份,并為瀏覽器和服務(wù)器之間的通信加密
https協(xié)議需要到ca申請(qǐng)證書,一般免費(fèi)證書很少,需要交費(fèi)。
http是超文本傳輸協(xié)議,信息是明文傳輸,https 則是具有安全性的ssl加密傳輸協(xié)議。
http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
http的連接很簡(jiǎn)單,是無(wú)狀態(tài)的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,比http協(xié)議安全。
兩個(gè)小問(wèn)題
1)如何保證公鑰不被篡改?
解決方法:將公鑰放在數(shù)字證書中。只要證書是可信的,公鑰就是可信的。
(2)公鑰加密計(jì)算量太大,如何減少耗用的時(shí)間?
解決方法:每一次對(duì)話(session),客戶端和服務(wù)器端都生成一個(gè)"對(duì)話密鑰"(session key),用它來(lái)加密信息。由于"對(duì)話密鑰"是對(duì)稱加密,所以運(yùn)算速度非???,而服務(wù)器公鑰(非對(duì)稱加密)只用于加密"對(duì)話密鑰"本身,這樣就減少了加密運(yùn)算的消耗時(shí)間。
SSL協(xié)議
SSL: SSL協(xié)議的基本思路是采用公鑰加密法, 采就是客戶端先向服務(wù)器端索要公鑰,然后用公鑰加密信息,服務(wù)器收到密文后,用自己的私鑰解密。
SSL協(xié)議的基本過(guò)程如下:
- 客戶端向服務(wù)器端索要并驗(yàn)證公鑰
- 雙方協(xié)商生成”對(duì)話密鑰”
- 雙方采用“ 對(duì)話密鑰”進(jìn)行加密通信c
NSTimer面試考點(diǎn)
先來(lái)說(shuō)一下NSTimer在使用的時(shí)候內(nèi)存泄漏的分析

NSTimer必須與RunLoop搭配使用,因?yàn)槠涠〞r(shí)任務(wù)的觸發(fā)基于RunLoop,NSTimer使用常見的Target-Action模式。由于RunLoop會(huì)強(qiáng)引用timer,timer會(huì)強(qiáng)引用Target,容易造成循環(huán)引用、內(nèi)存泄露等問(wèn)題
loop 強(qiáng)引用timer, timer 強(qiáng)引用 target,如果不能釋放,會(huì)造成內(nèi)存泄漏,有一個(gè)面試官問(wèn)如果在target中傳入weak的self,那么可以解決循環(huán)引用問(wèn)題嗎?答案是否,
Target強(qiáng)引用or弱引用Timer并不是問(wèn)題的關(guān)鍵,問(wèn)題的關(guān)鍵是:一定要在Timer使用完畢調(diào)用invalidate使之失效(手動(dòng)調(diào)用or系統(tǒng)自動(dòng)調(diào)用),Timer從RunLoop中被移除并清除強(qiáng)引用,這個(gè)操作可打破引用1、2,而引用3是強(qiáng)弱引用已經(jīng)不重要了
NSTimer一共有三種初始化方案:init開頭的普通創(chuàng)建方法、timer開頭的類工廠方法、scheduled開頭的類工廠方法。前兩者需要手動(dòng)加入RunLoop中,后者會(huì)自動(dòng)加入當(dāng)前RunLoop的DefaultMode中
以上我只是整理說(shuō)了一些核心的點(diǎn),其他部分可閱讀這里
對(duì)于NSTimer,面試官還會(huì)問(wèn),它是否是時(shí)間準(zhǔn)確呢?大家可能都知道是時(shí)間不準(zhǔn)確的,因?yàn)槭躌unLoop的影響,那么GCD中也有延時(shí),如果用GCD來(lái)做延時(shí),那時(shí)間準(zhǔn)確嗎?
答案是GCD的time是準(zhǔn)確的,GCD 的線程管理是通過(guò)系統(tǒng)來(lái)直接管理的。GCD Timer 是通過(guò) dispatch port 給 RunLoop 發(fā)送消息,來(lái)使 RunLoop 執(zhí)行相應(yīng)的 block,如果所在線程沒(méi)有 RunLoop,那么 GCD 會(huì)臨時(shí)創(chuàng)建一個(gè)線程去執(zhí)行 block,執(zhí)行完之后再銷毀掉,因此 GCD 的 Timer 是不依賴 RunLoop 的。
關(guān)于Block的可以參考下面幾個(gè)鏈接
KVC和KVO
在這里只說(shuō)一個(gè)問(wèn)題,kvo 里面什么時(shí)候修改屬性的stter方法的?
中間類在被觀察的屬性的setter方法中,在改變屬性值的前后分別添加了willChangeValueForKey:和didChangeValueForKey:。使其在通過(guò)KVC標(biāo)準(zhǔn)改變屬性值時(shí)可以被觀察到,并向觀察者發(fā)送消息。
事件響應(yīng)鏈,有面試必問(wèn),來(lái)了,老弟~
AFNetworking的工作原理,2.0和3.0的線程區(qū)別?
AFNetworking 2.0 線程 使用的是常駐線程,自己創(chuàng)建線程并添加到runloop中,AFN每次進(jìn)行的網(wǎng)絡(luò)操作,開始、暫停、取消操作時(shí)都將相應(yīng)的執(zhí)行任務(wù)扔進(jìn)了自己創(chuàng)建的線程的 RunLoop 中進(jìn)行處理,從而避免造成主線程的阻塞。

每一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)AFHTTPRequestOperation實(shí)例對(duì)象(以下簡(jiǎn)稱operation),每一個(gè)operation在初始化完成后都會(huì)被添加到一個(gè)NSOperationQueue中。由這個(gè)NSOperationQueue來(lái)控制并發(fā),系統(tǒng)會(huì)根據(jù)當(dāng)前可用的核心數(shù)以及負(fù)載情況動(dòng)態(tài)地調(diào)整最大的并發(fā) operation 數(shù)量,我們也可以通過(guò)setMaxConcurrentoperationCount:方法來(lái)設(shè)置最大并發(fā)數(shù)。注意:并發(fā)數(shù)并不等于所開辟的線程數(shù)。具體開辟幾條線程由系統(tǒng)決定。
也就是說(shuō)此處執(zhí)行operation是并發(fā)的、多線程的。
AF中常駐線程的實(shí)現(xiàn)
- 使用單例創(chuàng)建線程
- 添加到runloop中,且加了一個(gè)NSMachPort,來(lái)防止這個(gè)新建的線程由于沒(méi)有活動(dòng)直接退出?!?使用MachPort配合RunLoop進(jìn)行線程保活】
AF3.x為什么不再需要常駐線程?
NSURLConnection的一大痛點(diǎn)就是:發(fā)起請(qǐng)求后,這條線程并不能隨風(fēng)而去,而需要一直處于等待回調(diào)的狀態(tài)。
NSURLSession發(fā)起的請(qǐng)求,不再需要在當(dāng)前線程進(jìn)行代理方法的回調(diào)!可以指定回調(diào)的delegateQueue,這樣我們就不用為了等待代理回調(diào)方法而苦苦保活線程了。
同時(shí)還要注意一下,指定的用于接收回調(diào)的Queue的maxConcurrentOperationCount設(shè)為了1,這里目的是想要讓并發(fā)的請(qǐng)求串行的進(jìn)行回調(diào)。
為什么要串行回調(diào)?
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
//給所要訪問(wèn)的資源加鎖,防止造成數(shù)據(jù)混亂
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
這邊對(duì) self.mutableTaskDelegatesKeyedByTaskIdentifier 的訪問(wèn)進(jìn)行了加鎖,目的是保證多線程環(huán)境下的數(shù)據(jù)安全
面試官可能會(huì)問(wèn)你:為什么AF3.0中需要設(shè)置self.operationQueue.maxConcurrentOperationCount = 1;而AF2.0卻不需要?
--->>>
AF3.0的operationQueue是用來(lái)接收NSURLSessionDelegate回調(diào)的,鑒于一些多線程數(shù)據(jù)訪問(wèn)的安全性考慮,設(shè)置了maxConcurrentOperationCount = 1來(lái)達(dá)到串行回調(diào)的效果
--->>>
AF2.0的operationQueue是用來(lái)添加operation并進(jìn)行并發(fā)請(qǐng)求的,所以不要設(shè)置為1。
MRC環(huán)境下在assign、retain、copy下屬性的set方法
直接上代碼了-->>
//assign環(huán)境下
-(void)setName:(NSString *)name{
_name = name;
}
//retain環(huán)境下
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
//copy環(huán)境下
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
深拷貝,淺拷貝
- 淺copy,類似strong,持有原始對(duì)象的指針,會(huì)使retainCount加一。
- 深copy,會(huì)創(chuàng)建一個(gè)新的對(duì)象,不會(huì)對(duì)原始對(duì)象的retainCount變化。
面試官可能會(huì)問(wèn),如果對(duì)一個(gè)可變數(shù)組進(jìn)行深拷貝,則會(huì)對(duì)可變數(shù)組里面的元素也會(huì)進(jìn)行重新復(fù)制一份嗎?答:不會(huì),深拷貝,可變數(shù)組就是一個(gè)箱子,如果進(jìn)行深拷貝,則會(huì)再拷貝出一個(gè)新的箱子,但箱子里面的元素不會(huì)拷貝出新的。
iOS中的幾種鎖
互斥鎖
用于多線程編程,防止兩條線程同時(shí)對(duì)同一公共資源進(jìn)行讀寫的機(jī)制。NSLock,pthread_mutex, @synchronized
遞歸鎖
遞歸鎖有一個(gè)特點(diǎn),就是同一個(gè)線程可以加鎖N次而不會(huì)引發(fā)死鎖。
NSRecursiveLock, 2.pthread_mutex(recursive):
自旋鎖:
是用于多線程同步的一種鎖,線程反復(fù)檢查鎖變量是否可用。由于線程在這一過(guò)程中保持執(zhí)行,因此是一種忙等待。一旦獲取了自旋鎖,線程會(huì)一直保持該鎖,直至顯式釋放自旋鎖。
OSSpinLock
信號(hào)量:一種同步方式
信號(hào)量可以有更多的取值空間,用來(lái)實(shí)現(xiàn)更加復(fù)雜的同步,而不單單是線程間互斥。
dispatch_semaphore:
條件鎖:
就是條件變量,當(dāng)進(jìn)程的某些資源要求不滿足時(shí)就進(jìn)入休眠,也就是鎖住了。當(dāng)資源被分配到了,條件鎖打開,進(jìn)程繼續(xù)運(yùn)行。
NSCondition, NSConditionLock
遵循NSLocking協(xié)議,使用的時(shí)候同樣是lock,unlock加解鎖,wait是傻等,waitUntilDate:方法是等一會(huì),都會(huì)阻塞掉線程,signal是喚起一個(gè)在等待的線程,broadcast是廣播全部喚起。
讀寫鎖:
//加讀鎖
pthread_rwlock_rdlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//加寫鎖
pthread_rwlock_wrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
@synchronized結(jié)構(gòu)在工作時(shí)為傳入的對(duì)象分配了一個(gè)遞歸鎖,其他內(nèi)容可參閱文檔
SDWebImage 緩存原理
對(duì)于常用的三方庫(kù),一般面試官都會(huì)問(wèn)到,因?yàn)槠^長(zhǎng),我只說(shuō)一些比較核心的點(diǎn),
- SDWebImage 使用的是NSCache進(jìn)行緩存的,為什么用NSCache進(jìn)行緩存呢,
int main(int argc, const char * argv[]) {
@autoreleasepool {
//創(chuàng)建一個(gè)NSCache緩存對(duì)象
NSCache *cache = [[NSCache alloc] init];
//設(shè)置緩存中的對(duì)象個(gè)數(shù)最大為5個(gè)
[cache setCountLimit:5];
//創(chuàng)建一個(gè)CacheTest類作為NSCache對(duì)象的代理
CacheTest *ct = [[CacheTest alloc] init];
//設(shè)置代理
cache.delegate = ct;
//創(chuàng)建一個(gè)字符串類型的對(duì)象添加進(jìn)緩存中,其中key為Test
NSString *test = @"Hello, World";
[cache setObject:test forKey:@"Test"];
//遍歷十次用于添加
for (int i = 0; i < 10; i++)
{
[cache setObject:[NSString stringWithFormat:@"Hello%d", i] forKey:[NSString stringWithFormat:@"World%d", i]];
NSLog(@"Add key:%@ value:%@ to Cache", [NSString stringWithFormat:@"Hello%d", i], [NSString stringWithFormat:@"World%d", i]);
}
for (int i = 0; i < 10; i++)
{
NSLog(@"Get value:%@ for key:%@", [cache objectForKey:[NSString stringWithFormat:@"World%d", i]], [NSString stringWithFormat:@"World%d", i]);
}
[cache removeAllObjects];
for (int i = 0; i < 10; i++)
{
NSLog(@"Get value:%@ for key:%@", [cache objectForKey:[NSString stringWithFormat:@"World%d", i]], [NSString stringWithFormat:@"World%d", i]);
}
NSLog(@"Test %@", test);
}
return 0;
}
上面的代碼創(chuàng)建了一個(gè)NSCache對(duì)象,設(shè)置了其最大可緩存對(duì)象的個(gè)數(shù)為5個(gè),當(dāng)我們要添加第六個(gè)對(duì)象時(shí)NSCache自動(dòng)刪除了我們添加的第一個(gè)對(duì)象并觸發(fā)了NSCacheDelegate的回調(diào)方法,
添加第七個(gè)時(shí)也是同樣的,刪除了緩存中的一個(gè)對(duì)象才能添加進(jìn)去,一下情況NSCache會(huì)刪除緩存:
? NSCache緩存對(duì)象自身被釋放
? 手動(dòng)調(diào)用removeObjectForKey:方法
? 手動(dòng)調(diào)用removeAllObjects
? 緩存中對(duì)象的個(gè)數(shù)大于countLimit,或,緩存中對(duì)象的總cost值大于totalCostLimit
? 程序進(jìn)入后臺(tái)后
? 收到系統(tǒng)的內(nèi)存警告
異步方式在ioQueue上執(zhí)行刪除操作,所有IO操作使用一個(gè)串行隊(duì)列來(lái)執(zhí)行,避免加鎖釋放鎖的復(fù)雜,還有就是使用NSOperation作為一個(gè)標(biāo)識(shí)用來(lái)取消耗時(shí)的磁盤查詢?nèi)蝿?wù)。內(nèi)存緩存就直接刪除NSCache對(duì)象的數(shù)據(jù),磁盤緩存就直接獲取文件的絕對(duì)路徑后刪除即可
if (fromDisk) {
//異步方式在ioQueue上執(zhí)行刪除操作
dispatch_async(self.ioQueue, ^{
//使用key構(gòu)造一個(gè)默認(rèn)路徑下的文件存儲(chǔ)的絕對(duì)路徑
//調(diào)用NSFileManager刪除該路徑的文件
[_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];
//有回調(diào)塊就在主線程中執(zhí)行
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
//不需要?jiǎng)h除磁盤數(shù)據(jù)并且有回調(diào)塊就直接執(zhí)行
} else if (completion){
completion();
}
刪除磁盤中過(guò)期的圖片,以及當(dāng)緩存大小大于配置的值時(shí),進(jìn)行緩存清理
- (void)backgroundDeleteOldFiles {
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
[self deleteOldFilesWithCompletionBlock:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
多線程
iOS中有哪些多線程方案?
常用的有三種: NSThread NSOperationQueue GCD
1、NSThread 是這三種范式里面相對(duì)輕量級(jí)的,但也是使用起來(lái)最負(fù)責(zé)的,
你需要自己管理thread的生命周期,線程之間的同步。線程共享同一應(yīng)用程序的部分內(nèi)存空間,
它們擁有對(duì)數(shù)據(jù)相同的訪問(wèn)權(quán)限。你得協(xié)調(diào)多個(gè)線程對(duì)同一數(shù)據(jù)的訪問(wèn),
一般做法是在訪問(wèn)之前加鎖,這會(huì)導(dǎo)致一定的性能開銷。
2、NSOperationQueue 以面向?qū)ο蟮姆绞椒庋b了用戶需要執(zhí)行的操作,
我們只要聚焦于我們需要做的事情,而不必太操心線程的管理,同步等事情,
因?yàn)镹SOperation已經(jīng)為我們封裝了這些事情。
NSOperation 是一個(gè)抽象基類,我們必須使用它的子類。
3、 GCD: iOS4 才開始支持,它提供了一些新的特性,以及運(yùn)行庫(kù)來(lái)支持多核并行編程,
它的關(guān)注點(diǎn)更高:如何在多個(gè)cpu上提升效率。
總結(jié):
- NSThread是早期的多線程解決方案,實(shí)際上是把C語(yǔ)言的PThread線程管理代碼封裝成OC代碼。
- GCD是取代NSThread的多線程技術(shù),C語(yǔ)法+block。功能強(qiáng)大。
- NSOperationQueue是把GCD封裝為OC語(yǔ)法,額外比GCD增加了幾項(xiàng)新功能。
* 最大線程并發(fā)數(shù)
* 取消隊(duì)列中的任務(wù)
* 暫停隊(duì)列中的任務(wù)
* 可以調(diào)整隊(duì)列中的任務(wù)執(zhí)行順序,通過(guò)優(yōu)先級(jí)
* 線程依賴
* NSOperationQueue支持KVO。 這就意味著你可以觀察任務(wù)的狀態(tài)屬性。
但是NSOperationQueue的執(zhí)行效率沒(méi)有GCD高,所以一半情況下,我們使用GCD來(lái)完成多線程操作。
面試題:多個(gè)網(wǎng)絡(luò)請(qǐng)求完成后執(zhí)行下一步?
第一種方式:使用dispatch_group
-(void)Btn2{
NSString *str = @"http://www.itdecent.cn/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%d---%d",i,i);
dispatch_group_leave(downloadGroup);
}];
[task resume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
創(chuàng)建一個(gè)dispatch_group_t, 每次網(wǎng)絡(luò)請(qǐng)求前先dispatch_group_enter,請(qǐng)求回調(diào)后再dispatch_group_leave,對(duì)于enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會(huì)一直存在。當(dāng)所有enter的block都leave后,會(huì)執(zhí)行dispatch_group_notify的block。
第二種方式可以采用信號(hào)量dispatch_semaphore_t
-(void)Btn3{
NSString *str = @"http://www.itdecent.cn/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%d---%d",i,i);
count++;
if (count==10) {
dispatch_semaphore_signal(sem);
count = 0;
}
}];
[task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
dispatch_semaphore信號(hào)量為基于計(jì)數(shù)器的一種多線程同步機(jī)制。如果semaphore計(jì)數(shù)大于等于1,計(jì)數(shù)-1,返回,程序繼續(xù)運(yùn)行。如果計(jì)數(shù)為0,則等待。dispatch_semaphore_signal(semaphore)為計(jì)數(shù)+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設(shè)置等待時(shí)間,這里設(shè)置的等待時(shí)間是一直等待。
對(duì)于以上代碼通俗一點(diǎn)就是,開始為0,等待,等10個(gè)網(wǎng)絡(luò)請(qǐng)求都完成了,dispatch_semaphore_signal(semaphore)為計(jì)數(shù)+1,然后計(jì)數(shù)-1返回,程序繼續(xù)執(zhí)行。 (這里也就是為什么有個(gè)count變量的原因,記錄網(wǎng)絡(luò)回調(diào)的次數(shù),回調(diào)10次之后再發(fā)信號(hào)量,使后面程序繼續(xù)運(yùn)行)。
什么是dispatch_barrier_async(柵欄函數(shù))?
dispatch_barrier_sync(dispatch_queue_t queue, ^{
})
- 在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,它后面的任務(wù)要等它執(zhí)行完成后才會(huì)開始執(zhí)行,
- 避免數(shù)據(jù)競(jìng)爭(zhēng)
sync和async
sync: 同于當(dāng)前線程, 可以是主線程也可以是子線程
async: 就是不同于當(dāng)前線程, 可以是主線程也可以是子線程
XMPP是什么?XMPP進(jìn)行傳輸時(shí),需要傳大量的數(shù)據(jù),如何減少數(shù)據(jù)?
XMPP:
1)XMPP 是一種基于XML的協(xié)議,XMPP是一個(gè)分散型通信網(wǎng)絡(luò)
2)XMPP是一種基于標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言的子集XML的協(xié)議,它繼承了在XML環(huán)境中靈活的發(fā)展性,XMPP有超強(qiáng)的擴(kuò)展性。XMPP中定義了三個(gè)角色,客戶端,服務(wù)端,網(wǎng)關(guān)。通信能夠在這個(gè)三者的任意兩個(gè)之間雙向發(fā)生,而他們的傳輸是XML流
3)XMPP工作原理:所有從一個(gè)客戶端到另一個(gè)客戶端的消息和數(shù)據(jù)都要通過(guò)服務(wù)端
4)XMPP允許建立并行的TCP套接字鏈接對(duì)所有連接上的客戶端和服務(wù)器端。持久的套接字的連接使得XMPP能夠更有效的支持高級(jí)的具有存在能力的應(yīng)用在帶寬和處理資源的使用中。
小結(jié):
而XMPP的核心部分就是一個(gè)在網(wǎng)絡(luò)上分片斷發(fā)送XML的流協(xié)議。這個(gè)流協(xié)議是XMPP的即時(shí)通訊指令的傳遞基礎(chǔ),也是一個(gè)非常重要的可以被進(jìn)一步利用的網(wǎng)絡(luò)基礎(chǔ)協(xié)議。所以可以說(shuō),XMPP用TCP傳的是XML流。
=======
如何減少數(shù)據(jù)?
- 如果是大量的數(shù)據(jù),對(duì)于XML,需要對(duì)傳的信息進(jìn)行簡(jiǎn)化,比如command, message中的信息要簡(jiǎn)化,
- 使用別的數(shù)據(jù)傳輸協(xié)議,比如protocol Buff(可以傳輸binary 二進(jìn)制數(shù)據(jù)),格式可以用json
Swift問(wèn)題
swift語(yǔ)言和OC語(yǔ)言的本質(zhì)區(qū)別是什么?
答:本質(zhì)區(qū)別是Swift是靜態(tài)語(yǔ)言,而OC是動(dòng)態(tài)語(yǔ)言,面試回去路上,才想到問(wèn)題的最好的答案??----
問(wèn)題: 子類不能重寫父類的extension的方法?怎么解決呢?
解決方法如下:
//父類中
@objc extension MOBBaseViewController {
//要重寫的方法
public func testExt() {
print("----------");
}
}
-----
//子類中
import UIKit
class MOBClassifyViewController: MOBBaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//重寫父類extension方法
override func testExt(){
print(">>>>>>>>>>>>")
}
}
因?yàn)閑xtension中的方法是私有的,so子類訪問(wèn)不到,因此要用public修飾下,@objc有以下兩點(diǎn)說(shuō)明:
? fileprivate 或者 private 保證方法私有 能在同一個(gè)類 或者 同一個(gè)文件(extension)中訪問(wèn)這個(gè)方法 如果定義為private 那么只能在一個(gè)類中訪問(wèn) 不能在類擴(kuò)展中訪問(wèn)
? 允許這個(gè)函數(shù)在“運(yùn)行時(shí)”通過(guò)oc的消息機(jī)制調(diào)用
NSString跟Swift String的區(qū)別和使用場(chǎng)景
NSString和String的共同點(diǎn)
- String保留了大部分NSString的api比如
.hasPrefix
.lowercaseString
.componentsSeparatedByString
.substringWithRange 等等
所以很多常規(guī)操作在開發(fā)中使用兩者之一都是可以的,
NSString和String的不同點(diǎn)
- NSString是引用類型。Swift String是值類型
var nsString: NSString = NSString()
var swiftString:String = String()
var nsString: NSString = "dsx"
var swiftString:String = "dsx"
兩者都可以使用自己的類名來(lái)直接進(jìn)行初始化,下面的方法也是初始化,雖然寫法相同,但是NSString的意思是初始化了一個(gè)指針指向了這個(gè)字符串,但Swift String的意思則是把字符串字面量賦值給變量
- NSString需要用append或者stringWithFormat將兩個(gè)字符串拼接,Swift String只需要用 + 即可
- Swift String 可以實(shí)現(xiàn)字符串遍歷
for character in "My name is dsx".characters {
print(character)
}
- 計(jì)算字符串長(zhǎng)度,NSString直接使用 字符串.length 就可以獲得字符串的長(zhǎng)度,swift真正的類似于.length的方法就是取出characters屬性(數(shù)組)然后.count
- 比較字符串相等的方式
et strA: NSString = ""
let strB: NSString = ""
let strC: NSString = "dsx"
let strD: NSString = "dsx"
// NSString 字符串相等
if(strA.isEqualToString(strB as String)){
print("yes");
}
// String的相等
if (strC == strD){
print("yes");
}
- NSString可以同基本數(shù)據(jù)類型見轉(zhuǎn)化
var strA: NSString = "12306"
var strB: NSString = "0.618"
var numOfInt = strA.integerValue;
var numOfDouble = strB.doubleValue;
- String可以通過(guò)isEmpty屬性來(lái)判斷該字符串是否為空,是string獨(dú)有的
- String獨(dú)有的字符串插入字符功能
var strA:String = "My name is dx"
strA.insert("s", atIndex: strA.characters.indexOf("x")!);
print(strA) // My name is dsx
僅僅可以插入單個(gè)字符不能插字符串,如果里面寫成ss 就會(huì)報(bào)錯(cuò)Cannot convert value of type 'String' to expected argument type 'Character'
