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  {
    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];


}





你可能感兴趣的:(iOS方法交换研究)