Runloop 本質(zhì)是什么?

Runloop 本質(zhì)是什么?
本質(zhì)是一個OC對象,內(nèi)部也有isa指針。
Runloop 結(jié)構(gòu)分析
結(jié)構(gòu)內(nèi)部有2個重要的成員變量

Runloop 運行模式
Runloop 事件處理流程
Runloop 牽涉概念
自動釋放池
內(nèi)存管理
定時器
線程包活
卡頓檢測
OC對象本質(zhì)

KVC與kvo
KVC
什么是kvc,
KVC,俗稱“鍵值編碼”,全稱是“Key Value Coding”,它是一種可以直接通過字符串的名稱(Key)來訪問類屬性的機制,而不是通過調(diào)用Setter或者Getter方法來進行訪問。
kvc 能干什么?

setValue:forkey 給對象的屬性賦值,但是層級只有一層。
setValue:forkeyPath,支持一級屬性賦值,也支持多級屬性賦值
利用kvc給對象的成員變量賦值。

kvc 的實現(xiàn)原理(kvc的賦值,取值流程)
賦值流程
1 依次查找方法看看有沒有實現(xiàn)。setKey:、_setKey,如果找到了方法,則傳參并且調(diào)用方法。
2,如果沒有找到方法,則通過accessInstanceVariablesDirectly一個方法,查看是否能直接放問成員變量。能則給成員變量賦值,如果不能則拋出異常。
取值流程
1,依次查找?guī)讉€方法是否實現(xiàn)。_key,_isKey,key
,isKey。如果實現(xiàn)就調(diào)用取值。
2,如果沒有實現(xiàn),則通過一個方法,查看是否能直接訪問成員變量。如果能則獲取成員變量的值,如果不能則報錯。
KVC 相關(guān)問題
KVC 是否線程安全問題
可能會引出:kvc是否線程安全問題,以及自己設(shè)計kvo(主要是模型思路和上面幾個點的判斷和處理)
5.線程安全 (自旋鎖,遞歸鎖,互斥鎖,信號量,讀寫鎖,柵欄函數(shù)) 底層都是pthread_mutex的封裝
線程安全
內(nèi)存管理
內(nèi)存管理的三種方案,
iOS中主要通過引用計數(shù)來管理內(nèi)存,當引用計數(shù)為0的時候銷毀內(nèi)存。
蘋果一共提供了3中方案,(TaggetPointer、NONPOINTER_ISA、散列表),
TaggetPointer

Tagged Pointer是專??來存儲?的對象,例如NSNumber,NSDat
Tagged Pointer指針的值不再是地址了,?是真正的值。所以,實際上它不再是?個對象了,它只是?個披著對象?的普通變量?已。所以,它的內(nèi)存并不存儲 在堆中,也不需要創(chuàng)建和釋放

NONPOINTER_ISA
一切對象均為objc_object對象,objc_object對象內(nèi)部有一個isa屬性。這個isa指針之前只是一個純指針?,F(xiàn)在也報含指針外的其他信息,例如對象的引用計數(shù)、是否被弱引用...這時這個isa就是NONPOINTER_ISA。
isa是isa_t類型的聯(lián)合體,其內(nèi)部通過位域技術(shù)儲存很多了對象的信息。
NONPOINTER_ISA 中的引用計數(shù)存儲位置
/表示該對象的引用計數(shù)值,滿了就會存在sidetable 中/
uintptr_t extra_rc : 19;
復(fù)制代碼
源碼中的extra_rc就是用來存儲引用計數(shù)的,具體原理放到下面的引用計數(shù)部分說明。
散列表--引用計數(shù)&弱引用計數(shù)
散列表的結(jié)構(gòu)
系統(tǒng)維護了一張全局的Hash表,里面存了一張張SideTable散列表,而這個散列表中就儲存了對象的引用計數(shù)以及弱引用情況。
struct SideTable {
spinlock_t slock;//鎖,用于控制數(shù)據(jù)訪問安全
RefcountMap refcnts;//引用計數(shù)表s
weak_table_t weak_table;//弱引用計數(shù)表s
...
...
};

復(fù)制代碼

spinlock_t slock 鎖,用于控制這張散列表SideTble的數(shù)據(jù)訪問安全。

RefcountMap refcnts:引用計數(shù)表RefcountMap,用于儲存對象的引用計數(shù)情況,(在isa_t中extra_rc位引用計數(shù)滿了后會把一半的引用計數(shù)放到某個散列表SideTable中的引用計數(shù)表中)

weak_table_t weak_table:弱引用情況表,用于儲存對象的弱引用情況。

weak_table_t 弱引用計數(shù)表
1,weak_table_t 是個二維數(shù)組,里面包含了一個個weak_table,weak_table里面是一個個weak_entry數(shù)組
2, 當一個對象的屬性被設(shè)置成weak時,weak_table表中會查找當內(nèi)部有沒有該對象的弱引用數(shù)組(weak_entry數(shù)組),如果有就直接插入這個屬性到這個weak_entry數(shù)組,沒有就先創(chuàng)建weak_entry數(shù)組再插入
3,、當對象被釋放時delloc,會通過對象指針去查找weak_table沒有該對象的weak_entry數(shù)組,有的話遍歷weak_entry數(shù)組,將內(nèi)部的屬性置為nil;最后將這個weak_entry數(shù)組remov

為什么是 weak_entry數(shù)組 (因為一個對象可能擁有多個弱應(yīng)用屬性)

散列表
檢測內(nèi)存管理的方式
1,xcode 自帶一個靜態(tài)內(nèi)存分析和Instrument
2,工具,MLeaksFinder:精準 iOS 內(nèi)存泄露檢測工具
內(nèi)存泄漏主要有兩種方式

Laek Memory 這種是忘記 Release 操作所泄露的內(nèi)存。
Abandon Memory 這種是循環(huán)引用,無法釋放掉的內(nèi)存。

MLeaksFinder 實現(xiàn)的原理
1,不入侵開發(fā)代碼
這里使用了 AOP 技術(shù),hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法,關(guān)于如何 hook,請參考 Method Swizzling。
2, 實現(xiàn)原理

為基類 NSObject 添加一個方法 -willDealloc 方法
該方法的作用是,先用一個弱指針指向 self,并在一小段時間(3秒)后,通過這個弱指針調(diào)用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中斷言.

如果已經(jīng)釋放那么空指針不會調(diào)用斷言函數(shù),如果沒有釋放舊會斷點。
空指針是指向nil(調(diào)用方法無反應(yīng)),野指針是指向垃圾內(nèi)存(危險)
內(nèi)存泄漏
內(nèi)存泄漏的解決方案
定時器,block 代理的循環(huán)引用。
1,用weak修飾,
2,nstimer 可以考慮添加中間層
ARC和MRC如何管理內(nèi)存
arc 依靠llvm編譯器和Runtime 實現(xiàn)
llvm 干了什么

加上,release,retain,autoRlease 操作

當我們編譯源碼的時候,編譯器會分析源碼中每個對象的生命周期,然后基于這些對象的生命周期,來添加相應(yīng)的引用計數(shù)操作代碼。所以,ARC 是工作在編譯期的一種技術(shù)方案,這樣的好處是:
編譯之后,ARC 與非 ARC 代碼是沒有什么差別的,所以二者可以在源碼中共存。實際上,你可以通過編譯參數(shù) -fno-objc-arc 來關(guān)閉部分源代碼的 ARC 特性。
runtime 干了什么
運行時,清空弱引用的對象。
用一張哈希表,key 為弱引用的對象地址。values為數(shù)組,里面存放指針。
當弱引用的對象被釋放時,會將數(shù)組里面的指針全部置為nil。
自動釋放池
@autoreleasepool{}關(guān)鍵字通過編譯器轉(zhuǎn)換成objc_autoreleasePoolPush和objc_autoreleasePoolPop這一對方法。 將自動釋放池中的對象加到自動釋放池中。

由objc_autoreleasePoolPush作為自動釋放池作用域的第一個函數(shù)。
使用objc_autorelease將對象加入自動釋放池。
由objc_autoreleasePoolPop作為自動釋放池作用域的最后一個函數(shù)。

自動釋放池的結(jié)構(gòu)
自動釋放池都是由一個或者多個AutoreleasePoolPage組成,page的 SIZE 為 4096 bytes ,它們通過parent和child指針組成一個雙向鏈表。

hotPage:是當前正在使用的page,操作都是在hotPage上完成,一般處于鏈表末端或者倒數(shù)第二個位置。存儲在 TLS 中,可以理解為一個每個線程共享一個自動釋放池鏈表。
coldPage:位于鏈表頭部的page,可能同時為hotPage。

release 和autoRelease 方法
release 會立即釋放,見與set方法中先retain后release
autoRelease 則會等到自動釋放池結(jié)束的時候釋放,見與類方法創(chuàng)建對象的時候。
[NSMutableArarry arry]
autorelease pool的釋放時機

MRC下調(diào)用自動釋放池release方法后,會對在autorelease對象進行釋放,因此,此后訪問的person變量為野指針,再去訪問自然會導(dǎo)致crash。

而ARC下,@autoreleasepool并不會立即在結(jié)束括號符后,立即釋放person變量,而是會在一個合適的時間點。

合適的時間點。因此,當runloop進入kCFRunLoopEntry時,自動釋放池會進行push操作,當runloop進入kCFRunLoopBeforeWaiting | kCFRunLoopExit狀態(tài)時,自動釋放池會進行pop操作。

自動釋放池的應(yīng)用
autorelease pool和RunLoop(運行循環(huán))

主線程中,系統(tǒng)已經(jīng)在main.m中通過@autoreleasepool創(chuàng)建了自動釋放池,所以我們無需額外去創(chuàng)建和釋放了.

子線程中自動釋放池的創(chuàng)建和釋放都無需我們進行額外的操作。當然,在某些場景下,也可以手動通過@autoreleasepool進行創(chuàng)建和釋放。

autorelease pool和降低內(nèi)存峰值
當被加到自動釋放池的對象越來越來多,卻沒有得到及時釋放,就會導(dǎo)致內(nèi)存溢出。這個時候,我們可以手動添加自動釋放池來解決這個問題。
block
block 是什么?
block 是帶有自動變量的匿名函數(shù)。
block 的本質(zhì)
blcok的本質(zhì)是OC對象,其結(jié)構(gòu)體內(nèi)部也帶有isa指針。
block的變量捕獲機制
局部變量(捕獲值,)存放在block內(nèi)部一個同名的成員變量中.
靜態(tài)變量(靜態(tài)變量的地址捕獲到block中),存放在block內(nèi)部一個同名的成員變量中
當訪問全局變量時,因為全局變量是一直存在,不會銷毀,所以在block中直接訪問全局變量,不需要進行捕獲。
block 的類型 (全局,堆,棧)
2,block的類型,有沒訪問auto變量,區(qū)分是全局的還是棧空間的
3,??臻g類型的block 執(zhí)行copy操作,變成堆空間的block .
如何改變block內(nèi)捕獲的變量值(__block的用法)
__block 原理
在block內(nèi)部來修改外部變量的值,當然,__block只能用來修飾auto變量,不能用來修飾全局變量和靜態(tài)變量。
__block修飾的auto變量,編譯器會將此變量封裝成一個結(jié)構(gòu)體(其實也是一個對象),結(jié)構(gòu)體內(nèi)部有以下幾個成員變量 isa,val(使用的外部變量,如果是基本數(shù)據(jù)類型,就是變量的值,如果是對象類型,就是指向?qū)ο蟮闹羔?
如果是修飾對象類型的auto變量,那么生成的結(jié)構(gòu)體中會多出copy和dispose兩個函數(shù),用來管理person對象的內(nèi)存。
block循環(huán)引用帶來的問題。如何解決
如果block作為一個對象的屬性,并且在block中也使用到了這個對象,則會產(chǎn)生循環(huán)引用,導(dǎo)致block和對象相互引用,無法釋放。
用weak 修飾block 內(nèi)用捕獲的對象。
性能優(yōu)化
事件響應(yīng)傳遞鏈
尋找 響應(yīng)者(iOS響應(yīng)者鏈)
觸碰屏幕時,系統(tǒng)會把這一操作封裝成一個UIEvent放到事件隊列里。然后application從事件隊列中取出這個事件。接著尋找響應(yīng)這個事件的最佳視圖。此時用到2個重要的方法。

(void) hitTest (返回視圖層級中能響應(yīng)觸控點最深的視圖)
(Bool) pointInside (返回視圖中是否包含響應(yīng)的點)

尋找響應(yīng)者結(jié)論
1,尋找事件的最佳響應(yīng)視圖是通過hittest和pointInside完成的。
2,hittest的調(diào)用順序是從UIWindow開始的,對每個視圖的子視圖一次調(diào)用。子視圖的調(diào)用順序是從后面往前,也可以說是顯示從上面到下面。
3,遍歷直到找到響應(yīng)視圖,然后逐級返回到UIWindow返回此視圖。
處理者
卡頓現(xiàn)象的解決和原理
組件化開發(fā)
mach-o
埋點
啟動流程
app的啟動流程分為2種(冷啟動和熱啟動)
app 的啟動階段

dyld
runtime
main

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先對block有一個基本的認識block本質(zhì)上也是一個oc對象,他內(nèi)部也有一個isa指針。block是封裝了函數(shù)調(diào)...
    叫我小黑閱讀 436評論 0 0
  • 當需要執(zhí)行異步操作,或同步多個操作時,塊(Block)會非常有用。這一篇文章將介紹 Block 的本質(zhì)。如果你對 ...
    pro648閱讀 833評論 0 6
  • 1、block的基本概念及使用 blcok是一種特殊的數(shù)據(jù)結(jié)構(gòu),它可以保存一段代碼,等到需要的時候進行調(diào)用執(zhí)行這段...
    __weak閱讀 748評論 0 2
  • 上一篇文章iOS底層原理總結(jié) - 探尋block的本質(zhì)(一)中已經(jīng)介紹過block的底層本質(zhì)實現(xiàn)以及了解了變量的捕...
    xx_cc閱讀 11,059評論 8 54
  • 上一篇文章iOS底層原理總結(jié) - 探尋block的本質(zhì)(一)中已經(jīng)介紹過block的底層本質(zhì)實現(xiàn)以及了解了變量的捕...
    二斤寂寞閱讀 676評論 0 1

友情鏈接更多精彩內(nèi)容