ReactiveObjC 源码阅读笔记 (-rac_signalForSelector:)

常用方法响应 -rac_signalForSelector:

使用:

    [[[self rac_signalForSelector:@selector(viewWillAppear:)] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(RACTuple * _Nullable x) {
        
    }];

源码

  • NSObject+RACSelectorSignal
- (RACSignal *)rac_signalForSelector:(SEL)selector {
    NSCParameterAssert(selector != NULL);

    return NSObjectRACSignalForSelector(self, selector, NULL);
}

static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
     // rac_alias_ + 方法名 = 方法别名
    SEL aliasSelector = RACAliasForSelector(selector);

    @synchronized (self) {
        // 方法别名关联对象,存储subject,存在就直接返回,供外部订阅,不存在,去创建
        RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
        if (subject != nil) return subject;
        /**
          创建名为 Class_RACSelectorSignal 的子类
          重写子类一系统的方法,进行掩饰
          使 isa 指针指向新创建的子类
          返回新创建的 Class_RACSelectorSignal,下面的操作都针对 Class_RACSelectorSignal。
          具体看 RACSwizzleClass()
        */
        Class class = RACSwizzleClass(self);
        NSCAssert(class != nil, @"Could not swizzle class of %@", self);
        // 创建 subject,存到以方法别名为名的关联对象中。下次可直接取出使用。
        subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", RACDescription(self), 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) {
            // 监听的方法不存在
            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);

            // Define the selector to call -forwardInvocation:.
            // 添加这个方法,实现为_objc_msgForward
            if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) {
                // 如果添加失败,直接返一个ErrorSignal。
                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) {
            // 方法存在,且方法的实现不是 _objc_msgForward
            // Make a method alias for the existing method implementation.
            const char *typeEncoding = method_getTypeEncoding(targetMethod);

            RACCheckTypeEncoding(typeEncoding);
            /**
            添加一个方法,方法名是 rac_alias_originMethod,实现是目标方法 originMethod的实现。
            然后重写目标方法的实现,改为_objc_msgForward。
            相当于,下次,再调用目标方法的时候,会直接走消息转发流程,然后RAC在消息转发流程中,先触发 signal 响应,然后再执行 rac_alias_originMethod,也就是原方法的实现。以达到监听方法的目的。
            */
            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);

            // Redefine the selector to call -forwardInvocation:.
            class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
        }

        return subject;
    }
}

// 修改 Class
static Class RACSwizzleClass(NSObject *self) {
    /**
     因为 class方法被重写,所以这两个值有可能被替换
     statedClass:Class,对外表示的类
     baseClass:真正的类,isa 指向的类 Class 或 Class_RACSelectorSignal
    */
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);

    // The "known dynamic subclass" is the subclass generated by RAC.
    // It's stored as an associated object on every instance that's already
    // been swizzled, so that even if something else swizzles the class of
    // this instance, we can still access the RAC generated subclass.
    Class knownDynamicSubclass = objc_getAssociatedObject(self, RACSubclassAssociationKey);
    if (knownDynamicSubclass != Nil) return knownDynamicSubclass;

    NSString *className = NSStringFromClass(baseClass);

    if (statedClass != baseClass) {
        // If the class is already lying about what it is, it's probably a KVO
        // dynamic subclass or something else that we shouldn't subclass
        // ourselves.
        //
        // Just swizzle -forwardInvocation: in-place. Since the object's class
        // was almost certainly dynamically changed, we shouldn't see another of
        // these classes in the hierarchy.
        //
        // Additionally, swizzle -respondsToSelector: because the default
        // implementation may be ignorant of methods added to this class.
        // 这里是兼容系统KVO,因为系统KVO也会修改 isa。如果已经使用过系统KVO,则直接交换相关方法,不需要再重新新类。
        @synchronized (swizzledClasses()) {
            if (![swizzledClasses() containsObject:className]) {
                // 未交换方法
                RACSwizzleForwardInvocation(baseClass);
                RACSwizzleRespondsToSelector(baseClass);
                RACSwizzleGetClass(baseClass, statedClass);
                RACSwizzleGetClass(object_getClass(baseClass), statedClass);
                RACSwizzleMethodSignatureForSelector(baseClass);
                [swizzledClasses() addObject:className];
            }
        }

        return baseClass;
    }
    // 未修改 isa 指针,创建名为  Class_RACSelectorSignal 的子类
    const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String;
    Class subclass = objc_getClass(subclassName);

    if (subclass == nil) {
        subclass = objc_allocateClassPair(baseClass, subclassName, 0);
        if (subclass == nil) return nil;
        /**
        修改 Class_RACSelectorSignal -forwardInvocation: 方法实现
        rac_alias_selector 方法存在时,执行方法,subject sendNext:
        当方法未找到时,subject sendError:
        */
        RACSwizzleForwardInvocation(subclass);
        // 修改 Class_RACSelectorSignal -respondsToSelector: 方法实现,获取参数 selector 方法,当方法存在,并且方法实现不是 _objc_msg
        /**
        修改 Class_RACSelectorSignal -respondsToSelector: 方法实现
        获取参数 selector方法,当方法存在,且方法实现不是 _objc_msgForward
        获取 Rac_alias_selector 对应的关联对象,不为空是,返回YES。
        
        如果方法不存在,或方法实现是 _objc_msgForward 时
        返回原方法实现。
        */
        RACSwizzleRespondsToSelector(subclass);
        // 修改 Class_RACSelectorSignal 的 -class 方法实现,返回 Class
        RACSwizzleGetClass(subclass, statedClass);
        // 修改 Class_RACSelectorSignal 的 +class 方法实现,返回 Class
        RACSwizzleGetClass(object_getClass(subclass), statedClass);
        // 重写 -methodSignatureForSelectorMethod: 实现,当方法存在时,返回方法签名,方法不存在时,调用 super -methodSignatureForSelectorMethod: 方法,也就是 Class 的方法。
        RACSwizzleMethodSignatureForSelector(subclass);
        // 注册子类
        objc_registerClassPair(subclass);
    }
    // 修改 isa 指针
    object_setClass(self, subclass);
    objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass, OBJC_ASSOCIATION_ASSIGN);
    return subclass;
}

// 修改-forwardInvocation: 实现
static void RACSwizzleForwardInvocation(Class class) {
    SEL forwardInvocationSEL = @selector(forwardInvocation:);
    Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);

    // Preserve any existing implementation of -forwardInvocation:.
    void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
    if (forwardInvocationMethod != NULL) {
        originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod);
    }

    // Set up a new version of -forwardInvocation:.
    //
    // If the selector has been passed to -rac_signalForSelector:, invoke
    // the aliased method, and forward the arguments to any attached signals.
    //
    // If the selector has not been passed to -rac_signalForSelector:,
    // invoke any existing implementation of -forwardInvocation:. If there
    // was no existing implementation, throw an unrecognized selector
    // exception.
    id newForwardInvocation = ^(id self, NSInvocation *invocation) {
        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@:@");
}

// rac_alias_selector 是否能响应
static BOOL RACForwardInvocation(id self, NSInvocation *invocation) {
    SEL aliasSelector = RACAliasForSelector(invocation.selector);
    RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);

    Class class = object_getClass(invocation.target);
    BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector];
    // 别名方法的实现,是原方法的实现,因为有可能监听一个未实现的方法,所以别名方法有可能无法响应。所以这里判断一下。
    if (respondsToAlias) {
        invocation.selector = aliasSelector;
        [invocation invoke];
    }

    if (subject == nil) return respondsToAlias;

    [subject sendNext:invocation.rac_argumentsTuple];
    return YES;
}

// 修改-respondsToSelector: 方法实现
static void RACSwizzleRespondsToSelector(Class class) {
    SEL respondsToSelectorSEL = @selector(respondsToSelector:);

    // Preserve existing implementation of -respondsToSelector:.
    Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL);
    BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod);

    // Set up a new version of -respondsToSelector: that returns YES for methods
    // added by -rac_signalForSelector:.
    //
    // If the selector has a method defined on the receiver's actual class, and
    // if that method's implementation is _objc_msgForward, then returns whether
    // the instance has a signal for the selector.
    // Otherwise, call the original -respondsToSelector:.
    id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
        Method method = rac_getImmediateInstanceMethod(class, selector);

        if (method != NULL && method_getImplementation(method) == _objc_msgForward) {
            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));
}

RAC 监听方法前后,类结构图

方法监听前后
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容