美文网首页
RAC中的RACSelectorSignal

RAC中的RACSelectorSignal

作者: 一只二进制编码的狗 | 来源:发表于2017-01-08 22:13 被阅读908次

    其实这个源码大部分的内容都是runtime的,如果不了解runtime,这个看起来会比较蛋疼,源码很多,分成一小部分一小部分慢慢看。
    先看这个方法RACSwizzleForwardInvocation

    //这个方法就是swizzle一个class的forwardInvocation函数,熟悉oc方法消息转发的同学都知道,当一个对象收到一个不能响应的消息,最后一步都会走到这个方法。
    static void RACSwizzleForwardInvocation(Class class) {
        SEL forwardInvocationSEL = @selector(forwardInvocation:);
        Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);
    
        //如果forwardInvocation已经有实现了,就存下。。
        void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
        if (forwardInvocationMethod != NULL) {
            originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod);
        }
           //去定义新的forwardInvocation实现
        id newForwardInvocation = ^(id self, NSInvocation *invocation) {
                   //如果已经被rac_signalForSelector过了,直接走一遍RACForwardInvocation,然后返回
            BOOL matched = RACForwardInvocation(self, invocation);
            if (matched) return;
                   //如果没有的话就直接走远来的实现
            if (originalForwardInvocation == NULL) {
                [self doesNotRecognizeSelector:invocation.selector];
            } else {
                originalForwardInvocation(self, forwardInvocationSEL, invocation);
            }
        };
    
        class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@");
    }
    

    关于上段的RACForwardInvocation,源码如下

    static BOOL RACForwardInvocation(id self, NSInvocation *invocation) {
            //获取带rac_alias_前缀的方法
        SEL aliasSelector = RACAliasForSelector(invocation.selector);
           //可以看到sel其实也就是个const void *key
        RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
    
        Class class = object_getClass(invocation.target);
        BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector];
           //如果这个class有带rac_alias_前缀的方法,就执行
        if (respondsToAlias) {
            invocation.selector = aliasSelector;
            [invocation invoke];
        }
    
        if (subject == nil) return respondsToAlias;
            //给订阅这发送invovation的入参
        [subject sendNext:invocation.rac_argumentsTuple];
        return YES;
    }
    

    下面在继续看RACSwizzleRespondsToSelector,其实这个方法就是去swizzle一个class的respondsToSelector的方法

    static void RACSwizzleRespondsToSelector(Class class) {
        SEL respondsToSelectorSEL = @selector(respondsToSelector:);
        Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL);
        BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod);
           //和上面一个套路。。
        id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
                   //这个方法就是去根据selector去找一个class的method,其实系统有个方法
    class_getInstanceMethod,不懂为啥要自己写。。
            Method method = rac_getImmediateInstanceMethod(class, selector);
    
            if (method != NULL && method_getImplementation(method) == _objc_msgForward) {
                  //如果有alias的selector则返回yes,否则就返回原来的respondsToSelector的实现结果。
                SEL aliasSelector = RACAliasForSelector(selector);
                if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES;
            }
    
            return originalRespondsToSelector(self, respondsToSelectorSEL, selector);
        };
    
        class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod));
    }
    

    下面继续看RACSwizzleGetClass,这个就是去swizzle一个[self class] 这个get方法。

    static void RACSwizzleGetClass(Class class, Class statedClass) {
        SEL selector = @selector(class);
        Method method = class_getInstanceMethod(class, selector);
        IMP newIMP = imp_implementationWithBlock(^(id self) {
                    //返回statedClass
            return statedClass;
        });
        class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method));
    }
    

    熟悉runtime的都知道重写forwardInvocation,一定要重写方法签名,也就是methodSignatureForSelector这个函数。下面就开始看这个方法。

    static void RACSwizzleMethodSignatureForSelector(Class class) {
        IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) {
                    //这时候你去调self.class并不会拿到真实的,因为被我们swizzle掉了
            Class actualClass = object_getClass(self);
            Method method = class_getInstanceMethod(actualClass, selector);
     //如果有这个方法,那直接返回签名,如果没有的话,那就调super
            if (method == NULL) {
                struct objc_super target = {
                    .super_class = class_getSuperclass(class),
                    .receiver = self,
                };
                NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper;
    //这个相当于用[super ]
                return messageSend(&target, @selector(methodSignatureForSelector:), selector);
            }
    
            char const *encoding = method_getTypeEncoding(method);
            return [NSMethodSignature signatureWithObjCTypes:encoding];
        });
    
        SEL selector = @selector(methodSignatureForSelector:);
        Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector);
        class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod));
    }
    

    以上是基础部分现在可以开始看signalforselector这个方法了,其核心逻辑在下面这个方法中

    static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
            //获取带rac_alias_头的方法,正常第一次都是没有的
        SEL aliasSelector = RACAliasForSelector(selector);
    
        @synchronized (self) {
    //如果已经被rac_signalForSelector过了,那已经有这个subject了,直接返回就行了
            RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
            if (subject != nil) return subject;
    //这个方法上面已经说了,就是swizzzle这个class一些列协议转发的方法。
            Class class = RACSwizzleClass(self);
            NSCAssert(class != nil, @"Could not swizzle class of %@", self);
    //第一次去set下这个subject
            subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", self.rac_description, sel_getName(selector)];
            objc_setAssociatedObject(self, aliasSelector, subject, OBJC_ASSOCIATION_RETAIN);
    
            [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
                [subject sendCompleted];
            }]];
    //获取这个方法
            Method targetMethod = class_getInstanceMethod(class, selector);
            if (targetMethod == NULL) {
    //如果没有的话,不会crash,会走下面的逻辑,首先去定义入参的类型,classaddmethod的时候会用到。
                const char *typeEncoding;
                if (protocol == NULL) {
                    typeEncoding = RACSignatureForUndefinedSelector(selector);
                } else {
                    // Look for the selector as an optional instance method.
                    struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
    
                    if (methodDescription.name == NULL) {
                        // Then fall back to looking for a required instance
                        // method.
                        methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES);
                        NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol));
                    }
    
                    typeEncoding = methodDescription.types;
                }
    
                RACCheckTypeEncoding(typeEncoding);
    
                // 去添加这个方法,如果不成功,能直接返回racsignal error
                if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) {
                    NSDictionary *userInfo = @{
                        NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"A race condition occurred implementing %@ on class %@", nil), NSStringFromSelector(selector), class],
                        NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Invoke -rac_signalForSelector: again to override the implementation.", nil)
                    };
    
                    return [RACSignal error:[NSError errorWithDomain:RACSelectorSignalErrorDomain code:RACSelectorSignalErrorMethodSwizzlingRace userInfo:userInfo]];
                }
            } else if (method_getImplementation(targetMethod) != _objc_msgForward) {
    //如果这个方法存在
                // Make a method alias for the existing method implementation.
                const char *typeEncoding = method_getTypeEncoding(targetMethod);
    
                RACCheckTypeEncoding(typeEncoding);
    添加一个带标志为的方法。
                BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
                NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class);
    //siwzzle这个selector的实现,让他继续把消息发送出去这样就能走到我们上面的forwardInvocation,那个地方我们做了自己的逻辑,_objc_msgForward就是发送消息。。
                // Redefine the selector to call -forwardInvocation:.
                class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
            }
    
            return subject;
        }
    }

    相关文章

      网友评论

          本文标题:RAC中的RACSelectorSignal

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