本篇內容不做爲任何題目的解答,僅僅是我的學習記錄,若有錯誤還請指正。算法
atomic
的seter
/getter
內部實現是用了互斥鎖來保證seter
/getter
在多線程中的安全,但atomic
修飾的對象是自定義的,可能並無加鎖,在多線程中atomic
修飾對象並不能保證線程安全。編程
nonatomic
的setter
/getter
方法的實現並無加互斥鎖,因此nonatomic
修飾的對象是非線程安全的,同時nonatomic
的setter
/getter
方法也是非線程安全的,但也正由於沒有互斥鎖因此性能要比atomic
好。swift
(舉例:當多個線程同時調用同一屬性的讀取方法時,線程1會get
到一個肯定的值,可是get
的值不可控,多是線程2或者線程3...set
以後的值,也多是以前的值)數組
被weak
修飾的對象在被釋放時候會被自動置爲nil
。緩存
runtime
維護了一個weak
表,用於存儲指向某個對象的全部weak
指針。weak
表實際上是一個hash
哈希表,Key
是所指對象的地址,Value
是weak
指針的地址數組(這個地址的值是所指對象指針的地址)。安全
一、初始化時:runtime
會調用objc_initWeak
函數,初始化一個新的weak
指針指向對象的地址。bash
二、添加引用時:objc_initWeak
函數會調用 objc_storeWeak()
函數, objc_storeWeak()
的做用是更新指針指向,建立對應的弱引用表。服務器
三、釋放時,調用clearDeallocating
函數。clearDeallocating
函數首先根據對象地址獲取全部weak
指針地址的數組,而後遍歷這個數組把其中的數據設爲nil
,最後把這個entry
從weak
表中刪除,最後清理對象的記錄。網絡
在runtime
內存空間中,SideTables
是一個64個元素長度的hash
數組,裏面存儲了SideTable
。SideTables
的hash
鍵值就是一個對象obj
的address
。 所以能夠說,一個obj
,對應了一個SideTable
。可是一個SideTable
,會對應多個obj
。由於SideTable
的數量只有64個,因此會有不少obj
共用同一個SideTable
。而在一個SideTable
中,又有兩個成員,分別是RefcountMap refcnts
(引用計數的 hash
表,其key
是obj
的地址,而value
,則是obj
對象的引用計數)和weak_table_t weak_table
(weak
引用全局hash
表),weak_table
則存儲了弱引用obj
的指針的地址,其本質是一個以obj
地址爲key
,弱引用obj
的指針的地址做爲value
的hash
表。hash
表的節點類型是weak_entry_t
。 多線程
由於block
變量默認是聲明爲棧變量的,爲了可以在block
的聲明域外使用,因此要把block
copy
到堆,因此說爲了block
屬性聲明和實際的操做一致,block
用copy
修飾。
對於strong
修飾符:
首先,在OC
中的三種類型的block
:
_NSConcreateGlobalBlock
全局的靜態block
,不會訪問任何外部變量。_NSConcreateStackBlock
保存在棧中的block
,當函數返回時會被銷燬。_NSConcreateMallocBlock
保存在堆區的block
,當引用計數爲0時被銷燬。在MRC
環境下,block
存放在全局區、堆區、棧區,ARC
環境下只存放在全局區、堆區(棧區block
,在ARC
狀況下會自動拷貝到堆區)。
因此在MRC
環境下,若是block
要訪問外部變量,就必須用copy
修飾,而在ARC
環境下,若是block
訪問了外部變量,系統會自動將棧區block
拷貝到堆區,因此也可使用strong
。只不過copy
是MRC
遺留下來的,習慣而已
block
中使用到的局部變量,都會在編譯時動態建立的block
結構體中建立一個與局部變量名稱同樣的實例變量,該實例變量存儲着外部的局部變量的值,而當執行block
時,再將這裏存儲下來的值取出來。
__block
所起到的做用就是隻要觀察到該變量被block
所持有,就將外部變量在棧中的內存地址拷貝堆中。__block
將變量包裝成對象,而後在把捕獲到的變量封裝在block
的結構體裏面,block
內部存儲的變量爲結構體指針,也就能夠經過指針找到內存地址進而修改變量的值。
傳遞鏈:
(1)發生觸摸事件後,系統會利用Runloop
將該事件加入到一個由UIApplication
管理的隊列事件中.
(2)UIApplication
會從事件隊列中取出最前面的事件,並將事件分發下去處理,發送事件給應用程序的主窗口UIWindow
.
(3)主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸事件
(4)遍歷找到合適的視圖控件後,就會調用視圖控件的touches
方法來處理事件:touchesBegin
...此子控件就是咱們須要找到的第一響應者
響應鏈:
(1)判斷第一響應者可否響應事件,若是第一響應者能進行響應,則事件在響應鏈中的傳遞終止。若是第一響應者不能響應,則將事件傳遞給 nextResponder
也就是一般的superview
進行事件響應
(2)若是事件繼續上傳至UIWindow
而且沒法響應,它將會把事件繼續上報給UIApplication
(3)若是事件繼續上報至UIApplication
而且也沒法響應,它將會將事件上報給其Delegate
(4)若是最終事件依舊未被響應則會被系統拋棄
注: 穿透事件,擴大響應區域。
KVC
(key-value-coding
)鍵值編碼,是一種間接訪問實例變量的方法。提供一種機制來間接訪問對象的屬性。
一、給私有變量賦值。
二、給控件的內部屬性賦值(如自定義UITextFiled
的clearButton
,或placeholder
的顏色,通常可利用runtime
獲取控件的內部屬性名,Ivar *ivar = class_getInstanceVariable
獲取實例成員變量)。
[textField setValue:[UIColor redColor] forKeyPath:@"placeholderLabel.textColor"];
複製代碼
三、結合Runtime
,model
和字典的轉換(setValuesForKeysWithDictionary
,class_copyIvarList
獲取指定類的Ivar
成員列表)
KVO
是一種基於KVC
實現的觀察者模式。當指定的被觀察的對象的屬性更改了,KVO
會以自動或手動方式通知觀察者。
監聽 ScrollView
的 contentOffSet
屬性
[scrollview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
複製代碼
RunLoop
基本做用:
(1).使程序一直運行並接受用戶輸入:
程序一啓動就會開一個主線程,主線程一開起來就會跑一個主線程對應的RunLoop
,RunLoop
保證主線程不會被銷燬,也就保證了程序的持續運行。
(2)決定程序在什麼時候應該處理哪些Event
好比:touches
事件,timer
事件,selector
事件
(3)節省CPU
時間 程序運行起來,沒有任何事件源的時候,RunLoop
會告訴CPU
,要進入休眠狀態,這時CPU
就會將其資源釋放出來去作其餘的事情,當有事件源來的時候RunLoop
就會被喚醒處理事件源。
autoreleasepool
以一個隊列數組的形式實現,主要經過下列三個函數完成: (1).objc_autoreleasepoolPush
(2).objc_autoreleasepoolPop
(3).objc_autorelease
經過函數名就能夠知道,對autorelease
分別執行push
,和pop
操做。銷燬對象時執行release
操做。
iOS -- Autorelease & AutoreleasePool
Instruments
裏面工具不少,經常使用的有:
(1).Time Profiler
:性能分析,用來檢測應用CPU
的使用狀況.能夠看到應用程序中各個方法正在消耗CPU
時間。
(2).Zoombies
:檢查是否訪問了殭屍對象,可是這個工具只能從上往下檢查,不智能
(3).Allocations
:用來檢查內存,寫算法的那批人也用這個來檢查
(4).Leaks
:檢查內存,看是否有內存泄漏
(5).Core Animation
:評估圖形性能,這個選項檢查了圖片是否被縮放,以及像素是否對齊。被放縮的圖片會被標記爲黃色,像素不對齊則會標註爲紫色。黃色、紫色越多,性能越差。
是一個Class
類型的指針.
每一個實例對象有個isa
的指針,他指向對象的類,而Class
裏也有個isa
的指針, 指向meteClass
(元類)。元類保存了類方法的列表。當類方法被調用時,先會從自己查找類方法的實現,若是沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass
)也是類,它也是對象。元類也有isa指針,它的isa
指針最終指向的是一個根元類(root meteClass
).根元類的isa
指針指向自己,這樣造成了一個封閉的內循環。
類方法:
(1).類方法是屬於類對象的
(2).類方法只能經過類對象調用
(3).類方法中的self
是類對象
(4).類方法能夠調用其餘的類方法
(5).類方法中不能訪問成員變量
(6).類方法中不定直接調用對象方法
實例方法:
(1).實例方法是屬於實例對象的
(2).實例方法只能經過實例對象調用
(3).實例方法中的self
是實例對象
(4).實例方法中能夠訪問成員變量
(5).實例方法中直接調用實例方法
(6).實例方法中也能夠調用類方法(經過類名)
共同點:
(1).+load
和+initialize
會被自動調用,不能手動調用它們。
(2).子類實現了+load
和+initialize
的話,會隱式調用父類的+load
和+initialize
方法
(3).+load
和+initialize
方法內部使用了鎖,所以它們是線程安全的。
不一樣點:
+load
方法是在runtime
加載類、分類的時候調用(根據函數地址直接調用,默認執行),每一個類,分類的+load
方法都只會調用一次。
先執行父類的+load
方法,再執行子類的+load
方法,而分類的+load
方法會在它的主類的+load
方法以後執行。(分類按照編譯順序前後調用)
+initialize
方法在第一次給某個類發送消息時調用(經過objc_msgSend
調用),而且只會調用一次,是懶加載模式,此時全部的類都已經加載到了內存中,若是這個類一直沒有使用,就不會調用+initialize
方法。
先執行父類的+initialize
方法,再執行子類的+initialize
方法,若是子類沒有實現+initialize
方法時,會調用父類的+initialize
方法,因此父類的+initialize
方法會調用屢次,若是分類實現了+initialize
方法,就會'覆蓋掉'類自己的+initialize
方法。
+load
方法通常是用來交換方法Method Swizzle
,+initialize
方法主要用來對一些不方便在編譯期初始化的對象進行賦值,或者說對一些靜態常量進行初始化操做
_objc_msgForward
是 IMP
類型(函數指針),用於消息轉發的:當向一個對象發送一條消息,但它並無實現的時候,_objc_msgForward
會嘗試作消息轉發。
_objc_msgForward
消息轉發作的幾件事:
(1).調用resolveInstanceMethod:
方法 (或 resolveClassMethod:
)。容許用戶在此時爲該 Class
動態添加實現。若是有實現了,則調用並返回YES
,那麼從新開始objc_msgSend
流程。這一次對象會響應這個選擇器,通常是由於它已經調用過class_addMethod
。若是仍沒實現,繼續下面的動做。
(2).調用forwardingTargetForSelector:
方法,嘗試找到一個能響應該消息的對象。若是獲取到,則直接把消息轉發給它,返回非 nil
對象。不然返回nil
,繼續下面的動做。注意,這裏不要返回self
,不然會造成死循環。
(3).調用methodSignatureForSelector:
方法,嘗試得到一個方法簽名。若是獲取不到,則直接調用doesNotRecognizeSelector
拋出異常。若是能獲取,則返回非nil
:建立一個NSlnvocation
並傳給forwardInvocation:
。
(4).調用forwardInvocation:
方法,將第3步獲取到的方法簽名包裝成 Invocation
傳入,如何處理就在這裏面了,並返回非nil
。
(5).調用doesNotRecognizeSelector:
,默認的實現是拋出異常。若是第3步沒能得到一個方法簽名,執行該步驟。
一旦調用_objc_msgForward
,將跳過查找IMP
的過程,直接觸發「消息轉發」,若是調用了_objc_msgForward
,即便這個對象確實已經實現了這個方法,也會告訴objc_msgSend
沒有找到這個方法的實現。最多見的場景是:你想獲取某方法所對應的NSInvocation
對象。
先來看一下Method
在頭文件中的定義:
typedef struct objc_method *Method;
struct objc_method {
SEL method_name;
char * method_types;
IMP method_imp;
};
複製代碼
Method
被定義爲一個objc_method
指針,在 objc_method
結構體中,包含一個 SEL 和一個IMP
,其中,SEL
是一個指向objc_selector
的指針,其實就是一個保存方法名的字符串。IMP
是一個「函數指針」,就是用來找到函數地址,而後執行函數。因此,Method
創建了SEL
和IMP
的關聯,當對一個對象發送消息時,會經過給出的SEL
去找到IMP
,而後執行。
再加上繼承的狀況,能夠總結出:當向一個對象發送消息時,會去這個類的方法緩存裏尋找(cache methodLists
)若是有緩存則跳到方法實現,不然繼續在這個類的 methodLists
中查找相應的SEL
,若是查不到,則經過super_class
指針找到父類,再去父類的緩存列表和方法列表裏查找,層層遞進,直到找到基類爲止。最後仍然找不到,纔會走消息轉發的流程。
不能向編譯後獲得的類中增長實例變量;能向運行時建立的類中添加實例變量;
由於編譯後的類已經註冊在runtime
中,類結構體中的objc_ivar_list
實例變量的鏈表 和instance_size
實例變量的內存大小已經肯定,同時runtime
會調用 class_setIvarLayout
或class_setWeakIvarLayout
來處理strong weak
引用。因此不能向存在的類中添加實例變量;
運行時建立的類是能夠添加實例變量,調用 class_addIvar
函數。可是得在調用 objc_allocateClassPair
以後,objc_registerClassPair
以前。
AOP
: Aspect Oriented Programming
面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術 AOP
是OOP
的延續,函數式編程的一種衍生範型。(利用 Objective-C Runtime
的黑魔法Method Swizzling
。)
優點:對業務邏輯的各個部分進行隔離,下降業務邏輯各部分之間的耦合度,提升程序的可重用性,提升了開發的效率。
(1).通訊過程當中使用的是未加密的明文,內容會被取抓。
(2).對於服務器或者客戶端來講,不會驗證通訊方的身份,所以有可能遭到中間人的假裝。
(3).沒法驗證所發送報文的完整性和安全性,並且報文的內容也有多是中間人篡改以後發送過來的。
HTTP
協議是基於TCP
鏈接的,是應用層協議,主要解決如何包裝數據。HTTP
鏈接是一種短鏈接,客戶端向服務器發送一次請求,服務器響應後鏈接斷開,節省資源,並且服務器不能主動給客戶端響應。
Socket
是對TCP/IP
協議的封裝,Socket
自己並非協議,而是一個調用接口(API
),經過Socket
,咱們才能使用TCP/IP
協議。Socket
鏈接是一種長鏈接,客戶端跟服務器端直接使用Socket
進行鏈接,沒有規定鏈接後斷開,所以客戶端和服務器段保持鏈接通道,雙方能夠主動發送數據。Socket
默認鏈接超時時間是30秒,默認大小是8K(理解爲一個數據包大小)。
POP
:面向協議編程 面向協議 = 協議 + 擴展 + 繼承 經過協議、擴展作功能劃分,下降模塊間的耦合,加強代碼的可擴展性。iOS
中有一個不足之處就是多重繼承,而協議正好可以解決多重繼承的問題。在Swift中結構體變的更增強大了,不只能定義屬性,還能定義方法,還能多重繼承協議,這是OC
所不提供的。
若是已經使用Alamofire
進行抽象URLSession
,爲何不使用某些方法來抽象URL
,參數等的實質呢,所以,Moya
的基本思想是,咱們須要一些網絡抽象層,以充分封裝實際直接調用Alamofire
的層。
dispatch_once
能保證任務只會被執行一次,即便同時多線程調用也是線程安全的。經常使用於建立單例、swizzeld method
等功能。
@implementation ZHClass
+ (id)sharedInstance {
static ZHClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
複製代碼
信號量方式:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.semaphore = dispatch_semaphore_create(1);
for (NSInteger i = 0; i < 10; i ++) {
[[[NSThread alloc]initWithTarget:self selector:@selector(read) object:nil]start];
[[[NSThread alloc]initWithTarget:self selector:@selector(write) object:nil]start];
}
}
- (void)read{
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%s",__func__);
dispatch_semaphore_signal(self.semaphore);
}
- (void)write{
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%s",__func__);
dispatch_semaphore_signal(self.semaphore);
}
複製代碼
pthread_rwlock_t
方式:
@property (nonatomic,assign) pthread_rwlock_t rwlock;
//初始化讀寫鎖
pthread_rwlock_init(&_rwlock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 3; i ++) {
dispatch_async(queue, ^{
[[[NSThread alloc]initWithTarget:self selector:@selector(readPthreadRWLock) object:nil]start];
[[[NSThread alloc]initWithTarget:self selector:@selector(writePthreadRWLock) object:nil]start];
});
}
//讀
- (void)readPthreadRWLock{
pthread_rwlock_rdlock(&_rwlock);
NSLog(@"讀文件");
sleep(1);
pthread_rwlock_unlock(&_rwlock);
}
// 寫
- (void)writePthreadRWLock{
pthread_rwlock_wrlock(&_rwlock);
NSLog(@" 寫入文件");
sleep(1);
pthread_rwlock_unlock(&_rwlock);
}
//銷燬鎖
- (void)dealloc{
pthread_rwlock_destroy(&_rwlock);
}
複製代碼
dispatch_barrier
方式:
//異步隊列
self.testqueue = dispatch_queue_create("rw.thread", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 5; i ++) {
dispatch_async(queue, ^{
[self readBarryier];
[self readBarryier];
[self readBarryier];
[self writeBarrier];
});
}
- (void)readBarryier{
dispatch_async(self.testqueue, ^{
NSLog(@"讀文件 %@",[NSThread currentThread]);
sleep(2);
});
}
- (void)writeBarrier{
dispatch_barrier_async(self.testqueue, ^{
NSLog(@"寫入文件 %@",[NSThread currentThread]);
sleep(1);
});
}
複製代碼
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操做系統層面的一個錯誤,是進程死鎖的簡稱。
死鎖的產生知足一些特定條件:
(1).互斥條件:進程對於所分配到的資源具備排它性,即一個資源只能被一個進程佔用,直到被該進程釋放。
(2).請求和保持條件:一個進程因請求被佔用資源而發生阻塞時,對已得到的資源保持不放。
(3).不剝奪條件:任何一個資源在沒被該進程釋放以前,任何其餘進程都沒法對他剝奪佔用。
(4).循環等待條件:當發生死鎖時,所等待的進程一定會造成一個環路(相似於死循環),形成永久阻塞。
(1).NSLock
實現了最基本的互斥鎖,遵循了 NSLocking
協議,經過lock
和unlock
來進行鎖定和解鎖。
(2).NSRecursiveLock
遞歸鎖,能夠被一個線程屢次得到,而不會引發死鎖。它記錄了成功得到鎖的次數,每一次成功的得到鎖,必須有一個配套的釋放鎖和其對應,這樣纔不會引發死鎖。只有當全部的鎖被釋放以後,其餘線程才能夠得到鎖
(3).NSCondition
是一種特殊類型的鎖,經過它能夠實現不一樣線程的調度。一個線程被某一個條件所阻塞,直到另外一個線程知足該條件從而發送信號給該線程使得該線程能夠正確的執行。
(4).NSConditionLock
對象所定義的互斥鎖能夠在使得在某個條件下進行鎖定和解鎖。它和 NSCondition
很像,但實現方式是不一樣的。
(5).pthread_mutex
,互斥鎖是一種超級易用的互斥鎖,使用的時候,只須要初始化一個pthread_mutex_t
用pthread_mutex_lock
來鎖定 pthread_mutex_unlock
來解鎖,當使用完成後,記得調用 pthread_mutex_destroy
來銷燬鎖。
(6).pthread_rwlock
,讀寫鎖,在對文件進行操做的時候,寫操做是排他的,一旦有多個線程對同一個文件進行寫操做,後果不可估量,但讀是能夠的,多個線程讀取時沒有問題的。
當讀寫鎖被一個線程以讀模式佔用的時候,寫操做的其餘線程會被阻塞,讀操做的其餘線程還能夠繼續進行。 當讀寫鎖被一個線程以寫模式佔用的時候,寫操做的其餘線程會被阻塞,讀操做的其餘線程也被阻塞。
(7).dispatch_semaphore
,信號量機制實現鎖,等待信號,和發送信號,當有多個線程進行訪問的時候,只要有一個得到了信號,其餘線程的就必須等待該信號釋放。
(8).@synchronized
,一個便捷的建立互斥鎖的方式,它作了其餘互斥鎖所作的全部的事情。
應當針對不一樣的操做使用不一樣的鎖:
當進行文件讀寫的時候,使用pthread_rwlock
較好,文件讀寫一般會消耗大量資源,而使用互斥鎖同時讀文件的時候會阻塞其餘讀文件線程,而 pthread_rwlock
不會。 當性能要求較高時候,可使用pthread_mutex
或者 dispath_semaphore
,因爲OSSpinLock
不能很好的保證線程安全,而在只有在iOS10
中才有 os_unfair_lock
,因此,前兩個是比較好的選擇。既能夠保證速度,又能夠保證線程安全。 對於NSLock
及其子類,速度來講NSLock
< NSCondition
< NSRecursiveLock
< NSConditionLock
。
持續更新中..........