看這篇文章關(guān)于Runtime的話,主要是有OC基礎(chǔ)的同學(xué),共勉吧!
本文我自己的目的是:“ 弄清楚什么是 Runtime薇组,并對Runtime有一定的基本了解,可以在開發(fā)過程中把Runtime運(yùn)用自如.”
什么是 Runtime
- 簡而言之坐儿,Objective-C Runtime是一個將C語言轉(zhuǎn)化為面向?qū)ο笳Z言的擴(kuò)展.
- C++是基于靜態(tài)類型,Objective-C是基于動態(tài)運(yùn)行時類型.
- 通過Runtime把程序轉(zhuǎn)為可令機(jī)器讀懂的機(jī)器語言,Runtime是Objective不可缺少的重要一部分.
本章分成兩個部分進(jìn)行學(xué)習(xí):1. OC元素的認(rèn)知 2. OC消息傳遞
第一部分:OC元素的認(rèn)知:
子類 類 父類 元類 根類 其中指針指向和集成的關(guān)系自己梳理律胀,這里不多說,下面我們講Runtime的構(gòu)成貌矿,最快捷的了解方法就是進(jìn)去ios 9.2/usr/include/objc/objc或runtime隨便看看炭菌。總結(jié)如下:
主要有七個構(gòu)件:1.id和Class 2. SEL 3. Method 4. IMP 5. Ivar 6.Cache 7. Category
一. id和Class
objc_isa_availability
typedef struct
object_class *Class
逛漫、黑低、類
objc_object Class object_class isa OBJC_ISA_AVAILABILITY
、酌毡、isa指針
objc_object *id
-
Class
是一個指向objc_class
結(jié)構(gòu)體的指針克握, -
id
是一個指向objc_object
結(jié)構(gòu)體的指針, -
isa是一個指向
objc_class`結(jié)構(gòu)體的指針枷踏。 -
id
就是我們所說的對象菩暗,Class
就是我們所說的類。
Class super_class OBJC2_UNAVAILABLE
旭蠕、停团、父類
const char *name OBJC2_UNAVAILABLE
、掏熬、類名
long version OBJC2_UNAVAILABLE
佑稠、、類的版本信息孽江,默認(rèn)為0讶坯,可以通過runtime函數(shù)class_setVersion
或者class_getVersion
進(jìn)行修改、讀取
long info OBJC2_UNAVAILABLE
岗屏、辆琅、類信息,供運(yùn)行時期使用的一些位標(biāo)識这刷,如CLS_CLASS (0x1L)
表示該類為普通class
婉烟,其中包含實(shí)例方法和變量;CLS_META (0x2L)
表示該類為metaclass
,其中包含類方法;
long instance_size OBJC2_UNAVAILABLE
暇屋、似袁、該類的實(shí)例變量大小(包括從父類繼承下來的實(shí)例變量)
objec_ivar_list *ivars OBJC2_UNAVAILABLE
咐刨、昙衅、該類的成員變量地址列表
objc_method_list **methodLists OBJC2_UNAVAILABLE
、定鸟、方法地址列表而涉,與info的一些標(biāo)志位有關(guān),如CLS_CLASS (0x1L)
联予,則存儲實(shí)例方法啼县,如CLS_META (0x2L)
,則存儲類方法;
objc_cache_list *cache OBJC2_UNAVAILABLE
沸久、季眷、緩存最近使用的方法地址,用于提升效率卷胯;
objc_protocol_list *protocols OBJC2_UNAVAILABLE
子刮、、存儲該類聲明遵守的協(xié)議的列表
二. SEL
SEL
是selector
在Objective-C中的表示類型窑睁。
-
selector
可以理解為區(qū)別方法的ID话告。
objc_selector *SEL
objc_selector char * name OBJC2_UNAVLABLE
名稱
char *type
name
和types
都是char
類型。
三. IMP
id (* IMP)(id,SEL,...);
IMP是“implementation”的縮寫卵慰,它是由編譯器生成的一個函數(shù)指針沙郭。
當(dāng)你發(fā)起一個消息后(下文介紹),這個函數(shù)指針決定了最終執(zhí)行哪段代碼
四. Method
Method
代表類中的某個方法的類型
objc_method *Method
objc_method SEL method_name OBJC2_UNAVAILABLE
裳朋、病线、方法名
obcj_method char *method_types OBJC2_UNAVAILABLE
、鲤嫡、方法類型
IMP method_imp OBJC2_UNAVAILABLE
送挑、、方法實(shí)現(xiàn)
- 方法名
method_name
類型為SEL暖眼,上文提到過惕耕。 - 方法類型
method_types
是一個char
指針,存儲著方法的參數(shù)類型和返回值類型诫肠。 - 方法實(shí)現(xiàn)
method_imp
的類型為IMP司澎,上文提到過欺缘。
五. Ivar
Ivar
代表類中實(shí)例變量的類型
objc_ivar *Ivar
objc_ivar char *ivar_name OBJC2_UNAVAILABLE
、挤安、變量名
objc_ivar char *ivar_type OBJC2_UNAVAILABLE
谚殊、、變量類型
objc_ivar int ivar_offset OBJC2_UNAVAILABLE
蛤铜、嫩絮、基地址偏移字節(jié)
objc_ivar int space OBJC2_UNAVAILABLE
、围肥、占用空間
objc_propery_t
objc_property_t
是屬性
objc_property *objc_property_t;
-
objc_property
是內(nèi)置的類型剿干,與之關(guān)聯(lián)的還有一個objc_property_attribute_t
,它是屬性的attribute
穆刻, - 也就是其實(shí)是對屬性的詳細(xì)描述置尔,包括屬性名稱、屬性編碼類型蛹批、原子類型/非原子類型等
const char*name
撰洗、、名稱
const char*value
腐芍、差导、值(通常為空的)
------ objc_property_attribute_t
六. Cache
objc_cache *Cache
objc_cache unsigned int mask OBJC2_UNAVAILABLE
、猪勇、
unsigned int occupiedOBJC2_UNAVAILABLE
设褐、、
Method buckets[1]OBJC2_UNAVAILABLE
泣刹、助析、
-
mask:
指定分配cache buckets
的總數(shù)。 - 在方法查找中椅您,Runtime使用這個字段確定數(shù)組的索引位置外冀。
-
occupied:
實(shí)際占用cache buckets
的總數(shù)。 -
buckets:
指定Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組掀泳。 - 這個數(shù)組可能包含不超過
mask+1
個元素雪隧。 - 需要注意的是,指針可能是NULL员舵,
- 表示這個緩存
bucket
沒有被占用脑沿,另外被占用的bucket
可能是不連續(xù)的。 - 這個數(shù)組可能會隨著時間而增長马僻。
-
objc_msgSend
(下文講解)每調(diào)用一次方法后庄拇,就會把該方法緩存到cache列表中 - 下次的時候,就直接優(yōu)先從
cache
列表中尋找韭邓,如果cache
沒有措近,才從methodLists
中查找方法溶弟。
七. Category
就是我們平時所說的類別了,很熟悉吧熄诡。它可以動態(tài)的為已存在的類添加新的方法
objc_catagory *Category
char *category OBJC2_UNAVAILABLE
可很、诗力、類別名稱
char *class_name OBJC2_UNAVAILABLE
凰浮、、類名
objc_method_list *instance_methods OBJC2_UNAVAILABLE
苇本、袜茧、實(shí)例方法列表
objc_method_list *class_methods OBJC2_UNAVAILABLE
、瓣窄、類方法列表
objc_method_list *protocols OBJC2_UNAVAILABLE
笛厦、、協(xié)議列表
這小補(bǔ)充一點(diǎn):objc_AssociationPolicy
關(guān)聯(lián)策略俺夕,有以下幾種策略:
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
// 后面用得到裳凸。。劝贸。姨谷。
第二部分:OC消息傳遞:
一. 基本消息傳遞
1.在面向?qū)ο缶幊讨校瑢ο笳{(diào)用方法叫做發(fā)送消息映九。
2.在編譯時梦湘,程序的源代碼就會從對象發(fā)送消息轉(zhuǎn)換成Runtime的objc_msgSend
函數(shù)調(diào)用。
例如某實(shí)例變量receiver
實(shí)現(xiàn)某一個方法oneMethod
[receiver oneMethod]
Runtime會將其轉(zhuǎn)成類似這樣的代碼:objc_msgSend(receiver, selector);
- Runtime會根據(jù)類型自動轉(zhuǎn)換成下列某一個函數(shù):
-
objc_msgSend:
普通的消息都會通過該函數(shù)發(fā) -
objc_msgSend_stret:
消息中有數(shù)據(jù)結(jié)構(gòu)作為返回值(不是簡單值)時件甥,通過此函數(shù)發(fā)送和接收返回值 -
objc_msgSendSuper:
和objc_msgSend
類似捌议,這里把消息發(fā)送給父類的實(shí)例
5.objc_msgSendSuper_stret:
和objc_msgSend_stret
類似,這里把消息發(fā)送給父類的實(shí)例并接收返回值
二. objc_msgSend
函數(shù)的調(diào)用過程:
-
第一步:檢測這個
selector
是不是要忽略的引有。
第二步:檢測這個target
是不是nil對象瓣颅。nil對象發(fā)送任何一個消息都會被忽略掉。
第三步:- 調(diào)用實(shí)例方法時譬正,它會首先在自身isa指針指向的類
(class)methodLists
中查找該方法宫补,
如果找不到則會通過class的super_class
指針找到父類的類對象結(jié)構(gòu)體,
然后從methodLists
中查找該方法导帝,
如果仍然找不到守谓,則繼續(xù)通過super_class
向上一級父類結(jié)構(gòu)體中查找,
直至根class
您单; - 當(dāng)我們調(diào)用某個某個類方法時斋荞,它會首先通過自己的
isa
指針找到metaclass
,
并從其中methodLists
中查找該類方法虐秦,
如果找不到則會通過metaclass
的super_class
指針找到父類的metaclass
對象結(jié)構(gòu)體平酿,
然后從methodLists
中查找該方法凤优,
如果仍然找不到,則繼續(xù)通過super_class
向上一級父類結(jié)構(gòu)體中查找蜈彼,
直至根metaclass
筑辨;
- 調(diào)用實(shí)例方法時譬正,它會首先在自身isa指針指向的類
第四步:前三部都找不到就會進(jìn)入動態(tài)方法解析(看下文
三. 消息動態(tài)解析
演示:
第一步:
通過resolveInstanceMethod:
方法決定是否動態(tài)添加方法。
如果返回Yes則通過class_addMethod
動態(tài)添加方法幸逆,消息得到處理棍辕,結(jié)束;
如果返回No还绘,則進(jìn)入下一步楚昭;第二步:
這步會進(jìn)入forwardingTargetForSelector:
方法,
用于指定備選對象響應(yīng)這個selector
拍顷,不能指定為self
抚太。
如果返回某個對象則會調(diào)用對象的方法,結(jié)束昔案。
如果返回nil尿贫,則進(jìn)入第三部;第三步:
這步我們要通過methodSignatureForSelector:
方法簽名踏揣,
如果返回nil庆亡,則消息無法處理。
如果返回methodSignature
呼伸,則進(jìn)入下一步身冀;第四步:
這步調(diào)用forwardInvocation:
方法,
我們可以通過anInvocation
對象做很多處理括享,
比如修改實(shí)現(xiàn)方法搂根,修改響應(yīng)對象等,如果方法調(diào)用成功铃辖,則結(jié)束剩愧。
如果失敗,則進(jìn)入doesNotRecognizeSelector
方法娇斩,
若我們沒有實(shí)現(xiàn)這個方法仁卷,那么就會crash
案例:
import <Foundation/Foundation.h>
if TARGET_IPHONE_SIMULATOR
import <objc/objc-runtime.h>
else
import <objc/runtime.h>
import <objc/message.h>
endif
* 自定義函數(shù)
> ````
void sayFunction(id self, SEL _cmd, id some)
{
NSLog(@"%@歲的%@說:%@",
object_getIvar(self, class_getInstanceVariable([self class], "_age")),[self valueForKey:@"name"], some);
}
int main(int argc, const char * argv[]) {
// 添加了一個類
Class People = objc_allocateClassPair([NSObject class], "Person", 0);
// 給這個類添加了兩屬性 name age
class_addIvar(People, "_name", sizeof(NSString*), log2(sizeof(NSString *)), @encode(NSString ));
class_addIvar(People, "_age", sizeof(int), sizeof(int), @encode(int));
// 生成一個方法
SEL s = sel_registerName("say:");
class_addMethod(People, s, (IMP)sayFunction, "good");
// 注冊這個類
objc_registerClassPair(People);
// 對象
id peopleInstance = [[People alloc] init];
// 屬性值
[peopleInstance setValue:@"小萌" forKey:@"name"];
Ivar ageIvar = class_getInstanceVariable(People, "_age");
// 動態(tài)賦值
object_setIvar(peopleInstance, ageIvar, @18);
// 調(diào)用方法
((void ()(id, SEL, id))objc_msgSend)(peopleInstance, s, @"大家好");
// 銷毀對象
peopleInstance = nil;
objc_disposeClassPair(People);
return 0;
}
打印結(jié)果如下:
> 2016-04-08 18:52:15.009 RuntimeLine[4710:394843] 18歲的小明說:大家好
Program ended with exit code: 0
默認(rèn)會出現(xiàn)以下錯誤:
“`objc_msgSend()`報錯Too many arguments to function call ,expected 0,have3”
直接通過`objc_msgSend(self, setter, value)`是報錯,說參數(shù)過多犬第。
請這樣解決:
* Build Setting–> Apple LLVM 7.0 – Preprocessing–> Enable Strict Checking of objc_msgSend Calls 改為 NO
也可以這樣寫(推薦):
`((void(*)(id,SEL,id))objc_msgSend)(peopleInstance,s,@"大家好");`
強(qiáng)制轉(zhuǎn)換`objc_msgSend`函數(shù)類型為帶三個參數(shù)且返回值為void函數(shù)锦积,然后才能傳三個參數(shù)。
> ###案例小結(jié):
看看我們都干了什么歉嗓?丰介!What have we done?!O薄带膀!
此實(shí)戰(zhàn)內(nèi)容是,動態(tài)創(chuàng)建一個類橙垢,并創(chuàng)建成員變量和方法垛叨,最后賦值成員變量并發(fā)送消息。其中成員變量的賦值使用了KVC和object_setIvar函數(shù)兩種方式柜某,這些東西大家舉一反三就可以了
回顧一下: 1. OC元素的認(rèn)知 內(nèi)容 2. OC消息傳遞 機(jī)制 方法 案例演示
下一篇繼續(xù) [Runtime梳理(二)](http://www.reibang.com/p/6d685aeb0c4f)