runTime 和 runLoop
runTime的詳解:
1.什么是runtime供常?
runtime即運行時鞋吉,它是一套比較底層的純C語言API,屬于一個C語言庫凰浮,包含了許多C語言的API,它是OC的幕后工作者官疲,我們平時寫的OC代碼在運行過程中都會轉(zhuǎn)為runtime的C語言代碼仅醇。
2.runtime的作用艺晴?(代碼揭秘runtime的作用V缱辍!7饽)
作用一:獲取一個類的全部成員變量就算是私有成員變量也可以獲取的到
創(chuàng)建一個person類然评,".h"代碼如下:
這些成員變量都是公有的,我們直接點開頭文件就可以看到,如果是系統(tǒng)的類呢,你是不是就不知道類里面有哪些成員了,利用runtime可以輕松解決這個問題,現(xiàn)在我們來測試一下,記得要加 #import 。
打印結(jié)果如下:
這樣是不是所有的成員變量就一目了然了呢狈究,把成員變量放到“.m”中的打印出來的結(jié)果是一樣的碗淌。
作用二:同理,我們可以獲取到一個類的全部屬性名
打印結(jié)果如下:
作用三:獲取一個類的全部方法
打印結(jié)果:
這就是當(dāng)前類的所有方法。
作用四:獲取一個類中遵守的全部協(xié)議
首先把遵循的協(xié)議給注釋掉
測試代碼如下:
結(jié)果什么也沒有打印出來:
我們把注釋的代碼打開
在執(zhí)行測試代碼打印結(jié)果如下:
作用五:歸檔/解檔
這樣是不是要比平時寫歸檔/解檔更方便呢亿眠。
下面我們進(jìn)入更深層次的了解碎罚,也是我們開發(fā)經(jīng)常遇到的:
作用六:交換兩個方法的實現(xiàn),攔截系統(tǒng)自帶的方法調(diào)用功能
獲得某個類的類方法:
Method class_getClassMethod(Class cls , SEL name)
獲取某個類的實例對象方法:
Method class_getInstanceMethod(Class cls , SEL name)
交換兩個方法的實現(xiàn):
void method_exchangeImplementations(Method m1 , Method m2)
案例1:通過runtime實現(xiàn)方法交換纳像,類方法用class_getClassMethod
荆烈,對象方法用class_getInstanceMethod
// 獲取兩個類的類方法
Method m1 = class_getClassMethod([Person class], @selector(run));
Method m2 = class_getClassMethod([Person class], @selector(study));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
// 交換后,先打印學(xué)習(xí)竟趾,再打印跑憔购!
[Person run];
[Person study];```
案例2:攔截系統(tǒng)方法
需求:比如iOS6 升級 iOS7 后需要版本適配,根據(jù)不同系統(tǒng)使用不同樣式圖片(擬物化和扁平化)岔帽,如何通過不去手動一個個修改每個UIImage的imageNamed:方法就可以實現(xiàn)為該方法中加入版本判斷語句玫鸟?
步驟:
1、為UIImage建一個分類(UIImage+Category)
2犀勒、在分類中實現(xiàn)一個自定義方法屎飘,方法中寫要在系統(tǒng)方法中加入的語句,比如版本判斷
- (UIImage *)qq_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系統(tǒng)版本是7.0以上贾费,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage qq_imageNamed:name];```
3钦购、分類中重寫UIImage的load方法,實現(xiàn)方法的交換(只要能讓其執(zhí)行一次方法交換語句铸本,load再合適不過了)
+ (void)load {
// 獲取兩個類的類方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
}```
######注意:自定義方法中最后一定要調(diào)用一下系統(tǒng)的方法耗啦,讓其有加載圖片的功能剿吻,但是由于方法交換,系統(tǒng)的方法名已經(jīng)變成了我們自定義的方法名絮吵,這就實現(xiàn)了系統(tǒng)方法的攔截陌宿!
######作用7:在分類中設(shè)置屬性锡足,給任意一個對象設(shè)置屬性
眾所周知,分類中是無法設(shè)置屬性的壳坪,如果在分類的聲明中寫@property 只能為其生成get 和 set 方法的聲明舶得,但無法生成成員變量,就是雖然點語法能調(diào)用出來爽蝴,但程序執(zhí)行后會crash沐批,有人會想到使用全局變量呢?
但是全局變量程序整個執(zhí)行過程中內(nèi)存中只有一份蝎亚,我們創(chuàng)建多個對象修改其屬性值都會修改同一個變量九孩,這樣就無法保證像屬性一樣每個對象都擁有其自己的屬性值。這時我們就需要借助runtime為分類增加屬性的功能了发框。
**需要用到的方法:**
set方法躺彬,將值value 跟對象object 關(guān)聯(lián)起來(將值value 存儲到對象object 中)參數(shù) object:給哪個對象設(shè)置屬性參數(shù) key:一個屬性對應(yīng)一個Key,將來可以通過key取出這個存儲的值,key 可以是任何類型:double宪拥、int 等仿野,建議用char 可以節(jié)省字節(jié)參數(shù) value:給屬性設(shè)置的值參數(shù)policy:存儲策略 (assign 、copy 她君、 retain就是strong)
```void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)```
利用參數(shù)key 將對象object中存儲的對應(yīng)值取出來
```id objc_getAssociatedObject(id object , const void *key)```
實現(xiàn)過程:
1脚作、創(chuàng)建一個分類,比如給任何一個對象都添加一個name屬性缔刹,就是NSObject添加分類(NSObject+Category)2球涛、先在.h 中@property 聲明出get 和 set 方法,方便點語法調(diào)用
```@property(nonatomic,copy)NSString *name;```
3桨螺、在.m 中重寫set 和 get 方法宾符,內(nèi)部利用runtime 給屬性賦值和取值
```char nameKey;
- (void)setName:(NSString *)name {
// 將某個值跟某個對象關(guān)聯(lián)起來,將某個值存儲到某個對象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
###**runLoop:**基本作用:
1.保持程序的持續(xù)運行
2.處理App中的各類事件(觸摸事件灭翔、定時器事件魏烫、Selector事件)
3.節(jié)省CPU資源,提高程序性能:沒有事件時就進(jìn)行睡眠狀態(tài)
###runLoop的內(nèi)部實現(xiàn):
do-while循環(huán)肝箱,在這個循環(huán)內(nèi)部不斷地處理各種任務(wù)(Source\Timeer\Observer)
**注意點:**
1.一個線程對應(yīng)一個RunLoop(采用字典存儲哄褒,線程號為key,RunLoop 為value)
2.主線程的RunLoop默認(rèn)已經(jīng)啟動煌张,子線程的RunLoop需要手動啟動
3.RunLoop只能選擇一個Mode啟動呐赡,如果當(dāng)前Mode沒有任何Source、 Timer骏融、Observer链嘀,那么就不會進(jìn)入RunLoop
4.RunLoop的主要函數(shù)調(diào)用順序為:CFRunLoopRun->CFRunLoopRunSpecific->__CFRunLoopRun