iOS 知識點回顧(二)
- 2020 年 1 月 3 日
- 筆記

溫故而知新
目錄
- Runtime
- 消息發送機制
- isMemberOfClass 和 isKindOfClass
- Super 和 Self
- @synthesize/@dynamic
- RunLoop
- 執行緒和進程
- 什麼是優先順序反轉?
- 自旋鎖、互斥鎖比較
1. Runtime
OC是一門動態性比較強的程式語言,允許很多操作推遲到程式運行時再進行;OC的動態性就是由Runtime來支撐和實現的,Runtime是一套C語言的API,封裝了很多動態性相關的函數;平時編寫的OC程式碼,底層都是轉換成了Runtime API進行調用。 應用:
- 利用關聯對象(AssociatedObject)給分類添加屬性
- 遍歷類的所有成員變數(修改textfield的佔位文字顏色、字典轉模型、自動歸檔解檔)
- 交換方法實現(交換系統的方法)
- 利用消息轉發機制解決方法找不到的異常問題
- Runtime 相關API
1、涉及類的API: & 動態創建一個類(參數:父類,類名,額外的記憶體空間) Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) & 註冊一個類(要在類註冊之前添加成員變數) void objc_registerClassPair(Class cls) & 銷毀一個類 void objc_disposeClassPair(Class cls) & 獲取isa指向的Class Class object_getClass(id obj) & 設置isa指向的Class Class object_setClass(id obj, Class cls) & 判斷一個OC對象是否為Class BOOL object_isClass(id obj) & 判斷一個Class是否為元類 BOOL class_isMetaClass(Class cls) & 獲取父類 Class class_getSuperclass(Class cls) 2、涉及成員變數的API : & 獲取一個實例變數資訊 Ivar class_getInstanceVariable(Class cls, const char *name) & 拷貝實例變數列表(最後需要調用free釋放) Ivar *class_copyIvarList(Class cls, unsigned int *outCount) & 設置和獲取成員變數的值 void object_setIvar(id obj, Ivar ivar, id value) id object_getIvar(id obj, Ivar ivar) & 動態添加成員變數(已經註冊的類是不能動態添加成員變數的) BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types) & 獲取成員變數的相關資訊 const char *ivar_getName(Ivar v) const char *ivar_getTypeEncoding(Ivar v) 3、涉及屬性的API & 獲取一個屬性 objc_property_t class_getProperty(Class cls, const char *name) & 拷貝屬性列表(最後需要調用free釋放) objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) & 動態添加屬性 BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) & 動態替換屬性 void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) & 獲取屬性的一些資訊 const char *property_getName(objc_property_t property) const char *property_getAttributes(objc_property_t property) 4、涉及方法的API & 獲得一個實例方法、類方法 Method class_getInstanceMethod(Class cls, SEL name) Method class_getClassMethod(Class cls, SEL name) & 方法實現相關操作 IMP class_getMethodImplementation(Class cls, SEL name) IMP method_setImplementation(Method m, IMP imp) void method_exchangeImplementations(Method m1, Method m2) & 拷貝方法列表(最後需要調用free釋放) Method *class_copyMethodList(Class cls, unsigned int *outCount) & 動態添加方法 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) & 動態替換方法 IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) & 獲取方法的相關資訊(帶有copy的需要調用free去釋放) SEL method_getName(Method m) IMP method_getImplementation(Method m) const char *method_getTypeEncoding(Method m) unsigned int method_getNumberOfArguments(Method m) char *method_copyReturnType(Method m) char *method_copyArgumentType(Method m, unsigned int index) & 選擇器相關 const char *sel_getName(SEL sel) SEL sel_registerName(const char *str) & 用block作為方法實現 IMP imp_implementationWithBlock(id block) id imp_getBlock(IMP anImp) BOOL imp_removeBlock(IMP anImp)
2. 消息發送機制
OC中的方法調用其實都是轉成了objc_msgSend函數的調用,給receiver(方法調用者)發送了一條消息(selector方法名)。

Class的結構
objc_msgSend底層有3大階段:消息發送(當前類、父類中查找)、動態方法解析、消息轉發。
-
- 消息發送

消息發送
-
- 動態方法解析

動態方法解析

利用RunTime動態添加方法
-
- 消息轉發

消息轉發
3. isMemberOfClass 和 isKindOfClass

isMemberOfClass 和 isKindOfClass 底層實現
4. Super 和 Self
- self 調用方法時會使用 objc_msgSend 函數: id objc_msgSend(id theReceiver, SEL theSelector, …)。第 一個參數是消息接收者,第二個參數是調用的具體類方法的 selector,後面是 selector 方法的可變參數。
- 當使用 super 調用時,會使用 objc_msgSendSuper 函數:id objc_msgSendSuper(struct objc_super *super, SEL op, …)第一個參數是個objc_super的結構體(如下),第二個參數是調用的具體類方法的 selector。 struct objc_super { id receiver; receiver是消息接收者 Class superClass; 父類 }
- self 和 super 指向的是同一個對象,只是查找方法起始位置不同,一個是從本類開始,一個是從本類的父類開始;
- self是類,super是預編譯指令(指代從父類方法列表開始查詢方法的self);
5. @synthesize/@dynamic
@property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
@synthesize 表示如果屬性沒有手動實現setter和getter方法,編譯器會自動加上這兩個方法。 @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現,不自動生成。
6. RunLoop

RunLoop應用
- 6.1 RunLoop的基本作用:
- 保持程式的持續運行(執行緒常駐)
- 處理App中的各種事件(比如觸摸事件、定時器事件等)
- 節省CPU資源,提高程式性能:該做事時做事,該休息時休息
- 實際應用:控制執行緒生命周期(執行緒保活、解決NSTimer在滑動時停止工作的問題、監控應用卡頓、性能優化
- 6.2 RunLoop和執行緒的關係:
- 每條執行緒都有唯一的一個與之對應的RunLoop對象
- RunLoop保存在一個全局的Dictionary里,執行緒作為key,RunLoop作為value
- 執行緒剛創建時並沒有RunLoop對象,RunLoop會在第一次獲取它時創建, RunLoop會在執行緒結束時銷毀
- 主執行緒的RunLoop已經自動獲取(創建),子執行緒默認沒有開啟RunLoop
- 6.3 RunLoop相關的類

RunLoop相關的類
CFRunLoopModeRef:RunLoop的運行模式
- 一個RunLoop包含若干個Mode,每個Mode又包含若干個Source0/Source1/Timer/Observer
- RunLoop啟動時只能選擇其中一個Mode,作為currentMode
- 如果需要切換Mode,只能退出當前Loop,再重新選擇一個Mode進入;不同組的Source0 / Source1 / Timer / Observer 能分隔開來,互不影響
- 如果Mode里沒有任何Source0 / Source1 / Timer / Observer,RunLoop會立馬退出
常見的幾種Mode
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認Mode,通常主執行緒是在這個Mode下運行。
- UITrackingRunLoopMode:介面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證介面滑動時不受其他 Mode 影響。
- NSRunLoopCommonModes:是偽模式,model集合,指可以在標記為Common Modes的模式下運行,目前被標記為Common Modes的模式: kCFRunLoopDefaultMode,UITrackingRunLoopMode。
- UIInitializationRunLoopMode:在剛啟動App時第進入的第一個 Mode,啟動完成後就不再使用
- GSEventReceiveRunLoopMode:接受系統內部事件,通常用不到
CFRunLoopSourceRef:輸入源 / 事件源。 Source 有兩個版本:Source0 和 Source1。Source0 只包含了一個回調(函數指針),它並不能主動觸發事件。使用時,你需要先調用 CFRunLoopSourceSignal(source),將這個 Source0 標記為待處理,然後手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。Source1 包含了一個 mach_port 和一個回調(函數指針),被用於通過內核和其他執行緒相互發送消息。這種 Source1 能主動喚醒 RunLoop 的執行緒。
CFRunLoopTimerRef:基於時間的觸發器。它和 NSTimer 是 toll-free bridged 的,可以混用。其包含一個時間長度和一個回調(函數指針)。當其加入到 RunLoop 時,RunLoop 會註冊對應的時間點,當時間點到時,RunLoop 會被喚醒以執行那個回調。
CFRunLoopObserverRef:觀察監聽RunLoop的活動狀態。每個 Observer 都包含了一個回調(函數指針),當 RunLoop 的狀態發生變化時,觀察者就能通過回調接受到這個變化。可以觀察到的狀態如下圖:

RunLoop的活動狀態
- 6.4 RunLoop的運行邏輯

RunLoop 模型圖

RunLoop的運行邏輯

RunLoop的運行邏輯
7. 執行緒和進程的區別
- 調度 :在引入執行緒的作業系統中,執行緒是調度和分配的基本單位 ,進程是資源擁有的基本單位 。把傳統進程的兩個屬性分開,執行緒便能輕裝運行,從而可顯著地提高系統的並發程度。 在同一進程中,執行緒的切換不會引起進程的切換;在由一個進程中的執行緒切換到另一個進程中的執行緒時,才會引起進程的切換。
- 並發性 :在引入執行緒的作業系統中,不僅進程之間可以並發執行,而且在一個進程中的多個執行緒之間亦可並發執行,因而使作業系統具有更好的並發性,從而能更有效地使用系統資源和提高系統吞吐量。
- 擁有資源 :不論是傳統的作業系統,還是設有執行緒的作業系統,進程都是擁有資源的一個獨立 單位,它可以擁有自己的資源。 一般地說,執行緒自己不擁有系統資源(只有一些必不可少的資源),但它可以訪問其隸屬進程的資源。
- 系統開銷: 由於在創建或撤消進程時,系統都要為之分配或回收資源,因此,作業系統所付出的開銷將顯著地大於在創建或撤消執行緒時的開銷。 進程切換的開銷也遠大於執行緒切換的開銷。
8. 什麼是優先順序反轉?
(1) 簡單從字面上來說,就是低優先順序的任務先於高優先順序的任務執行了,優先順序搞反了。那在什麼情況下會生這種情況呢? 假設三個任務準備執行,A,B,C,優先順序依次是A>B>C; 首先:C處於運行狀態,獲得CPU正在執行,同時佔有了某種資源; 其次:A進入就緒狀態,因為優先順序比C高,所以獲得CPU,A轉為運行狀態;C進入就緒狀態; 第三:執行過程中需要使用資源,而這個資源又被等待中的C佔有的,於是A進入阻塞狀態,C回到運行狀態; 第四:此時B進入就緒狀態,因為優先順序比C高,B獲得CPU,進入運行狀態;C又回到就緒狀態; 第五:如果這時又出現B2,B3等任務,他們的優先順序比C高,但比A低,那麼就會出現高優先順序任務的A不能執行,反而低優先順序的B,B2,B3等任務可以執行的奇怪現象,而這就是優先反轉。 (2)如何解決優先順序反轉 高優先順序任務A不能執行的原因是C霸佔了資源,而C如果不能獲得CPU,不釋放資源,那A也只好一直等在那,所以解決優先順序反轉的原則肯定就是讓C儘快執行,儘早把資源釋放了。基於這個原則產生了兩個方法: 2.1 優先順序繼承 當發現高優先順序的任務因為低優先順序任務佔用資源而阻塞時,就將低優先順序任務的優先順序提升到等待它所佔有的資源的最高優先順序任務的優先順序。 2.2 優先順序天花板 優先順序天花板是指將申請某資源的任務的優先順序提升到可能訪問該資源的所有任務中最高優先順序任務的優先順序.(這個優先順序稱為該資源的優先順序天花板) 2.3 兩者的區別 優先順序繼承:只有一個任務訪問資源時一切照舊,沒有區別,只有當高優先順序任務因為資源被低優先順序佔有而被阻塞時,才會提高佔有資源任務的優先順序;而優先順序天花板,不論是否發生阻塞,都提升,即誰先拿到資源,就將這個任務提升到該資源的天花板優先順序。
9. 自旋鎖、互斥鎖比較
什麼情況使用自旋鎖比較划算?
- 預計執行緒等待鎖的時間很短
- 加鎖的程式碼(臨界區)經常被調用,但競爭情況很少發生
- CPU資源不緊張
- 多核處理器
什麼情況使用互斥鎖比較划算?
- 預計執行緒等待鎖的時間較長
- 單核處理器
- 臨界區有IO操作
- 臨界區程式碼複雜或者循環量大
- 臨界區競爭非常激烈
如果需要跟我交流的話: ※ Github: https://github.com/wsl2ls ※ 簡書:https://www.jianshu.com/u/e15d1f644bea ※ 微信公眾號:iOS2679114653 ※ QQ群:835303405