以下筆記是邊看視頻課程邊記錄下來的,供以后自己查閱復習使用
Runtime學習筆記1
消息轉(zhuǎn)發(fā)
在OC中,調(diào)用方法其實是給對象發(fā)送消息
[[Person new] sendMessage:@"gelo"];
// 等價于
objc_msgSend([Person new], @selector(sendMessage:), "gelo");
通過對象的isa指針找到class,如果有方法的話,直接調(diào)用。沒有找到的話,通過繼承樹查找,進入消息轉(zhuǎn)發(fā)機制

動態(tài)方法解析,動態(tài)添加方法實現(xiàn)
? resolveInstanceMethod
快速轉(zhuǎn)發(fā),當前的類或者繼承樹沒有該方法的實現(xiàn),在更加廣的范圍尋找
? forwardingTargetForSelector
慢速轉(zhuǎn)發(fā)
? methodSignatureForSelector
? forwardInvocation
doesNotRecognizeSelector:
方法交換 Method Swizzling
用自己寫的方法替換系統(tǒng)方法,通過class_getInstanceMethod方法獲取,通過method_exchangeImplementations交換兩個函數(shù)
字典轉(zhuǎn)模型
遍歷字典獲取key和value
key作為屬性名,value作為屬性值
通過objc_msgSend發(fā)送set方法
模型轉(zhuǎn)字典
字典的key通過模型的屬性列表獲取
字典的value通過調(diào)用get方法獲取
實現(xiàn)KVO
KVO是基于runtime的
A監(jiān)聽B 系統(tǒng)會為B創(chuàng)建一個子類
B的isa指針指向B的子類
在子類中重新set方法
KVO底層實現(xiàn)
KVO的基礎使用
觀察某一個對象的某個屬性
options參數(shù)可以觀察一下幾個值
new 返回變化后的新值
old 返回變化前的舊值
init 注冊的時候就會發(fā)一次通知,改變后的值的時候也會發(fā)送
prior 新值和舊值都會返回
KVO默認是自動模式,每次修改值都會發(fā)送通知
手動發(fā)送通知的時候,對象調(diào)用willchangeValueForKey,改變之后調(diào)用didchangeValueForKey
監(jiān)聽屬性下面的屬性值,只需要在監(jiān)聽path中通過點監(jiān)聽:"dog.age"(屬性依賴)
KVO監(jiān)聽的是set方法。比如監(jiān)聽不到數(shù)組的add方法
要是需要監(jiān)聽容器方法,需要結(jié)合KVC
需要監(jiān)聽對象下多個屬性,只需要監(jiān)聽對象本身,并實現(xiàn)keyPat***ForKey。返回需要的真正監(jiān)聽的內(nèi)部屬性的NSSet
內(nèi)部實現(xiàn)
屬性是對成員變量和set、get方法的封裝
KVO觀察的是set方法(設置成員變量之后,外部通過person->name方法修改,KVO監(jiān)聽不到)通過runtime創(chuàng)建一個觀察者的子類(NSKVONotifying_Person)重寫set方法。修改指針到子類、在重寫的方法里面調(diào)用willchangeValueForKey、superSetName、didchangeValueForKey。
在創(chuàng)建的子類中,沒有父類的set方法!需要重寫set方法
OC的方法中包含SEL(方法編號)、IMP(方法實現(xiàn))一一對應。調(diào)用方法的時候發(fā)送的是SEL
OC的方法調(diào)用里面有兩個默認參數(shù):id self,SEL _cmd。由于sendMsg傳遞了該參數(shù)(調(diào)用者以及SEL)
監(jiān)聽容器類(NSArray、NSDic)
通過KVO觀察容器屬性的變化,利用KVC
通過KVC的mutableArray**ForKey返回一個容器對象,向該對象添加元素可以實現(xiàn)KVO。內(nèi)部新建子類、重寫add方法。
KVO返回的NSDic中,kind類型
觀察set方法 返回1
觀察插入方法 返回2
觀察刪除方法 返回3
觀察替換方法 返回4
數(shù)組中count
使用KVO中監(jiān)聽不到數(shù)組中的count、使用KVC同樣取不到[array valueForKey:@"count"]
count是集合運算符,KVC需要用@"@count"取值。
count是只讀屬性
數(shù)組(NSMutableArray)
關于數(shù)組的容量,容量不夠用的時候,會成倍的增加
對象本身是指針,指向該對象的結(jié)構體
x/100xb arr 打印arr 100個內(nèi)存地址
找到count的內(nèi)存地址,修改內(nèi)存地址的值,進而可以修改count的值
Runtime學習筆記2
消息機制
OC代碼會轉(zhuǎn)化為C語言執(zhí)行,使用runtime的時候,需要關閉代碼的嚴格檢測
調(diào)用函數(shù)的方法:
[p eat]
[p performSelector: SEL]
objc_msgSend()
使用runtime創(chuàng)建對象:
類名.class即為對象 Person.class == objc_getClass("Person")
// 在目錄下執(zhí)行
clang --rewrite-objc main.m
// 手動編譯OC代碼生成cpp文件
歸檔/解檔
歸檔和解檔對象,需要遵循NSCoding的協(xié)議,并且實現(xiàn)協(xié)議方法
KVC可以使用id類型為屬性賦值
Ivar:成員變量
Method:成員方法
C語言 基本數(shù)據(jù)類型的指針 函數(shù)內(nèi)部是為了修改外部的值
class_copyIvarList獲取所有屬性數(shù)量
關鍵字copy、new、creat代表著會在堆區(qū)域(malloc)開辟空間
方法執(zhí)行完畢—>方法調(diào)用棧平衡—>內(nèi)部變量指針出?!?gt;但是指針指向的堆區(qū)的值還在—>內(nèi)存泄漏
在OC中使用C的代碼,要手動釋放指針,防止內(nèi)存溢出
OC方法定位以及替換
OC的方法表:返回值類型+參數(shù)類型一樣 編號就一樣
? 類
SEL 編號 ————— IMP實現(xiàn)(地址指針)
SEL 編號 ————— IMP實現(xiàn)(地址指針)
SEL 編號 ————— IMP實現(xiàn)(地址指針)
用HOOK!鉤住系統(tǒng)方法,在調(diào)用之前修改方法的調(diào)用
在分類中的load方法(由于預加載,比main更早執(zhí)行)中交換IMP
#import <objc/runtime.h>
+ (void)load {
// 獲取替換后的類方法
Method otherMethod = class_getClassMethod([UIImage class], @selector(imageNameNextWith:));
// 獲取替換前的類方法
Method method = class_getClassMethod([UIImage class], @selector(imageNamed:));
// 然后交換類方法
method_exchangeImplementations(otherMethod, method);
}
+ (UIImage *)imageNameNextWith:(NSString *)nameString {
UIImage *image = nil;
image = [UIImage imageNameNextWith:[nameString stringByAppendingString:@"tupian.jpg"]];
return image;
}
關于在imageNameNextWith中調(diào)用自身,并不會引起循環(huán)引用。
交換之前
SEL(系統(tǒng)方法:ImageNamed) —————> IMP(系統(tǒng)方法:ImageNamed的實現(xiàn)地址指針)
SEL(自己的方法:ImageNamedNextWith) —————> IMP(自己的方法:ImageNamedNextWith的實現(xiàn)地址指針)
交換之后
SEL(系統(tǒng)方法:ImageNamed) —————> IMP(自己的方法:ImageNamedNextWith的實現(xiàn)地址指針)
SEL(自己的方法:ImageNamedNextWith) —————> IMP(系統(tǒng)方法:ImageNamed的實現(xiàn)地址指針)
交換之后,每當再次調(diào)用imageNameNextWith方法的時候,實際上執(zhí)行的是系統(tǒng)方法ImageNamed指向的方法實現(xiàn),所以不會引起循環(huán)調(diào)用。
OC對象本質(zhì)上是指針占用8個字節(jié)
OC方法調(diào)用順序:消息發(fā)送——>SEL——>IMP——>代碼——>函數(shù)——>匯編
函數(shù)響應式編程RAC
RAC的代理
RAC里面內(nèi)部實現(xiàn)類似于通知
- 創(chuàng)建信號 提供外界訂閱
創(chuàng)建了一個容量為1的可變數(shù)組_subscribers
支持多個訂閱者訂閱該信號
RACSubject *subject = [RACSubject subject];
- 訂閱信號(注冊通知)
創(chuàng)建訂閱者對象
將Block放到訂閱者對象中
將訂閱者對象放入_subscribers里面
[subject subscribeNext:^(id){
// 函數(shù)式編程,免除了遵循協(xié)議,引用方法的步驟
}];
- 發(fā)送信號(發(fā)起通知)
遍歷_subscribers取出中的訂閱者對象
執(zhí)行訂閱者對象中的Block
[subject sentNext:@"hahha];
RAC中可以使用Selector通過方法名稱創(chuàng)建信號,直接訂閱
RAC中的KVO
RAC可以直接用Block回調(diào),在觀察多個屬性的時候,可以避免在回調(diào)函數(shù)中判斷。
RAC同樣不能觀察到數(shù)組的count
RAC監(jiān)聽事件
將按鈕的點擊事件包裝成信號,訂閱
RAC中的Timer
使用NSTimer的時候,創(chuàng)建完之后需要添加到NSRunLoop中
在NSTread子線程中需要手動啟動NSRunLoop
[[NSRunLoop currentRunLoop] run]
RAC中通過信號創(chuàng)建子線程并發(fā)Timer,底層使用GCD創(chuàng)建
RAC中的宏定義
當輸入框內(nèi)容發(fā)生變化,相應更新到_label上
RAC(_label, text) = _textField.rac_textSignal;
只要對象的屬性發(fā)生改變,就會產(chǎn)生信號
RACObserver(self, name) sub....
關于Block中的循環(huán)引用,但是在特殊情況下是允許循環(huán)應用的出現(xiàn)
NSURLSession中的delegate是強引用,目的是發(fā)送請求的時候只需要一個對象。是單例
使用強引用,并不會銷毀,導致內(nèi)存泄漏
Socket探索
IP地址可以在網(wǎng)絡上定位到一臺終端設備
端口號可以訪問到設備上的服務:比如80端口為Apache端口服務
網(wǎng)絡的七層協(xié)議從上至下:應用層、表示層、會話層、傳輸層、網(wǎng)絡層、數(shù)據(jù)鏈路層、物理層
標準幀格式數(shù)據(jù)包
socket處于傳輸層、IP/TCP協(xié)議在網(wǎng)絡層
TCP與UDP協(xié)議的區(qū)別
UDP(用戶數(shù)據(jù)報協(xié)議)短信
? 只管發(fā)送,不確認對方是否收到
? 將數(shù)據(jù)及源和目的封裝成數(shù)據(jù)包中,不需要建立連接
? 每個數(shù)據(jù)報大小限制在64k
? 因為無需連接,因此是不可靠協(xié)議
? 不需要連接,速度快
TCP(傳輸控制協(xié)議)電話
? 建立連接,形成傳輸數(shù)據(jù)通道
? 在連接中進行大數(shù)據(jù)傳輸(數(shù)據(jù)大小不受限制)
? 通過三次握手連接,是可靠的協(xié)議,安全送達
? 必須建立連接,效率稍低
直播推流、游戲是UDP協(xié)議,下載的過程是TCP協(xié)議
Socket
類比插座,socket需要兩端的IP+端口號建立連接
- 創(chuàng)建socket
/*
domain: 協(xié)議域 AF_INET = IPV4 IPV6
type: Socket類型 SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)
protocol: IPPROTO_TCP, 傳入0, 會自動根據(jù)第二個值選擇合適的協(xié)議
*/
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
- 連接服務器
/*
客戶端socket
服務器IP地址結(jié)構體指針
結(jié)構體長度
*/
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;//IPV4
serverAddr.sin_port = htons(80);//端口號
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//IP地址
int connectResult = connect();
// 返回0代表連接成功
netcat工具,用于監(jiān)聽本地端口
- 發(fā)送數(shù)據(jù)
/*
客戶端socket
發(fā)送內(nèi)容地址
發(fā)送內(nèi)容長度
發(fā)送方式標志,一般為0
返回值
發(fā)送成功之后返回發(fā)送字節(jié)長度,失敗返回error
*/
send();
- 讀取數(shù)據(jù)
/*
客戶端socket
接受內(nèi)容緩沖區(qū)域地址
接受內(nèi)容緩沖區(qū)長度
接受方式標志,0表示阻塞,必須等待服務器返回數(shù)據(jù)
返回值
接受成功之后返回發(fā)送字節(jié)長度,失敗返回error
*/
uint8_t buffer[1024];
recv();
通過data數(shù)據(jù)讀取二進制數(shù)組,解析返回字節(jié)流
- 關閉socket
close();
HTTP訪問
通過向百度的IP地址,發(fā)送"GET HTTP/1.1\n Host: www.baidu.com\n\n"可以接收到百度服務器返回的百度首頁數(shù)據(jù)。
手寫了一個HTTP協(xié)議
Connection被廢棄的原因
異步下載,無法回調(diào)數(shù)據(jù),因為Runloop默認在子線程不開啟
線程管理
線程中有任務才有可能不被釋放
HTTPS協(xié)議
https本身不會對客戶端進行驗證
加密算法:RSA
RSA:公鑰和私鑰
明文+公鑰 = 密文
密文+私鑰 = 明文
第一次請求HTTPS服務器,客戶端安裝證書(下載公鑰并保存),通訊時通過該公鑰加密傳輸
登錄校驗時隨機鹽可以提高安全性
加密詳解
Base64
base64是編碼方式,不屬于加密算法
可以將任意的二進制數(shù)據(jù)進行編碼 編碼成為65中字符的文本文件
0-9,a-z,A-Z,+ / =
對稱加密:
DES
3DES
AES(高級密碼標準)
數(shù)學算法:
哈希函數(shù)MD5
內(nèi)存釋放
free()以及CFRelease()的區(qū)別
單元測試
setup 初始化
tearDown 銷毀
所有的測試用例必須以test開頭
given
when
then
架構模式
MVC解耦
vc代碼過于沉重
代碼耦合性過高 UI與Model的通訊
MVP
面向協(xié)議編程---代理
@synchronized(self)多線程鎖
通過代理使UI與Model通訊
MVVM
雙向綁定:數(shù)據(jù)和UI的綁定,即修改一處另一處隨之修改
異步的一般處理:代理、通知、匿名函數(shù)(Block)
從數(shù)據(jù) -----> UI 通過Block進行通訊
從UI -------> 數(shù)據(jù) 通過KVO監(jiān)聽通訊
NSMutableArray是線程不安全的,在處理里面的數(shù)據(jù)的時候需要加鎖
多線程
進程
線程
NSThread
- alloc init
+ detacNewThread
- self perform

線程的名稱
線程的優(yōu)先級
多線程的共享資源
線程不安全:獲取的數(shù)據(jù)和預期可能不一樣
互斥鎖:線程同步,@synchronized 當一個線程在操作數(shù)據(jù)的時候,其它線程不得操作該數(shù)據(jù)
原子屬性 atomic
原子屬性是線程安全的,自旋鎖
原子屬性的成員變量,在set方法中會添加@synchronized保護線程安全