1、Class/SEL/Method/IMP
Class+SEL=>Method=>IMP,...
2、class_
class_addMethod/class_replaceMethod/method_exchangeImplementations
class_addMethod//如果要添加的A方法已經(jīng)存在則返回NO
A->B,B->B
method_exchangeImplementations
A->B,B->A
class_replaceMethod
A->B,B->B
3、消息處理機(jī)制
objc_msgSend/NSInvocation+NSMethodSignature/perfermSelector
1辩稽、更低層c語(yǔ)言實(shí)現(xiàn)
id objc_msgSend(id self, SEL op, ...)
2厢蒜、oc的封裝高級(jí)層
NSMethodSignature=返回值類型+參數(shù)(列)類型
SEL=函數(shù)名字
NSInvocation=target+SEL+NSMethodSignature
getArgumentTypeAtIndex:第0個(gè)參數(shù)是target,第1個(gè)參數(shù)是SEL油额,其它的參數(shù)(列)從第2,3刻帚,4潦嘶,下標(biāo)開始。
3崇众、消息處理機(jī)制總結(jié):
那么 objc_msgSend 到底是怎么工作的呢掂僵?
在Objective-C中,消息直到運(yùn)行時(shí)才會(huì)綁定到方法的實(shí)現(xiàn)上顷歌。編譯器會(huì)把代碼中[target doSth]轉(zhuǎn)換成 objc_msgSend消息函數(shù)看峻,這個(gè)函數(shù)完成了動(dòng)態(tài)綁定的所有事情。它的運(yùn)行流程如下:
檢查selector是否需要忽略衙吩。(ps: Mac開發(fā)中開啟GC就會(huì)忽略retain,release方法互妓。)
檢查target是否為nil。如果為nil,直接cleanup冯勉,然后return澈蚌。(這就是我們可以向nil發(fā)送消息的原因。)
然后在target的Class中根據(jù)Selector去找IMP
尋找IMP的過程:
先從當(dāng)前class的cache方法列表(cache methodLists)里去找
找到了灼狰,跳到對(duì)應(yīng)函數(shù)實(shí)現(xiàn)
沒找到宛瞄,就從class的方法列表(methodLists)里找
還找不到,就到super class的方法列表里找交胚,直到找到基類(NSObject)為止
最后再找不到份汗,就會(huì)進(jìn)入動(dòng)態(tài)方法解析和消息轉(zhuǎn)發(fā)的機(jī)制。(這部分知識(shí)蝴簇,下次再細(xì)談)
0:對(duì)應(yīng)SEL(對(duì)應(yīng)參數(shù))找到否 respondsToSelector
1:動(dòng)態(tài)addmethod機(jī)會(huì) 實(shí)例方法調(diào)resolveInstanceMethod杯活, 類方法調(diào)resolveClassMethod
2:重定向target機(jī)會(huì) forwardingTargetForSelector
3:重定向NSInvocation機(jī)會(huì) methodSignatureForSelector/forwardInvocation
4:無法識(shí)別異常退出 doesNotRecognizeSelector
4、容易混淆的類型
NSObject
@protocol NSObject
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
Class
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;//變量
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;//方法名字+參數(shù)類型+imp函數(shù)指針
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;//協(xié)議熬词?
#endif
} OBJC2_UNAVAILABLE;
SEL
typedef struct objc_selector *SEL
SEL本質(zhì)是一個(gè)字符串,到底是結(jié)構(gòu)體還是字符串
Method
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
IMP
id (*IMP)(id, SEL, ...)
typedef id (*IMP)(id,SEL,...) 要添加的方法
5旁钧、方法交換實(shí)踐
//RAC的方法交換。selector和block
// 因?yàn)閐ealloc方法不能用selector互拾,報(bào)錯(cuò)信息:ARC forbids use of 'dealloc' in a @selector
static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable;
static NSMutableSet *swizzledClasses() {
static dispatch_once_t onceToken;
static NSMutableSet *swizzledClasses = nil;
dispatch_once(&onceToken, ^{
swizzledClasses = [[NSMutableSet alloc] init];
});
return swizzledClasses;
}
static void swizzleDeallocIfNeeded(Class classToSwizzle) {
@synchronized (swizzledClasses()) {
NSString *className = NSStringFromClass(classToSwizzle);
if ([swizzledClasses() containsObject:className]) return;
SEL deallocSelector = sel_registerName("dealloc");
//__unsafe_unretained: 并不對(duì)其保持強(qiáng)引用歪今,這一點(diǎn)和__weak修飾符的變量一樣。當(dāng)這塊地址的內(nèi)存被系統(tǒng)回收時(shí)颜矿,它仍然指向這個(gè)地址寄猩。weak會(huì)自動(dòng)變?yōu)閚il。 再次訪問__unsafe_unretained釋放了內(nèi)存的地址骑疆,會(huì)產(chǎn)生奔潰田篇。
__block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
id newDealloc = ^(__unsafe_unretained id self) {
RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
[compoundDisposable dispose];
if (originalDealloc == NULL) {
struct objc_super superInfo = {
.receiver = self,
.super_class = class_getSuperclass(classToSwizzle)
};
void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
msgSend(&superInfo, deallocSelector);
} else {
originalDealloc(self, deallocSelector);
}
};
IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
//如果當(dāng)前類沒有實(shí)現(xiàn)dealloc,則dealloc直接被替換為新的dealloc的block指針封断;如果實(shí)現(xiàn)了,則保存dealloc的block指針舶担。
if (! class_addMethod(classToSwizzle,
deallocSelector,
newDeallocIMP,
"v@:")) {
// The class already contains a method implementation.
Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
// We need to store original implementation before setting new implementation
// in case method is called at the time of setting.
originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
// We need to store original implementation again, in case it just changed.
originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
}
[swizzledClasses() addObject:className];
}
}
// yykit 不支持協(xié)議方法的交換
BOOL swizzleInstanceMethod(Class aClass, SEL originalSel, SEL newSel) {
// SEL
// typedef struct objc_selector *SEL
// SEL本質(zhì)是一個(gè)字符串,到底是結(jié)構(gòu)體還是字符串
// Method
// struct objc_method {
// SEL method_name OBJC2_UNAVAILABLE;
// char *method_types OBJC2_UNAVAILABLE;
// IMP method_imp OBJC2_UNAVAILABLE;
// }
Method originalMethod = class_getInstanceMethod(aClass, originalSel);
Method newMethod = class_getInstanceMethod(aClass, newSel);
if (!originalMethod || !newMethod) return NO;
// 動(dòng)態(tài)添加方法
//BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
class_addMethod(aClass,// 被添加的類
originalSel, // 方法名坡疼。如果存在則添加不了。如果不存在衣陶,可以添加柄瑰。
// id (*IMP)(id, SEL, ...)
// typedef id (*IMP)(id,SEL,...) 要添加的方法
class_getMethodImplementation(aClass, originalSel),
method_getTypeEncoding(originalMethod));// 添加方法的返回類型和參數(shù)類型
class_addMethod(aClass,// 被添加的類
// 指定一個(gè)類的方法(成員方法/類方法)
newSel,
// 把一個(gè)類的方法(成員方法/類方法),轉(zhuǎn)化為c語(yǔ)言的函數(shù)指針剪况。
class_getMethodImplementation(aClass, newSel),
// 參數(shù):返回值類型教沾,參數(shù)列表
method_getTypeEncoding(newMethod));
method_exchangeImplementations(class_getInstanceMethod(aClass, originalSel),
class_getInstanceMethod(aClass, newSel));
return YES;
}
// 支持協(xié)議方法的交換
BOOL rp_classMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector, SEL nopSelector) {
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
BOOL didAddMethod = class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzleMethod),
method_getTypeEncoding(swizzleMethod));
if (didAddMethod) {
Method nopMehtod = class_getInstanceMethod(aClass, nopSelector);
// 方法1--最妥
class_replaceMethod(aClass,
swizzleSelector,
method_getImplementation(nopMehtod),
method_getTypeEncoding(nopMehtod));
// 方法2--如果直接掉nopMethod會(huì)麻煩
//method_exchangeImplementations(nopMehtod, swizzleMethod);
// 方法3-- 不支持協(xié)議方法的交換
//char *typeeconding=method_getTypeEncoding(originalMethod);
//IMP imp1=method_getImplementation(originalMethod);
//// 如果imp參數(shù)為nil則不能替換
//class_replaceMethod(aClass, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 交換method1和method2的
// A->B,B->A
method_exchangeImplementations(originalMethod, swizzleMethod);
//這種不行。沒有交換译断,只是僅僅替換授翻。A->B,B->B
// class_replaceMethod(aClass, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
}
return YES;
}
// 處理特點(diǎn):強(qiáng)制轉(zhuǎn)化對(duì)應(yīng)的函數(shù)指針。根據(jù)參數(shù)的個(gè)數(shù),強(qiáng)轉(zhuǎn)對(duì)應(yīng)參數(shù)個(gè)數(shù)類型的函數(shù)
id objc_msgSend(id self, SEL op, ...)
// 先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型堪唐,在強(qiáng)制轉(zhuǎn)化為“返回void類型(無返回參數(shù))巡语,3個(gè)參數(shù):id,SEL,int8_t”這個(gè)類型的函數(shù)的指針。再調(diào)用此函數(shù)淮菠。
((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
// 先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型男公,在強(qiáng)制轉(zhuǎn)化為“返回SEL類型,2個(gè)參數(shù):id,SEL”這個(gè)類型的函數(shù)的指針合陵。再調(diào)用此函數(shù)枢赔。
SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
// 先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型,在強(qiáng)制轉(zhuǎn)化為“返回double類型拥知,2個(gè)參數(shù):id,SEL”這個(gè)類型的函數(shù)的指針踏拜。再調(diào)用次此數(shù)。
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
// 先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型举庶,在強(qiáng)制轉(zhuǎn)化為“返回void類型(無返回參數(shù))执隧,3個(gè)參數(shù):id,SEL,double這個(gè)類型的函數(shù)的指針户侥。再調(diào)用次此數(shù)镀琉。
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = [NSString stringWithFormat:@"%p",pointer];
/**
1、
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
2蕊唐、
NSInvocation和NSMethodSingature的target屋摔,sel都不匹配也能行。sel的名不一樣也行替梨。
但是sel的參數(shù)個(gè)數(shù)不一致钓试,會(huì)閃退。
如果NSMethodSignature返回nil副瀑,NSInvocation構(gòu)建會(huì)閃退弓熏。
所以target,sel 最好是一致匹配的糠睡。
為了保證NSMethodSignature不返回nil挽鞠,NSMethodSingature的target和NSInvocation的target要不一樣。
3狈孔、
target是0信认,sel是1呜舒,其它參數(shù)是2户誓,3,4....
總結(jié):
NSMethodSignature/NSInvocation 可以向任意的target發(fā)送sel消息脖隶,傳遞參數(shù)油挥,獲取返回值潦蝇。
NSMethodSignature:定義:Sel名款熬,參數(shù),返回值
*/
+ (void)testNSInvocation{
//SimpleNSInvocationClassA *a =[SimpleNSInvocationClassA new];
//NSMethodSignature*signature= [a methodSignatureForSelector:@selector(method_b1:)];
{
SimpleNSInvocationClassB*b=[SimpleNSInvocationClassB new];
NSMethodSignature*signature = [b methodSignatureForSelector:@selector(method_b1:)];
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:signature];
invocation.target=b;
invocation.selector=@selector(method_b1:);
NSString*arg1=@"helloworld";
[invocation setArgument:&arg1 atIndex:2];
[invocation invoke];
}
{
SimpleNSInvocationClassB*b=[SimpleNSInvocationClassB new];
NSMethodSignature*signature = [b methodSignatureForSelector:@selector(method_b4:str2:str3:)];
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:signature];
invocation.target=b;
invocation.selector=@selector(method_b4:str2:str3:);
NSString*arg1=@"helloworld";
[invocation setArgument:&arg1 atIndex:2];
BOOL result;
[invocation getReturnValue:&result];
/*
__unsafe_unretained: 并不對(duì)其保持強(qiáng)引用护蝶,這一點(diǎn)和__weak修飾符的變量一樣华烟。當(dāng)這塊地址的內(nèi)存被系統(tǒng)回收時(shí),它仍然指向這個(gè)地址持灰。weak會(huì)自動(dòng)變?yōu)閚il盔夜。
再次訪問__unsafe_unretained釋放了內(nèi)存的地址,會(huì)產(chǎn)生奔潰堤魁。
NSNumber __unsafe_unretained *tempResult;
[invocation getReturnValue:&tempResult];
NSNumber *result = tempResult;
return result;
void *tempResult = NULL;
[invocation getReturnValue:&tempResult];
NSNumber *result = (__bridge NSNumber *)tempResult;
return result;
*/
[invocation invoke];
}
}
+ (void)testNSINovcationReWrite {
SimpleNSInvocationClassC*a=[SimpleNSInvocationClassC new];
a.nocrash=[NoSelDoObj new];
[a method_a];
{
TestMethod*tmp= [[self class]new];
// 因?yàn)镾impleNSInvocationClassC沒有method_b方法喂链,所以NSMethodSignature用自己類method_b方法來構(gòu)建,簡(jiǎn)單妥泉。如果用signatureWithObjCTypes椭微,則比較麻煩。
NSMethodSignature*sig=[tmp methodSignatureForSelector:@selector(method_b)];
NSInvocation*invocation=[NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:a];
[invocation setSelector:@selector(method_b)];
[invocation invoke];
}
{
[a performSelector:@selector(method_b)];
}
{
((void (*)(id, SEL)) (void *)objc_msgSend)((id)a, @selector(method_b) );
}
}
-(void)method_b{
}
@interface NoSelDoObj : NSObject
@end
@interface SimpleNSInvocationClassC : NSObject
@property(nonatomic,strong)NoSelDoObj*nocrash;
-(void)method_a;
@end
/**
執(zhí)行順序:
1盲链、如果有對(duì)應(yīng)的SEL蝇率,則直接執(zhí)行SEL方法
2、如果不存在對(duì)應(yīng)的SEL則:
resolveInstanceMethod/resolveClassMethod->返回YES刽沾,并動(dòng)態(tài)添加方法本慕,重新發(fā)送消息至動(dòng)態(tài)添加的方法。返回NO侧漓,或者未動(dòng)態(tài)添加方法锅尘,跳轉(zhuǎn)到forwardingTargetForSelector
forwardingTargetForSelector->由哪個(gè)target來對(duì)應(yīng),如果此target有對(duì)應(yīng)的sel布蔗,則立即執(zhí)行藤违,如果此target沒有對(duì)應(yīng)的sel,則crash纵揍。如果返回target為nil顿乒,則繼續(xù)判斷。
methodSignatureForSelector->由哪個(gè)NSMethodSignature(sel泽谨,返回值類型璧榄,參數(shù)類型)來對(duì)應(yīng),如果對(duì)應(yīng)NSMethodSignature為nil隔盛,則跳轉(zhuǎn)到最后一步doesNotRecognizeSelector犹菱,到這里發(fā)生crash
forwardInvocation->重定向到此執(zhí)行invoke
doesNotRecognizeSelector->如果以上都無法對(duì)應(yīng)拾稳,跳轉(zhuǎn)到這里crash
*/
@implementation SimpleNSInvocationClassC
- (void)method_a{
NSLog(@"a");
}
- (IMP)methodForSelector:(SEL)aSelector {
return [super methodForSelector:aSelector];
}
+ (IMP)instanceMethodForSelector:(SEL)aSelector{
return [super instanceMethodForSelector:aSelector];
}
/**
unrecognized selector sent to instance 0x60000000ce90
找不到sel吮炕,會(huì)閃退
*/
- (void)doesNotRecognizeSelector:(SEL)aSelector{
return [super doesNotRecognizeSelector:aSelector];
}
/**
2: 重定向到其它消息接受者來執(zhí)行。
返回其它消息SEL接受者访得。檢查是否有合適的target龙亲,如果不自定義陕凹,默認(rèn)返回nil
*/
- (id)forwardingTargetForSelector:(SEL)aSelector{
id r= [super forwardingTargetForSelector:aSelector];
// if (!r&&sel_isEqual(aSelector, @selector(method_b))) {
// return _nocrash;
// }
// if (!r) {
// return _nocrash;
// }
//
return r;
}
/**
3: NSInvocation
重定向到其它消息接受者、并對(duì)應(yīng)SEL方法
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
//An array of characters containing the type encodings for the method arguments.
//NSMethodSignature只包括參數(shù)鳄炉。返回類型杜耙,參數(shù)(列)類型.通過Class和SEL可以獲取參數(shù)
//NSMethodSignature*sig= [self.nocrash methodSignatureForSelector:aSelector];
//NSMethodSignature*sig=[super methodSignatureForSelector:aSelector];
NSMethodSignature*sig= [self.nocrash methodSignatureForSelector:@selector(method_c)];
return sig;
}
/**
methodSignatureForSelector -> forwardInvocation
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSInvocation *invocation=anInvocation;
//NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:anInvocation.methodSignature];
// 消息接受者。
invocation.target=_nocrash;
// SEL函數(shù)名拂盯。默認(rèn)是找不到的消息SEL佑女,可以改
//invocation.selector=anInvocation.selector;
invocation.selector=@selector(method_c);
[invocation invoke];
// [super forwardInvocation:invocation];
// [super forwardInvocation:anInvocation];
}
+ (BOOL)resolveClassMethod:(SEL)sel {
BOOL r= [super resolveClassMethod:sel];
return r;
}
/**
1: 執(zhí)行動(dòng)態(tài)添加方法的機(jī)會(huì)class_addMethod
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel{
BOOL r= [super resolveInstanceMethod:sel];
// 返回YES,并動(dòng)態(tài)添加方法谈竿,重新發(fā)送消息至動(dòng)態(tài)添加的方法团驱。
// if (sel==@selector(method_b)) {
// //Class+SEL->IMP, Class+SEL->Method->TypeEncoding
// class_addMethod([self class], sel, class_getMethodImplementation([TestMethod class], sel), method_getTypeEncoding(class_getInstanceMethod([TestMethod class], sel)));
// return YES;
// }
// 返回NO空凸,或者未動(dòng)態(tài)添加方法嚎花,跳轉(zhuǎn)到forwardingTargetForSelector
return r;
}
- (BOOL)respondsToSelector:(SEL)aSelector {
BOOL r=[super respondsToSelector:aSelector];
return r;
}
@end
@implementation NoSelDoObj
-(void)method_b{
MyLog(@"");
}
-(void)method_c {
MyLog(@"");
}
@end
- (id)performSelectorWithArgs:(SEL)sel, ...{
NSMethodSignature * sig = [self methodSignatureForSelector:sel];
if (!sig) {
[self doesNotRecognizeSelector:sel];
return ((void *)0);
}
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
if (!inv) {
[self doesNotRecognizeSelector:sel];
return ((void *)0);
}
[inv setTarget:self];
[inv setSelector:sel];
va_list args;
__builtin_va_start(args, sel);
[NSObject setInv:inv withSig:sig andArgs:args];
__builtin_va_end(args);;
[inv invoke];
return [NSObject getReturnFromInv:inv withSig:sig];
}