[iOS]底層原理三 : (多線程捆探、內(nèi)存管理)

底層原理一: (OC 本質(zhì)然爆、KVC、KVO黍图、Category曾雕、Block)
底層原理二: (Runtime、Runloop)
底層原理三 : (多線程助被、內(nèi)存管理)
底層原理四 : (性能優(yōu)化剖张、架構(gòu))
底層原理五 : (面試題目整理)

十四. 多線程

14.1 ios 多線程方案

pthread / NSThread /GCD /NSOperation
45.png

14.2GCD的常用函數(shù)

GCD中有2個(gè)用來(lái)執(zhí)行任務(wù)的函數(shù)
用同步的方式執(zhí)行任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:隊(duì)列
block:任務(wù)

用異步的方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

GCD源碼:https://github.com/apple/swift-corelibs-libdispatch

14.3 GCD的隊(duì)列

GCD的隊(duì)列可以分為2大類(lèi)型
并發(fā)隊(duì)列(Concurrent Dispatch Queue)
可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

串行隊(duì)列(Serial Dispatch Queue)
讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))

14.4 容易混淆的術(shù)語(yǔ)

有4個(gè)術(shù)語(yǔ)比較容易混淆:同步恰起、異步修械、并發(fā)、串行
同步和異步主要影響:能不能開(kāi)啟新的線程
同步:在當(dāng)前線程中執(zhí)行任務(wù)检盼,不具備開(kāi)啟新線程的能力
異步:在新的線程中執(zhí)行任務(wù)肯污,具備開(kāi)啟新線程的能力

并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
串行:一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)

14.5 各種隊(duì)列的執(zhí)行效果

46.png

14.6 GCD隊(duì)列組的使用

47.png

14.7 多線程的安全隱患

資源共享
1塊資源可能會(huì)被多個(gè)線程共享吨枉,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源
比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象蹦渣、同一個(gè)變量、同一個(gè)文件

當(dāng)多個(gè)線程訪問(wèn)同一塊資源時(shí)貌亭,很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題
48.png

14.8 多線程安全隱患的解決方案

49.png

14.9 iOS中的線程同步方案 線程安全.線程鎖

解決方案: 使用線程同步技術(shù)(同步,就是協(xié)同步調(diào),按預(yù)定的先后次序進(jìn)行)
常見(jiàn)線程同步技術(shù): 加鎖

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

14.10 OSSpinLock (自旋鎖) 不安全

50.png

14.11 OSUnfairLock (互斥鎖) 運(yùn)行效率最高

51.png

14.12 pthread_mutex

52.png

mutex 互斥鎖,等待鎖的線程會(huì)處于休眠狀態(tài)

14.13 NSLock柬唯、NSRecursiveLock

53.png

14.14 NSCondition

54.png

14.15 dispatch_queue (SerialQueue)

使用GCD串行隊(duì)列,實(shí)現(xiàn)同步

55.png

14.16 dispatch_semaphore (信號(hào)量) 可以用于控制最大并發(fā)數(shù)量

semaphore叫做”信號(hào)量”
信號(hào)量的初始值,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量
信號(hào)量的初始值為1圃庭,代表同時(shí)只允許1條線程訪問(wèn)資源锄奢,保證線程同步

56.png

14.17 @synchronized (互斥鎖)

@synchronized是對(duì)mutex遞歸鎖的封裝
源碼查看:objc4中的objc-sync.mm文件
@synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖,然后進(jìn)行加鎖剧腻、解鎖操作

14.18 iOS線程同步方案性能比較

57.png
os_unfair_lock  ios10 開(kāi)始
OSSpanLock      ios10 廢棄
dispatch_semaphore  
dispatch_mutex
dispatch_queue   串行
NSLock  對(duì) mutex 封裝
@synchronized 最差

14.19 自旋鎖拘央、互斥鎖比較

什么情況使用自旋鎖比較劃算?
預(yù)計(jì)線程等待鎖的時(shí)間很短
加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用书在,但競(jìng)爭(zhēng)情況很少發(fā)生
CPU資源不緊張
多核處理器

什么情況使用互斥鎖比較劃算灰伟?
預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
臨界區(qū)競(jìng)爭(zhēng)非常激烈

14.20 Atomic 和 Noatomic

atomic用于保證屬性setter、getter的原子性操作儒旬,相當(dāng)于在getter和setter內(nèi)部加了線程同步的鎖
可以參考源碼objc4的objc-accessors.mm
它并不能保證使用屬性的過(guò)程是線程安全的

14.21 多線程 讀寫(xiě)線程安全方案

思考如何實(shí)現(xiàn)以下場(chǎng)景
同一時(shí)間栏账,只能有1個(gè)線程進(jìn)行寫(xiě)的操作
同一時(shí)間帖族,允許有多個(gè)線程進(jìn)行讀的操作
同一時(shí)間,不允許既有寫(xiě)的操作挡爵,又有讀的操作

上面的場(chǎng)景就是典型的“多讀單寫(xiě)”竖般,經(jīng)常用于文件等數(shù)據(jù)的讀寫(xiě)操作,iOS中的實(shí)現(xiàn)方案有
pthread_rwlock:讀寫(xiě)鎖
dispatch_barrier_async:異步柵欄調(diào)用

14.22 pthread_rwlock

58.png

14.23 dispatch_barrier_async

59.png

十五. 內(nèi)存管理

15.1 CADisplayLink了讨、NSTimer使用注意

    CADisplayLink 保證調(diào)用頻率和刷幀頻率一直,60FPS, 不用設(shè)置時(shí)間間隔,每秒鐘60次
    可以使用 proxy 代理解決循環(huán)引用
    
    CADisplayLink捻激、NSTimer會(huì)對(duì)target產(chǎn)生強(qiáng)引用,如果target又對(duì)它們產(chǎn)生強(qiáng)引用前计,那么就會(huì)引發(fā)循環(huán)引用

解決方案1.使用block

60.png

解決方案2.使用代理對(duì)象(NSProxy)

61.png

15.2 NSProxy 也屬于基類(lèi)

代理,用于解決循環(huán)引用,,用于消息轉(zhuǎn)發(fā),不會(huì)在父類(lèi)查找方法
NSObject 和 NSProxy 區(qū)別

15.3 GCD定時(shí)器

NSTimer依賴于RunLoop胞谭,如果RunLoop的任務(wù)過(guò)于繁重,可能會(huì)導(dǎo)致NSTimer不準(zhǔn)時(shí)
而GCD的定時(shí)器會(huì)更加準(zhǔn)時(shí),GCD定時(shí)器,不依賴 Runloop ,會(huì)很準(zhǔn)時(shí),依賴內(nèi)核
62.png

15.4 iOS 程序的內(nèi)存布局

低地址-> 高地址
保留->代碼段->數(shù)據(jù)段(字符串常量,已初始化全局?jǐn)?shù)據(jù),未初始化數(shù)據(jù))>堆->棧內(nèi)存-> 內(nèi)核區(qū)域
代碼段: 編譯之后的代碼
數(shù)據(jù)段: 字符串常量,已經(jīng)初始化的全局變量,或者靜態(tài)變量,未初始化的全局變量,靜態(tài)變量
堆 (低>高)  通過(guò) alloc malloc calloc 動(dòng)態(tài)分配的內(nèi)存

棧 (高地址 從 低地址)  函數(shù)調(diào)用開(kāi)銷(xiāo)()
63.png
//main.cpp  
int a = 0; 全局初始化區(qū)  
char *p1; 全局未初始化區(qū)  
main()   {  
  int b; 棧  
  char s[] = "abc"; 棧  
  char *p2; 棧  
  char *p3 = "123456"; 123456\0在常量區(qū)男杈,p3在棧上丈屹。  
  static int c =0; 全局(靜態(tài))初始化區(qū)  
  p1 = (char *)malloc(10);  
  p2 = (char *)malloc(20);  
  分配得來(lái)得10和20字節(jié)的區(qū)域就在堆區(qū)伶棒。  
  strcpy(p1, "123456"); 123456\0放在常量區(qū)旺垒,編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。  
}  

15.5 Tagged Pointer

從64bit開(kāi)始肤无,iOS引入了Tagged Pointer技術(shù)先蒋,用于優(yōu)化NSNumber、NSDate宛渐、NSString等小對(duì)象的存儲(chǔ)

在沒(méi)有使用Tagged Pointer之前竞漾, NSNumber等對(duì)象需要?jiǎng)討B(tài)分配內(nèi)存、維護(hù)引用計(jì)數(shù)等窥翩,NSNumber指針存儲(chǔ)的是堆中NSNumber對(duì)象的地址值

使用Tagged Pointer之后业岁,NSNumber指針里面存儲(chǔ)的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲(chǔ)在了指針中

當(dāng)指針不夠存儲(chǔ)數(shù)據(jù)時(shí)寇蚊,才會(huì)使用動(dòng)態(tài)分配內(nèi)存的方式來(lái)存儲(chǔ)數(shù)據(jù)

objc_msgSend能識(shí)別Tagged Pointer笔时,比如NSNumber的intValue方法,直接從指針提取數(shù)據(jù)仗岸,節(jié)省了以前的調(diào)用開(kāi)銷(xiāo)

如何判斷一個(gè)指針是否為T(mén)agged Pointer允耿?
iOS平臺(tái),最高有效位是1(第64bit)
Mac平臺(tái)扒怖,最低有效位是1

判斷是否為T(mén)agged Pointer

64.png

15.6 OC對(duì)象的內(nèi)存管理

在iOS中右犹,使用引用計(jì)數(shù)來(lái)管理OC對(duì)象的內(nèi)存

一個(gè)新創(chuàng)建的OC對(duì)象引用計(jì)數(shù)默認(rèn)是1,當(dāng)引用計(jì)數(shù)減為0姚垃,OC對(duì)象就會(huì)銷(xiāo)毀,釋放其占用的內(nèi)存空間

調(diào)用retain會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1盼忌,調(diào)用release會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1

內(nèi)存管理的經(jīng)驗(yàn)總結(jié)
當(dāng)調(diào)用alloc积糯、new掂墓、copy、mutableCopy方法返回了一個(gè)對(duì)象看成,在不需要這個(gè)對(duì)象時(shí)君编,要調(diào)用release或者autorelease來(lái)釋放它
想擁有某個(gè)對(duì)象,就讓它的引用計(jì)數(shù)+1川慌;不想再擁有某個(gè)對(duì)象吃嘿,就讓它的引用計(jì)數(shù)-1

可以通過(guò)以下私有函數(shù)來(lái)查看自動(dòng)釋放池的情況
extern void _objc_autoreleasePoolPrint(void);

15.7 copy和mutableCopy

65.png

15.8 引用計(jì)數(shù)器的存儲(chǔ) retaincount

66.png

15.9 dealloc

67.png

15.10 autoreleasePool 自動(dòng)釋放池

自動(dòng)釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是:__AtAutoreleasePool、AutoreleasePoolPage

調(diào)用了autorelease的對(duì)象最終都是通過(guò)AutoreleasePoolPage對(duì)象來(lái)管理的

源碼分析
-clang重寫(xiě)@autoreleasepool
-objc4源碼:NSObject.mm
68.png

15.11 AutoreleasePoolPage的結(jié)構(gòu)

69.png
調(diào)用push方法會(huì)將一個(gè)POOL_BOUNDARY入棧梦重,并且返回其存放的內(nèi)存地址

調(diào)用pop方法時(shí)傳入一個(gè)POOL_BOUNDARY的內(nèi)存地址兑燥,會(huì)從最后一個(gè)入棧的對(duì)象開(kāi)始發(fā)送release消息,直到遇到這個(gè)POOL_BOUNDARY

id *next指向了下一個(gè)能存放autorelease對(duì)象地址的區(qū)域

15.12 runloop 和 autoreleasePool

iOS在主線程的Runloop中注冊(cè)了2個(gè)Observer
-第1個(gè)Observer監(jiān)聽(tīng)了kCFRunLoopEntry事件琴拧,會(huì)調(diào)用objc_autoreleasePoolPush()
-第2個(gè)Observer
    監(jiān)聽(tīng)了kCFRunLoopBeforeWaiting事件降瞳,會(huì)調(diào)用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
    監(jiān)聽(tīng)了kCFRunLoopBeforeExit事件蚓胸,會(huì)調(diào)用objc_autoreleasePoolPop()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挣饥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子沛膳,更是在濱河造成了極大的恐慌扔枫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锹安,死亡現(xiàn)場(chǎng)離奇詭異短荐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)八毯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)搓侄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人话速,你說(shuō)我怎么就攤上這事讶踪。” “怎么了泊交?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乳讥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我廓俭,道長(zhǎng)云石,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任研乒,我火速辦了婚禮汹忠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己宽菜,他們只是感情好谣膳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著铅乡,像睡著了一般继谚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阵幸,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天花履,我揣著相機(jī)與錄音,去河邊找鬼挚赊。 笑死诡壁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咬腕。 我是一名探鬼主播欢峰,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼涨共!你這毒婦竟也來(lái)了纽帖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤举反,失蹤者是張志新(化名)和其女友劉穎懊直,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體火鼻,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡室囊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了魁索。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片融撞。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖粗蔚,靈堂內(nèi)的尸體忽然破棺而出尝偎,到底是詐尸還是另有隱情,我是刑警寧澤鹏控,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布致扯,位于F島的核電站,受9級(jí)特大地震影響当辐,放射性物質(zhì)發(fā)生泄漏抖僵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一缘揪、第九天 我趴在偏房一處隱蔽的房頂上張望耍群。 院中可真熱鬧义桂,春花似錦、人聲如沸世吨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耘婚。三九已至,卻和暖如春陆赋,著一層夾襖步出監(jiān)牢的瞬間沐祷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工攒岛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赖临,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓灾锯,卻偏偏與公主長(zhǎng)得像兢榨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顺饮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較吵聪? GCD中的三種隊(duì)列...
    有夢(mèng)想的老伯伯閱讀 1,020評(píng)論 0 4
  • 一吟逝、基礎(chǔ)概念 有4個(gè)術(shù)語(yǔ)比較容易混淆:同步、異步赦肋、并發(fā)块攒、串行 1.進(jìn)程和線程 進(jìn)程:進(jìn)程是計(jì)算機(jī)中已運(yùn)行程序的實(shí)體...
    666真666閱讀 1,281評(píng)論 0 7
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,100評(píng)論 1 32
  • 1、測(cè)量體重佃乘,身體指標(biāo) 今天的體重還算穩(wěn)定囱井,但體脂率漲了,從三天前的27.3%漲到了29.6%趣避。老師在課上說(shuō)的庞呕,今...
    叮噹Vicky閱讀 226評(píng)論 0 0
  • 1.1 啟動(dòng)時(shí)間 關(guān)于應(yīng)用的啟動(dòng)時(shí)間的測(cè)試,分為三類(lèi): 1.首次啟動(dòng) --應(yīng)用首次啟動(dòng)所花費(fèi)的時(shí)間 2.非首次...
    黃海佳閱讀 5,017評(píng)論 0 0