iOS方法交换研究
1、Class/SEL/Method/IMP
Class+SEL=>Method=>IMP,...
2、class_
class_addMethod/class_replaceMethod/method_exchangeImplementations
class_addMethod//如果要添加的A方法已经存在则返回NO
A->B,B->B
method_exchangeImplementations
A->B,B->A
class_replaceMethod
A->B,B->B
3、消息处理机制
objc_msgSend/NSInvocation+NSMethodSignature/perfermSelector
1、更低层c语言实现
id objc_msgSend(id self, SEL op, ...)
2、oc的封装高级层
NSMethodSignature=返回值类型+参数(列)类型
SEL=函数名字
NSInvocation=target+SEL+NSMethodSignature
getArgumentTypeAtIndex:第0个参数是target,第1个参数是SEL,其它的参数(列)从第2,3,4,下标开始。
3、消息处理机制总结:
那么 objc_msgSend 到底是怎么工作的呢?
在Objective-C中,消息直到运行时才会绑定到方法的实现上。编译器会把代码中[target doSth]转换成 objc_msgSend消息函数,这个函数完成了动态绑定的所有事情。它的运行流程如下:
检查selector是否需要忽略。(ps: Mac开发中开启GC就会忽略retain,release方法。)
检查target是否为nil。如果为nil,直接cleanup,然后return。(这就是我们可以向nil发送消息的原因。)
然后在target的Class中根据Selector去找IMP
寻找IMP的过程:
先从当前class的cache方法列表(cache methodLists)里去找
找到了,跳到对应函数实现
没找到,就从class的方法列表(methodLists)里找
还找不到,就到super class的方法列表里找,直到找到基类(NSObject)为止
最后再找不到,就会进入动态方法解析和消息转发的机制。(这部分知识,下次再细谈)
0:对应SEL(对应参数)找到否 respondsToSelector
1:动态addmethod机会 实例方法调resolveInstanceMethod, 类方法调resolveClassMethod
2:重定向target机会 forwardingTargetForSelector
3:重定向NSInvocation机会 methodSignatureForSelector/forwardInvocation
4:无法识别异常退出 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;//方法名字+参数类型+imp函数指针
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;//协议?
#endif
} OBJC2_UNAVAILABLE;
SEL
typedef struct objc_selector *SEL
SEL本质是一个字符串,到底是结构体还是字符串
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、方法交换实践
//RAC的方法交换。selector和block
// 因为dealloc方法不能用selector,报错信息: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: 并不对其保持强引用,这一点和__weak修饰符的变量一样。当这块地址的内存被系统回收时,它仍然指向这个地址。weak会自动变为nil。 再次访问__unsafe_unretained释放了内存的地址,会产生奔溃。
__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);
//如果当前类没有实现dealloc,则dealloc直接被替换为新的dealloc的block指针;如果实现了,则保存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 不支持协议方法的交换
BOOL swizzleInstanceMethod(Class aClass, SEL originalSel, SEL newSel) {
// SEL
// typedef struct objc_selector *SEL
// SEL本质是一个字符串,到底是结构体还是字符串
// 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;
// 动态添加方法
//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));// 添加方法的返回类型和参数类型
class_addMethod(aClass,// 被添加的类
// 指定一个类的方法(成员方法/类方法)
newSel,
// 把一个类的方法(成员方法/类方法),转化为c语言的函数指针。
class_getMethodImplementation(aClass, newSel),
// 参数:返回值类型,参数列表
method_getTypeEncoding(newMethod));
method_exchangeImplementations(class_getInstanceMethod(aClass, originalSel),
class_getInstanceMethod(aClass, newSel));
return YES;
}
// 支持协议方法的交换
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会麻烦
//method_exchangeImplementations(nopMehtod, swizzleMethod);
// 方法3-- 不支持协议方法的交换
//char *typeeconding=method_getTypeEncoding(originalMethod);
//IMP imp1=method_getImplementation(originalMethod);
//// 如果imp参数为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;
}
// 处理特点:强制转化对应的函数指针。根据参数的个数,强转对应参数个数类型的函数
id objc_msgSend(id self, SEL op, ...)
// 先把objc_msgSend函数指针强制转化为void*类型,在强制转化为“返回void类型(无返回参数),3个参数:id,SEL,int8_t”这个类型的函数的指针。再调用此函数。
((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
// 先把objc_msgSend函数指针强制转化为void*类型,在强制转化为“返回SEL类型,2个参数:id,SEL”这个类型的函数的指针。再调用此函数。
SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
// 先把objc_msgSend函数指针强制转化为void*类型,在强制转化为“返回double类型,2个参数:id,SEL”这个类型的函数的指针。再调用次此数。
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
// 先把objc_msgSend函数指针强制转化为void*类型,在强制转化为“返回void类型(无返回参数),3个参数:id,SEL,double这个类型的函数的指针。再调用次此数。
((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的参数个数不一致,会闪退。
如果NSMethodSignature返回nil,NSInvocation构建会闪退。
所以target,sel 最好是一致匹配的。
为了保证NSMethodSignature不返回nil,NSMethodSingature的target和NSInvocation的target要不一样。
3、
target是0,sel是1,其它参数是2,3,4....
总结:
NSMethodSignature/NSInvocation 可以向任意的target发送sel消息,传递参数,获取返回值。
NSMethodSignature:定义:Sel名,参数,返回值
*/
+ (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: 并不对其保持强引用,这一点和__weak修饰符的变量一样。当这块地址的内存被系统回收时,它仍然指向这个地址。weak会自动变为nil。
再次访问__unsafe_unretained释放了内存的地址,会产生奔溃。
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];
// 因为SimpleNSInvocationClassC没有method_b方法,所以NSMethodSignature用自己类method_b方法来构建,简单。如果用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
/**
执行顺序:
1、如果有对应的SEL,则直接执行SEL方法
2、如果不存在对应的SEL则:
resolveInstanceMethod/resolveClassMethod->返回YES,并动态添加方法,重新发送消息至动态添加的方法。返回NO,或者未动态添加方法,跳转到forwardingTargetForSelector
forwardingTargetForSelector->由哪个target来对应,如果此target有对应的sel,则立即执行,如果此target没有对应的sel,则crash。如果返回target为nil,则继续判断。
methodSignatureForSelector->由哪个NSMethodSignature(sel,返回值类型,参数类型)来对应,如果对应NSMethodSignature为nil,则跳转到最后一步doesNotRecognizeSelector,到这里发生crash
forwardInvocation->重定向到此执行invoke
doesNotRecognizeSelector->如果以上都无法对应,跳转到这里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,会闪退
*/
- (void)doesNotRecognizeSelector:(SEL)aSelector{
return [super doesNotRecognizeSelector:aSelector];
}
/**
2: 重定向到其它消息接受者来执行。
返回其它消息SEL接受者。检查是否有合适的target,如果不自定义,默认返回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
重定向到其它消息接受者、并对应SEL方法
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
//An array of characters containing the type encodings for the method arguments.
//NSMethodSignature只包括参数。返回类型,参数(列)类型.通过Class和SEL可以获取参数
//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函数名。默认是找不到的消息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: 执行动态添加方法的机会class_addMethod
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel{
BOOL r= [super resolveInstanceMethod:sel];
// 返回YES,并动态添加方法,重新发送消息至动态添加的方法。
// 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,或者未动态添加方法,跳转到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];
}