在OC中方法的調(diào)用被稱為消息的發(fā)送.例如: [obj test] 這段代碼會裝換為objc_msgSend(obj, test)這段運(yùn)行時代碼;
而關(guān)于objc_msgSend(obj, test)這段代碼的掉用過程如下:
步驟一:
當(dāng)調(diào)用對象方法時,首先回去當(dāng)前對象的isa指針指向的類method_list中找方法的實(shí)現(xiàn),如果沒有找到,則會去父類的method_list中尋找,沒有則繼續(xù)往父類尋找,直至到root class為止坑资。
當(dāng)調(diào)用類方法時,首先會去當(dāng)前類的isa指針?biāo)赶虻膍eta class的method_list中找方法的實(shí)現(xiàn),如果沒有找到,則會去父類的meta class的method_list中尋找,沒有則繼續(xù)往父類的meta class尋找,直至root meta class.
步驟二:
如果在步驟一中沒有找到方法的實(shí)現(xiàn),則會進(jìn)入方法的動態(tài)解析過程.
方法動態(tài)解析
方法的動態(tài)解析使程序有三次機(jī)會避免出現(xiàn)找不到方法實(shí)現(xiàn)的crash機(jī)會朋譬。
過程如圖(圖片來源網(wǎng)絡(luò)):
第一步(動態(tài)添加方法的實(shí)現(xiàn))
通過resolveInstanceMethod:方法,(類方法用resolveClassMethod:),返回Yes,可以通過class_addMethod動態(tài)添加方法實(shí)現(xiàn).如果返回No,則進(jìn)入下一步.而如果返回Yes,但沒有添加方法的實(shí)現(xiàn),則程序就會在這一步直接crash掉了徙赢。
例子
@interface Person : NSObject
- (void)run;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if ([NSStringFromSelector(sel) isEqualToString:@"run"]) {
class_addMethod(self, sel, addRun, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void addRun() {
NSLog(@"addRun");
}
@end
最終程序正常運(yùn)行,打印結(jié)果為addRun.本例中person類只有run這個實(shí)例方法的聲明,而沒有實(shí)現(xiàn),當(dāng)外部直接調(diào)用這個方法時,會因為找不到run方法的實(shí)現(xiàn)而crash.而通過實(shí)現(xiàn) resolveInstanceMethod方法返回YES,直接動態(tài)添加方法的實(shí)現(xiàn)addRun,使得程序在調(diào)用run方法時調(diào)用到addRun這個實(shí)現(xiàn).
第二步 (將消息轉(zhuǎn)發(fā)給其它對象處理)
到這一步,可以通過forwardingTargetForSelector方法講這個消息轉(zhuǎn)發(fā)給其它對象處理,當(dāng)返回nil時,將進(jìn)入第三步;當(dāng)返回self,則會直接因為找不到方法實(shí)現(xiàn)而直接crash;當(dāng)返回其它對象時,則將這個方法轉(zhuǎn)發(fā)給其它類去實(shí)現(xiàn).
例子
@interface Person : NSObject
- (void)walk;
@end
#import "Person.h"
#import <objc/runtime.h>
#import "Animal.h"
@implementation Person
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"walk"]) {
return [[Animal alloc]init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
#import "Animal.h"
@implementation Animal
- (void)walk {
NSLog(@"animal walk");
}
@end
程序正常運(yùn)行,打印animal walk.在本例中, Person類聲明了一個實(shí)例方法而沒有實(shí)現(xiàn),并且沒有動態(tài)添加方法的實(shí)現(xiàn),而是將walk方法轉(zhuǎn)發(fā)給Animal這個類實(shí)現(xiàn),所以最后調(diào)用的是animal的walk方法的實(shí)現(xiàn)窑业。
第三步 (修改方法的實(shí)現(xiàn)和方法的響應(yīng)對象)
到了最后一步,可以通過methodSignatureForSelector,返回一個NSMethodSignature,來修改方法的實(shí)現(xiàn)和方法響應(yīng)對象.如果返回nil,則直接crash掉了枕屉。
例子
@interface Person : NSObject
- (void)jump;
@end
#import "Person.h"
#import <objc/runtime.h>
#import "Animal.h"
@implementation Person
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"jump"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation setSelector:@selector(changeJump)];
[anInvocation invokeWithTarget:self];
}
- (void)changeJump {
NSLog(@"changeJump");
}
@end
程序正常運(yùn)行,并打印changeJump,本例中Person類的jump方法只有聲明而沒有方法的實(shí)現(xiàn),并且沒有動態(tài)添加方法的實(shí)現(xiàn)和轉(zhuǎn)發(fā)給其它對象,而是修改了方法的實(shí)現(xiàn),將jump方法的實(shí)現(xiàn)改為了changeJump方法的實(shí)現(xiàn).