在投遞簡歷以前,就是所謂的寒冬將至,開個年會都是守望寒冬,而後我身邊的準備跳槽的大佬們,都是有幾分涼意,不過我還好,總感受一我的吃飽,全家不餓,😁O(∩_∩)O哈!沒想那麼多,直接就全身投入,找工做。如今作個回顧吧,爲本身,也爲路過的各位大俠。html
先說一個問題,是寒冬嗎?我真沒以爲,說本身的一個親身體會,不誇張的說,基本上是天天2家,且持續一個月,固然是距離能夠接受,公司小中大都有的,我感受不是互聯網的寒冬,是本身的寒冬,有一句說的很好,人生就兩季,努力是旺季,不努力是淡季!我感受頗有道理🤣~~~~ios
如今面試要求高在要會各類語言,另外要很深刻,要夠底層,要懂數據結構與算法之美(面試過的都會體會什麼是真是一言難盡吧),看一些大佬,進入一個大廠,也寫了本身的準備,我感受真是有付出有回報的,也看出本身的一些不足吧!so,革命還沒有成功,同志們仍需努力伐!!面試
由於本身水平有限,可能有些路過的大佬感受比較簡單,我也總結了下,請飄過~~還有一些答案僅供參考,若有錯誤,請不吝賜教,在此謝過😀---->算法
先看下面表格二者的區別,後續會繼續介紹sql
+load | +initialize | |
---|---|---|
調用時機 | 被添加runtime時 | 收到第一條消息時,可能永遠不調用 |
調用順序 | 父類->子類->分類 | 父類->子類 |
調用次數 | 1次 | 屢次 |
是否須要顯示調用父類實現 | 否 | 否 |
是否沿用父類的實現 | 否 | 是 |
分類中的實現 | 類和分類都執行 | |
相同點:數據庫
1.系統都執行一次。 2.假如父類和子類都被調用,父類在子類以前被調用編程
不一樣點:json
1.load 方法會在加載類的時候就被調用,也就是 ios 應用啓動的時候,就會加載全部的類,就會調用每一個類的 + load 方法。 2.+initialize 這個方法會在 第一次初始化這個類以前 被調用,咱們用它來初始化靜態變量 3.load 會在main()函數以前調用。initialize 則在類實例化 或 類方法被調用時調用; 4.若是子類中沒有initialize方法,則會再次調用父類的initialize方法,類別會覆蓋主類的initialize,load則不會被覆蓋 5.load順序在 initialize以前;swift
7.類接收消息時,運行時會先檢查 + initialize 有沒有被調用過。若是沒有,會在消息被處理前調用api
--->>>> initialize 最終是經過 objc_msgSend 來執行的,objc_msgSend 會執行一系列方法查找,而且 Category 的方法會覆蓋類中的方法 load 是在被添加到 runtime 時開始執行,父類最早執行,而後是子類,最後是 Category。又由於是直接獲取函數指針來執行,不會像 objc_msgSend 同樣會有方法查找的過程。 ---->>>> 怎麼實現單例, 2種方法實現//喜馬拉雅面試問題\
//第一種方式: 線程安全的單例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;
}
複製代碼
然而僅僅知道這些是不夠的,說了上面的,面試官會繼續問單例,怎麼實現的,加鎖了嗎?單例何時釋放?而後你就會一臉懵~
用途:限制建立,提供全局調用,節約資源和提升性能。參考 常見的應用場景: • UIApplication • NSNotificationCenter • NSFileManager • NSUserDefaults • NSURLCache • NSHTTPCookieStorage
那麼單例是怎麼銷燬的呢?以下: 方法一:
+(void)attemptDealloc{
[_instance release]; //mrc 須要釋放,固然你就不能重寫release的方法了.
_instance = nil;
}
方法二:
1. 必須把static dispatch_once_t onceToken; 這個拿到函數體外,成爲全局的.
2.
+(void)attempDealloc{
onceToken = 0; // 只有置成0,GCD纔會認爲它從未執行過.它默認爲0.這樣才能保證下次再次調用shareInstance的時候,再次建立對象.
[_instance release];
_instance = nil;
}
複製代碼
下面說下數據持久化吧?若是是在2年前,你說了數據持久化有NSUserDefaults,plist,歸檔,CoreData巴拉巴拉,感受這位童靴還闊以,可是如今就有點low了😆,你懂得~ 面試大佬會問有幾種?而後每種有什麼不一樣?什麼能存儲什麼不能存儲?每一個在具體使用應該注意什麼?等等,問到你懷疑人生
PS: 在這裏說了小問題,就是有面試官會問,你在開發中用NSUserDefaults有沒有什麼坑?你能夠這樣答:好比你存儲一個值時,沒有進行及時的調用synchronize方法,而後此時程序就crash了或者強制殺死,那麼你再下次去取值的時候,就會取不到你以前存儲的值,路過的大佬能夠試下~~😜😜😜
歸檔序列化存儲 歸檔能夠直接將對象存儲爲文件,也可將文件直接解歸檔爲對象,相對於plist文件與偏好設置數據的存儲更加多樣,支持自定義的對象存儲,歸檔後的文件是加密的,也更加的安全,文件存儲的位置能夠自定義。 遵照NSCoding或者NSSecureCoding協議
沙盒存儲 能夠提升程序的體驗度,爲用戶節約數據流量,主要在用戶閱讀書籍、聽音樂、看視頻等,在沙盒中作數據的存儲,主要包含文件夾:Documents: 最經常使用的目錄,存放重要的數據,iTunes同步時會備份該目錄Library/Caches: 通常存放體積大,不重要的數據,iTunes同步時不會備份該目錄Library/Preferences: 存放用戶的偏好設置,iTunes同步時會備份該目錄tmp: 用於存放臨時文件,在程序未運行時可能會刪除該文件夾中的數據,iTunes同步時不會備份該目錄
Core Data Core Data是框架,並非數據庫,該框架提供了對象關係的映射功能,使得可以將OC對象轉換成數據,將數據庫中的數據還原成OC對象,在轉換的過程當中不須要編寫任何的SQL語句,在Core Data中有三個重要的概念: NSPersistentStoreCoordinator:持久化存儲協調器,在NSPersistentStoreCoordinator中包含了持久化存儲區,在持久化存儲區中包含了數據表中的不少數據,持久化存儲區的設置一般選擇NSSQLiteStoreType,也就是選擇SQLite數據庫 NSManagedObjectModel:託管對象模型,用於描述數據結構的模型
SQLite3 SQLite是輕量級的數據庫,佔用資源不多,最初是用於嵌入式的系統,在iOS中使用SQLite,須要加入"libsqlite3.tbd"依賴庫並導入頭文件。不該該頻繁的打開關閉數據庫,有可能會影響性能, 應在啓動程序時打開數據庫,在退出程序是關閉數據庫
FMDB FMDB以OC的方式封裝了SQLite的C語言API,減去了冗餘的C語言代碼,使得API更具備OC的風格,更加的面向對象,相對於Core Data框架更加的輕量級,FMDB還提供了多線程安全的數據庫操做方法,在FMDB中有三個重要的概念: FMDatabase:一個FMDatabase就表明一個SQLite數據庫,執行sql語句 FMResultSet:執行查詢後的結果集 FMDatabaseQueue:用於在多線程中執行多個查詢或更新,安全的
=== 緊接着說下CoreData吧?它老是比你知道的還要多? CoreData中的多線程問題
主要推薦的實施方案,也是最優方案,以下:
1.使用一個NSPersistentStoreCoordinator,以及兩個獨立的Contexts,一個context負責主線程與UI協做,一個context在後臺負責耗時的處理,用Notifications的方式通知主線程的NSManagedObjectContext進行mergeChangesFromContextDidSaveNotification操做
2.後臺線程作讀寫更新,而主線程只讀
3.CoreData中的NSManagedObjectContext在多線程中不安全,若是想要多線程訪問CoreData的話,最好的方法是一個線程一個NSManagedObjectContext,每一個NSManagedObjectContext對象實例均可以使用同一個NSPersistentStoreCoordinator實例,這個實例能夠很安全的順序訪_問永久存儲,這是由於NSManagedObjectContext會在便用NSPersistentStoreCoordinator前上鎖。ios5.0爲NSManagedObjectContext提供了initWithConcurrentcyType方法,其中的一個NSPrivateQueueConcurrencyType,會自動的建立一個新線程來存放NSManagedObjectContext並且它還會自動建立NSPersistentStoreCoordinator,
CoreData裏面還帶有一個通知NSManagedObjectContextDidSaveNotification,主要監聽NSManagedObjectContext的數據是否改變,併合並數據改變到相應context。
面試官問的Context是那兩種?這個面試官問的應該是用到的那兩個Type? 答:NSConfinementConcurrencyType NSMainQueueConcurrencyType
//建立並行的NSManagedObjectContext對象
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
ps:NSConfinementConcurrencyType (或者不加參數,默認就是這個)NSMainQueueConcurrencyType (表示只會在主線程中執行)
複製代碼
接着談談數據庫的優化問題,能夠經過如下幾點進行優化
FMDB事務批量更新數據庫速度問題。(親測能夠呀---740條數據用和不用事務效率差異20倍+)
寫同步(synchronous) 在SQLite中,數據庫配置的參數都由編譯指示(pragma)來實現的,而其中synchronous選項有三種可選狀態,分別是full、normal、off 設置爲synchronous OFF (0)時,SQLite在傳遞數據給系統之後直接繼續而不暫停
一條SQL語句插入多條數據
在事務中進行插入處理。
數據有序插入。
再說下什麼是事務?\英語流利說總監面試問題// 事務:
做爲單個邏輯工做單元執行的一系列操做,而這些邏輯工做單元須要具備原子性,一致性,隔離性和持久性
是併發控制的基本單元。所謂的事務,它是一個操做序列,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單元。例如,銀行轉帳工做:從一個帳號扣款並使另外一個帳號增款,這兩個操做要麼都執行,要麼都不執行。因此,應該把它們當作一個事務。
事務是一種機制,用於維護數據庫的完整性
事務基本特徵:
原子性(Atomicity):事務的個元素是不可分的,事務是一個完整的操做,一個操做序列,要麼都執行,要麼都不執行
一致性(Consistemcy):事務完成時,數據必須是一致的,保證數據的無損
隔離性(Isolation):多個事務彼此隔離,事務必須是獨立的,任何事務都不該該受影響
持久性(Durability):事務完成以後,它對於系統的影響是永久的,該修改即便出現系統故障也將一直保留,真實的修改了數據庫
這個面試題針對我本身的簡歷,可略過~ 在製做Framework時,能夠設置framework中的Mach-O Type,不手動修改的默認配置即爲 Dynamic Library,在SDK中默認使用的是 Relocatable Object File
Relocatable Object File 是組裝靜態庫和動態庫的零件,而靜態庫和動態庫就是可執行二進制文件的組件。這裏用了零件和組件的概念,零件是不可缺乏的,組件則是可選的
Dynamic Library 更靈活;複用性更強;且就安全來講,統一放置在 Payload/Framework 目錄下的自建的動態庫,不參與應用的加殼操做,安全性稍遜一籌 Relocatable Object File 以及 Static Library 都是在編譯後直接合併到最後的可執行文件中的,缺點相對不夠靈活,但安全性稍強。
若是要偏向靜態的方案,應該選擇 Relocatable Object File 仍是 Static Library? 使用 Relocatable Object File 能夠減小二進制文件的大小
1.動態庫和靜態庫的區別: 2.若是使用動態庫,須要考慮的是:
對於啓動速度的影響。 對於保密要求高的線下渠道 SDK,可能會被從 .app/ 中單獨拿出來,反編譯研究具體實現。靜態庫則比較安全一點。
Objective-C的內存管理主要有三種方式ARC(自動內存計數)、手動內存計數、內存池。
1). 自動內存計數ARC:由Xcode自動在App編譯階段,在代碼中添加內存管理代碼。 2). 手動內存計數MRC:遵循內存誰申請、誰釋放;誰添加,誰釋放的原則。 3). 內存釋放池Release Pool:把須要釋放的內存統一放在一個池子中,當池子被抽乾後(drain),池子中全部的內存空間也被自動釋放掉。內存池的釋放操做分爲自動和手動。自動釋放受runloop機制影響。 有一個很經典的面試題,考察自動釋放池的以下:
for (int i = 0; i < MAXFLOAT; i++) {
NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}
複製代碼
上述的這種寫法,會使內存慢慢增長,如何解決呢,面試官想要的答案就是用自動釋放池,你也能夠改爲其餘的,但不是面試官要的,你懂的[😂],修改以下:
for (int i = 0; i < MAXFLOAT; i++) {
@autoreleasepool {
NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}
}
複製代碼
但對於 blockOperation 和 invocationOperation 這種默認的Operation ,系統已經幫咱們封裝好了,不須要手動建立自動釋放池。
@autoreleasepool 當自動釋放池被銷燬或者耗盡時,會向自動釋放池中的全部對象發送 release 消息,釋放自動釋放池中的全部對象。
若是在一個vc的viewDidLoad中建立一個 Autorelease對象,那麼該對象會在 viewDidAppear 方法執行前就被銷燬了。
GPU屏幕渲染有兩種方式: (1)On-Screen Rendering (當前屏幕渲染) 指的是GPU的渲染操做是在當前用於顯示的屏幕緩衝區進行。 (2)Off-Screen Rendering (離屏渲染) 指的是在GPU在當前屏幕緩衝區之外開闢一個緩衝區進行渲染操做。
下面的狀況或操做會引起離屏渲染:
優化:
一、圓角優化 方案1 : 使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角
方案2 : 使用CAShapeLayer和UIBezierPath設置圓角
二、shadow優化 對於shadow,若是圖層是個簡單的幾何圖形或者圓角圖形,咱們能夠經過設置shadowPath來優化性能,能大幅提升性能
其餘優化:
當咱們須要圓角效果時,可使用一張中間透明圖片蒙上去使用ShadowPath指定layer陰影效果路徑 使用異步進行layer渲染(Facebook開源的異步繪製框架AsyncDisplayKit) 設置layer的opaque值爲YES, 減小複雜圖層合成儘可能使用不包含透明(alpha)通道的圖片資源 儘可能設置layer的大小值爲整形值 直接讓美工把圖片切成圓角進行顯示,這是效率最高的一種方案不少狀況下用戶上傳圖片進行顯示, 可讓服務端處理圓角使用代碼手動生成圓角Image設置到要顯示的View上, 利用UIBezierPath(CoreGraphics框架)畫出來圓角圖片
• 一、應用層 協議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP • 二、表示層 數據的表示、安全、壓縮,格式有:JPEG、ASCll、DECOIC、加密格式等(數據格式化,代碼轉換,數據加密),沒有協議 • 三、會話層 創建、管理、終止會話,沒有協議 • 四、傳輸層 定義傳輸數據的協議端口號,以及流控和差錯校驗。協議有:TCP UDP,數據包一旦離開網卡即進入網絡傳輸層 • 五、網絡層 進行邏輯地址尋址,實現不一樣網絡之間的路徑選擇。協議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP • 六、數據鏈路層 創建邏輯鏈接、進行硬件地址尋址、差錯校驗 等功能。(由底層網絡定義協議)將比特組合成字節進而組合成幀,用MAC地址訪問介質,錯誤發現但不能糾正。協議有:SLIP CSLIP PPP MTU ARP[連接:baike.baidu.com/item/A ... addin]RARP • 七、物理層 創建、維護、斷開物理鏈接。以二進制數據形式在物理媒體上傳輸數據(由底層網絡定義協議)協議有:ISO2110 IEEE802 IEEE802.2
===
TPC/IP協議是傳輸層協議,主要解決數據如何在網絡中傳輸
HTTP是應用層協議,主要解決如何包裝數據
咱們在傳輸數據時,能夠只使用(傳輸層)TCP/IP協議,可是那樣的話,若是沒有應用層,便沒法識別數據內容,若是想要使傳輸的數據有意義,則必須使用到應用層協議,應用層協議有不少,好比HTTP、FTP、TELNET等,也能夠本身定義應用層協議
TCP和UDP使用該協議從一個網絡傳送數據包到另外一個網絡。把IP想像成一種高速公路,它容許其它協議在上面行駛並找到到其它電腦的出口。TCP和UDP是高速公路上的「卡車」,它們攜帶的貨物就是像HTTP,文件傳輸協議FTP這樣的協議等。
===========
Socket其實並非一個協議 而是一個通訊模型。它是爲了方便你們直接使用更底層協議(TCP | UDP)而存在的抽象層
Socket是對 TCP/IP協議的封裝,Socket自己並非協議,而是一個調用的接口(API),主要用來一臺電腦的兩個進程通訊,
Socket在網絡通訊中,它涵蓋了網絡層、傳輸層、會話層、表示層、應用層,由於其信時候用到了IP和端口,僅這兩個就代表了它用到了網絡層和傳輸層,並且它無視多臺電腦通訊的系統差異,因此它涉及了表示層,通常Socket都是基於一個應用程序的,因此會涉及到會話層和應用層
WebSocket是應用層第七層上的一個應用層協議,它必須依賴 HTTP 協議進行一次握手 ,握手成功後,數據就直接從 TCP 通道傳輸,與 HTTP 無關了
Websocket的數據傳輸是frame形式傳輸的,好比會將一條消息分爲幾個frame,按照前後順序傳輸出去。這樣作會有幾個好處: • 1) 大數據的傳輸能夠分片傳輸,不用考慮到數據大小致使的長度標誌位不足夠的狀況。 • 2 )和http的chunk同樣,能夠邊生成數據邊傳遞消息,即提升傳輸效率。
總之:WebSocket 的實現分爲握手,數據發送/讀取,關閉鏈接。
英語流利說總監問了一個HTTP的PUT請求,下面看下各個請求的不一樣之處吧 HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法。 HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 HTTP協議使用的是URI,是一種表示資源標誌,那麼對應的HTTP Verb就是各類對資源的操做,GET,PUT,DELETE等,明確這些,再往下看。可參考 HTTP: Hyper Text Transfer Protocol,超文本傳輸協議URI: Universal Resource Identifier,統一資源標識符URL: Universal Reversource Locator,統一資源定位符 簡單地說,URI是在某一規則下能把資源獨一無二地標識出來,URL是特殊的URI,即用定位的方式實現URI
GET 請求指定的頁面信息,並返回實體主體。 HEAD 相似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭。 PUT: 從客戶端向服務器傳送的數據取代指定的文檔的內容, 用PUT來達到更改資源,須要client提交資源所有信息,若是隻有部分信息,不該該使用PUT DELETE: 請求服務器刪除指定的頁面。 OPTIONS: 容許客戶端查看服務器的性能。
通常面試官問了你HTTP以後就會問你HTTPS了,真是一個都不能少伐?
HTTPS通訊過程:
客戶端請求https連接,服務端返回公鑰 客戶端產生隨機對稱密鑰 客戶端用公鑰對對稱密鑰加密 客戶端發送加密後的對稱密鑰 客戶端發送經過對稱密鑰加密的密文通訊
=== HTTPS與HTTP的區別:
兩個小問題 (1)如何保證公鑰不被篡改? 解決方法:將公鑰放在數字證書中。只要證書是可信的,公鑰就是可信的。 (2)公鑰加密計算量太大,如何減小耗用的時間? 解決方法:每一次對話(session),客戶端和服務器端都生成一個"對話密鑰"(session key),用它來加密信息。因爲"對話密鑰"是對稱加密,因此運算速度很是快,而服務器公鑰(非對稱加密)只用於加密"對話密鑰"自己,這樣就減小了加密運算的消耗時間。
SSL: SSL協議的基本思路是採用公鑰加密法, 採就是客戶端先向服務器端索要公鑰,而後用公鑰加密信息,服務器收到密文後,用本身的私鑰解密。 SSL協議的基本過程以下:
客戶端向服務器端索要並驗證公鑰 雙方協商生成」對話密鑰」 雙方採用「 對話密鑰」進行加密通訊c
先來講一下NSTimer在使用的時候內存泄漏的分析
NSTimer必須與RunLoop搭配使用,由於其定時任務的觸發基於RunLoop,NSTimer使用常見的Target-Action模式。因爲RunLoop會強引用timer,timer會強引用Target,容易形成循環引用、內存泄露等問題
loop 強引用timer, timer 強引用 target,若是不能釋放,會形成內存泄漏,有一個面試官問若是在target中傳入weak的self,那麼能夠解決循環引用問題嗎?答案是否,
Target強引用or弱引用Timer並非問題的關鍵,問題的關鍵是:必定要在Timer使用完畢調用invalidate使之失效(手動調用or系統自動調用),Timer從RunLoop中被移除並清除強引用,這個操做可打破引用一、2,而引用3是強弱引用已經不重要了
NSTimer一共有三種初始化方案:init開頭的普通建立方法、timer開頭的類工廠方法、scheduled開頭的類工廠方法。前二者須要手動加入RunLoop中,後者會自動加入當前RunLoop的DefaultMode中
以上我只是整理說了一些核心的點,其餘部分可閱讀這裏
對於NSTimer,面試官還會問,它是不是時間準確呢?你們可能都知道是時間不許確的,由於受RunLoop的影響,那麼GCD中也有延時,若是用GCD來作延時,那時間準確嗎?
答案是GCD的time是準確的,GCD 的線程管理是經過系統來直接管理的。GCD Timer 是經過 dispatch port 給 RunLoop 發送消息,來使 RunLoop 執行相應的 block,若是所在線程沒有 RunLoop,那麼 GCD 會臨時建立一個線程去執行 block,執行完以後再銷燬掉,所以 GCD 的 Timer 是不依賴 RunLoop 的。
在這裏只說一個問題,kvo 裏面何時修改屬性的stter方法的? 中間類在被觀察的屬性的setter方法中,在改變屬性值的先後分別添加了willChangeValueForKey:和didChangeValueForKey:。使其在經過KVC標準改變屬性值時能夠被觀察到,並向觀察者發送消息。
AFNetworking 2.0 線程 使用的是常駐線程,本身建立線程並添加到runloop中,AFN每次進行的網絡操做,開始、暫停、取消操做時都將相應的執行任務扔進了本身建立的線程的 RunLoop 中進行處理,從而避免形成主線程的阻塞。
每個請求對應一個AFHTTPRequestOperation實例對象(如下簡稱operation),每個operation在初始化完成後都會被添加到一個NSOperationQueue中。由這個NSOperationQueue來控制併發,系統會根據當前可用的核心數以及負載狀況動態地調整最大的併發 operation 數量,咱們也能夠經過setMaxConcurrentoperationCount:方法來設置最大併發數。注意:併發數並不等於所開闢的線程數。具體開闢幾條線程由系統決定。 也就是說此處執行operation是併發的、多線程的。
AF中常駐線程的實現
AF3.x爲何再也不須要常駐線程?
NSURLConnection的一大痛點就是:發起請求後,這條線程並不能隨風而去,而須要一直處於等待回調的狀態。
NSURLSession發起的請求,再也不須要在當前線程進行代理方法的回調!能夠指定回調的delegateQueue,這樣咱們就不用爲了等待代理回調方法而苦苦保活線程了。
同時還要注意一下,指定的用於接收回調的Queue的maxConcurrentOperationCount設爲了1,這裏目的是想要讓併發的請求串行的進行回調。
爲何要串行回調?
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
//給所要訪問的資源加鎖,防止形成數據混亂
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
複製代碼
這邊對 self.mutableTaskDelegatesKeyedByTaskIdentifier 的訪問進行了加鎖,目的是保證多線程環境下的數據安全 面試官可能會問你:爲何AF3.0中須要設置self.operationQueue.maxConcurrentOperationCount = 1;而AF2.0卻不須要? --->>> AF3.0的operationQueue是用來接收NSURLSessionDelegate回調的,鑑於一些多線程數據訪問的安全性考慮,設置了maxConcurrentOperationCount = 1來達到串行回調的效果
--->>> AF2.0的operationQueue是用來添加operation並進行併發請求的,因此不要設置爲1。
直接上代碼了-->>
//assign環境下
-(void)setName:(NSString *)name{
_name = name;
}
//retain環境下
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
//copy環境下
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
複製代碼
互斥鎖 用於多線程編程,防止兩條線程同時對同一公共資源進行讀寫的機制。NSLock,pthread_mutex, @synchronized
遞歸鎖 遞歸鎖有一個特色,就是同一個線程能夠加鎖N次而不會引起死鎖。 NSRecursiveLock, 2.pthread_mutex(recursive):
自旋鎖: 是用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。因爲線程在這一過程當中保持執行,所以是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯式釋放自旋鎖。 OSSpinLock
信號量:一種同步方式 信號量能夠有更多的取值空間,用來實現更加複雜的同步,而不僅僅是線程間互斥。 dispatch_semaphore:
條件鎖: 就是條件變量,當進程的某些資源要求不知足時就進入休眠,也就是鎖住了。當資源被分配到了,條件鎖打開,進程繼續運行。 NSCondition, NSConditionLock 遵循NSLocking協議,使用的時候一樣是lock,unlock加解鎖,wait是傻等,waitUntilDate:方法是等一會,都會阻塞掉線程,signal是喚起一個在等待的線程,broadcast是廣播所有喚起。
讀寫鎖:
//加讀鎖
pthread_rwlock_rdlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//加寫鎖
pthread_rwlock_wrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
複製代碼
@synchronized結構在工做時爲傳入的對象分配了一個遞歸鎖,其餘內容可參閱文檔
對於經常使用的三方庫,通常面試官都會問到,由於篇幅較長,我只說一些比較核心的點,
int main(int argc, const char * argv[]) {
@autoreleasepool {
//建立一個NSCache緩存對象
NSCache *cache = [[NSCache alloc] init];
//設置緩存中的對象個數最大爲5個
[cache setCountLimit:5];
//建立一個CacheTest類做爲NSCache對象的代理
CacheTest *ct = [[CacheTest alloc] init];
//設置代理
cache.delegate = ct;
//建立一個字符串類型的對象添加進緩存中,其中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;
}
複製代碼
上面的代碼建立了一個NSCache對象,設置了其最大可緩存對象的個數爲5個,當咱們要添加第六個對象時NSCache自動刪除了咱們添加的第一個對象並觸發了NSCacheDelegate的回調方法, 添加第七個時也是一樣的,刪除了緩存中的一個對象才能添加進去,一下狀況NSCache會刪除緩存: • NSCache緩存對象自身被釋放 • 手動調用removeObjectForKey:方法 • 手動調用removeAllObjects • 緩存中對象的個數大於countLimit,或,緩存中對象的總cost值大於totalCostLimit • 程序進入後臺後 • 收到系統的內存警告
異步方式在ioQueue上執行刪除操做,全部IO操做使用一個串行隊列來執行,避免加鎖釋放鎖的複雜,還有就是使用NSOperation做爲一個標識用來取消耗時的磁盤查詢任務。內存緩存就直接刪除NSCache對象的數據,磁盤緩存就直接獲取文件的絕對路徑後刪除便可
if (fromDisk) {
//異步方式在ioQueue上執行刪除操做
dispatch_async(self.ioQueue, ^{
//使用key構造一個默認路徑下的文件存儲的絕對路徑
//調用NSFileManager刪除該路徑的文件
[_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];
//有回調塊就在主線程中執行
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
//不須要刪除磁盤數據而且有回調塊就直接執行
} else if (completion){
completion();
}
複製代碼
刪除磁盤中過時的圖片,以及當緩存大小大於配置的值時,進行緩存清理
- (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
一、NSThread 是這三種範式裏面相對輕量級的,但也是使用起來最負責的,
你須要本身管理thread的生命週期,線程之間的同步。線程共享同一應用程序的部份內存空間,
它們擁有對數據相同的訪問權限。你得協調多個線程對同一數據的訪問,
通常作法是在訪問以前加鎖,這會致使必定的性能開銷。
二、NSOperationQueue 以面向對象的方式封裝了用戶須要執行的操做,
咱們只要聚焦於咱們須要作的事情,而沒必要太操心線程的管理,同步等事情,
由於NSOperation已經爲咱們封裝了這些事情。
NSOperation 是一個抽象基類,咱們必須使用它的子類。
三、 GCD: iOS4 纔開始支持,它提供了一些新的特性,以及運行庫來支持多核並行編程,
它的關注點更高:如何在多個cpu上提高效率。
總結:
- NSThread是早期的多線程解決方案,其實是把C語言的PThread線程管理代碼封裝成OC代碼。
- GCD是取代NSThread的多線程技術,C語法+block。功能強大。
- NSOperationQueue是把GCD封裝爲OC語法,額外比GCD增長了幾項新功能。
* 最大線程併發數
* 取消隊列中的任務
* 暫停隊列中的任務
* 能夠調整隊列中的任務執行順序,經過優先級
* 線程依賴
* NSOperationQueue支持KVO。 這就意味着你能夠觀察任務的狀態屬性。
可是NSOperationQueue的執行效率沒有GCD高,因此一半狀況下,咱們使用GCD來完成多線程操做。
複製代碼
面試題:多個網絡請求完成後執行下一步? 第一種方式:使用dispatch_group
-(void)Btn2{
NSString *str = @"http://www.jianshu.com/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");
});
}
複製代碼
建立一個dispatch_group_t, 每次網絡請求前先dispatch_group_enter,請求回調後再dispatch_group_leave,對於enter和leave必須配合使用,有幾回enter就要有幾回leave,不然group會一直存在。當全部enter的block都leave後,會執行dispatch_group_notify的block。
第二種方式能夠採用信號量dispatch_semaphore_t
-(void)Btn3{
NSString *str = @"http://www.jianshu.com/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信號量爲基於計數器的一種多線程同步機制。若是semaphore計數大於等於1,計數-1,返回,程序繼續運行。若是計數爲0,則等待。dispatch_semaphore_signal(semaphore)爲計數+1操做,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)爲設置等待時間,這裏設置的等待時間是一直等待。 對於以上代碼通俗一點就是,開始爲0,等待,等10個網絡請求都完成了,dispatch_semaphore_signal(semaphore)爲計數+1,而後計數-1返回,程序繼續執行。 (這裏也就是爲何有個count變量的緣由,記錄網絡回調的次數,回調10次以後再發信號量,使後面程序繼續運行)。
什麼是dispatch_barrier_async(柵欄函數)?
dispatch_barrier_sync(dispatch_queue_t queue, ^{
})
複製代碼
sync和async sync: 同於當前線程, 能夠是主線程也能夠是子線程 async: 就是不一樣於當前線程, 能夠是主線程也能夠是子線程
XMPP: 1)XMPP 是一種基於XML的協議,XMPP是一個分散型通訊網絡 2)XMPP是一種基於標準通用標記語言的子集XML的協議,它繼承了在XML環境中靈活的發展性,XMPP有超強的擴展性。XMPP中定義了三個角色,客戶端,服務端,網關。通訊可以在這個三者的任意兩個之間雙向發生,而他們的傳輸是XML流 3)XMPP工做原理:全部從一個客戶端到另外一個客戶端的消息和數據都要經過服務端 4)XMPP容許創建並行的TCP套接字連接對全部鏈接上的客戶端和服務器端。持久的套接字的鏈接使得XMPP可以更有效的支持高級的具備存在能力的應用在帶寬和處理資源的使用中。
小結: 而XMPP的核心部分就是一個在網絡上分片段發送XML的流協議。這個流協議是XMPP的即時通信指令的傳遞基礎,也是一個很是重要的能夠被進一步利用的網絡基礎協議。因此能夠說,XMPP用TCP傳的是XML流。
======= 如何減小數據?
答:本質區別是Swift是靜態語言,而OC是動態語言,面試回去路上,纔想到問題的最好的答案😂----
解決方法以下:
//父類中
@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(">>>>>>>>>>>>")
}
}
複製代碼
由於extension中的方法是私有的,so子類訪問不到,所以要用public修飾下,@objc有如下兩點說明: • fileprivate 或者 private 保證方法私有 能在同一個類 或者 同一個文件(extension)中訪問這個方法 若是定義爲private 那麼只能在一個類中訪問 不能在類擴展中訪問 • 容許這個函數在「運行時」經過oc的消息機制調用
var nsString: NSString = NSString()
var swiftString:String = String()
var nsString: NSString = "dsx"
var swiftString:String = "dsx"
複製代碼
二者均可以使用本身的類名來直接進行初始化,下面的方法也是初始化,雖然寫法相同,可是NSString的意思是初始化了一個指針指向了這個字符串,但Swift String的意思則是把字符串字面量賦值給變量
for character in "My name is dsx".characters {
print(character)
}
複製代碼
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");
}
複製代碼
var strA: NSString = "12306"
var strB: NSString = "0.618"
var numOfInt = strA.integerValue;
var numOfDouble = strB.doubleValue;
複製代碼
var strA:String = "My name is dx"
strA.insert("s", atIndex: strA.characters.indexOf("x")!);
print(strA) // My name is dsx
複製代碼
僅僅能夠插入單個字符不能插字符串,若是裏面寫成ss 就會報錯Cannot convert value of type 'String' to expected argument type 'Character'
此文來源於簡書點擊原文地址便可跳轉 原文地址 掃描圖中二維碼或者搜索iOSSir關注公衆號,天天可掌握最新的文章資訊,添加微信:pingwen20 邀請你進入全網最大微信的交流羣 備註:iOS入羣