runtime詳解
1资锰,runtime的核心—消息傳遞
在[object foo]在運(yùn)行的過(guò)程中會(huì)給object發(fā)送foo消息,但是這個(gè)消息可能會(huì)被轉(zhuǎn)發(fā)到另一個(gè)對(duì)象,也可能
并不會(huì)立即執(zhí)行foo這個(gè)發(fā)放的代碼。它在運(yùn)行時(shí)給object發(fā)送一個(gè)叫foo的消息泽铛,這個(gè)消息也許會(huì)由object來(lái)處理。也許會(huì)轉(zhuǎn)發(fā)給另一個(gè)對(duì)象辑鲤,或者不接收這個(gè)消息有另一個(gè)方法來(lái)實(shí)現(xiàn)這個(gè)消息
在編譯時(shí)盔腔,Object-C函數(shù)調(diào)用的語(yǔ)法會(huì)被編譯成一個(gè)C的函數(shù)調(diào)用 - objc_mesSend(),- objc_mesSend(object, @selector(), arg)
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
**struct objc_method_list **methodLists**;
**struct objc_cache *cache**;
struct objc_protocol_list *protocols;
#endif
};
struct objc_method_list {
struct objc_method_list *obsolete;
int method_count;
#ifdef __LP64__
int space;
#endif
/* variable length structure */
struct objc_method method_list[1];
};
struct objc_method {
SEL method_name;
char *method_types; /* a string representing argument/return types */
IMP method_imp;
};
從API中可以看出oc的對(duì)象是一個(gè)指向objc_object(對(duì)象)結(jié)構(gòu)體的指針月褥。Class是一個(gè)指向objc_class(類)結(jié)構(gòu)體的指針弛随。每個(gè)結(jié)構(gòu)體中都包含一個(gè)isa指針,isa指針指向的類結(jié)構(gòu)宁赤。
objc_mesSend(object, @selector(), arg) 消息傳遞的過(guò)程是:先通過(guò)object的isa指針找到class,在class的object_cache找到改函數(shù)(object_cache是以method_name作為key,method_imp為value存儲(chǔ)的能夠直接查找)舀透,如果object_cache找不到會(huì)從class的methodList找改函數(shù)(從API中可以看出methodList其實(shí)是一個(gè)數(shù)組,查找效率太低)决左,再找不到會(huì)從他的superclass中找愕够,如果再找不到,如果我們不做特殊處理程序就會(huì)掛掉
2佛猛,消息傳遞補(bǔ)救:
如果消息未被實(shí)現(xiàn)時(shí)链烈,runtime調(diào)用resolveInstanceMethod實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
//添加某個(gè)方法的實(shí)現(xiàn)
if(aSEL == name){
class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
}
return YES;
}
resolveInstanceMethod:方法返回YES時(shí)系統(tǒng)會(huì)從新發(fā)送一次消息,在返回YES之前添加沒有實(shí)現(xiàn)的方法
如果resolveInstanceMethod沒有特殊處理系統(tǒng)會(huì)調(diào)用forwardingTargetForSelector方法把消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == SEL){
return newObject;
}
return [super forwardingTargetForSelector:aSelector];
}
當(dāng)forwardingTargetForSelector返回nil,系統(tǒng)會(huì)發(fā)送methodSignatureForSelector消息獲得函數(shù)的返回值類型挚躯。如果 -methodSignatureForSelector: 返回 nil ,系統(tǒng)會(huì)發(fā)出doesNotRecognizeSelector消息擦秽,這時(shí)就無(wú)力會(huì)天了码荔。如果返回了一個(gè)函數(shù)簽名,Runtime 就會(huì)創(chuàng)建一個(gè) NSInvocation 對(duì)象并發(fā)送 -forwardInvocation: 消息給目標(biāo)對(duì)象感挥。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
}
runtime使用的幾種場(chǎng)景
1缩搅,添加category屬性(關(guān)聯(lián)對(duì)象)
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id object, const void *key)
程序會(huì)在執(zhí)行某個(gè)類的load,initialize方法時(shí)候執(zhí)行分類屬性
category不能夠直接添加屬性是因?yàn)閛c的對(duì)象objc_class結(jié)構(gòu)體大小是固定的触幼,不可能往這個(gè)結(jié)構(gòu)體中添加數(shù)據(jù)硼瓣,只能修改
category能夠直接添加方法是因?yàn)閛c的對(duì)象的objc_object結(jié)構(gòu)體里面的methodList是一個(gè)二維數(shù)組,所以可以修改*methodLists的值來(lái)增加成員方法置谦,雖沒辦法擴(kuò)展methodLists指向的內(nèi)存區(qū)域堂鲤,卻可以改變這個(gè)內(nèi)存區(qū)域的值
2,動(dòng)態(tài)得到屬性和方法
(1)獲取property屬性:
unsigned int outCount;
objc_property_t *objc_property_t = class_copyPropertyList([self class], &outCount)
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
NSString * propertyName = [[NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
}
(2)獲取所有屬性
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * name = [NSString stringWithUTF8String:ivar_getName(ivar)];
}
(3)得到方法列表class_copyMethodList
3媒峡,方法替換(method swizzling)
//newFoo將要替換的函數(shù)瘟栖,oldFoo被替換的函數(shù)
//獲取方法的實(shí)現(xiàn)
IMP newImp = class_getMethodImplementation([self class], @selector(newFoo));
//動(dòng)態(tài)添加方法
class_addMethod(Class, @selector(newFoo), newImp, types);
//動(dòng)態(tài)替換方法實(shí)現(xiàn)
class_replaceMethod(peopleClass, @selector(oldFoo), newImp, types);
method swizzling需要寫在load方法里面(load調(diào)用時(shí)機(jī)是被在到runtime時(shí),只調(diào)用一次谅阿,可以看看runtime源碼)