美文网首页iOS-Runtime
iOS方法交换研究

iOS方法交换研究

作者: 我是小胡胡分胡 | 来源:发表于2017-07-28 09:57 被阅读1125次

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

    相关文章

      网友评论

        本文标题:iOS方法交换研究

        本文链接:https://www.haomeiwen.com/subject/tndxlxtx.html