RAC中的RACSelectorSignal

其實這個源碼大部分的內(nèi)容都是runtime的座硕,如果不了解runtime,這個看起來會比較蛋疼初肉,源碼很多呻惕,分成一小部分一小部分慢慢看荆责。
先看這個方法RACSwizzleForwardInvocation

//這個方法就是swizzle一個class的forwardInvocation函數(shù),熟悉oc方法消息轉(zhuǎn)發(fā)的同學(xué)都知道亚脆,當(dāng)一個對象收到一個不能響應(yīng)的消息做院,最后一步都會走到這個方法。
static void RACSwizzleForwardInvocation(Class class) {
    SEL forwardInvocationSEL = @selector(forwardInvocation:);
    Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);

    //如果forwardInvocation已經(jīng)有實現(xiàn)了濒持,就存下键耕。。
    void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
    if (forwardInvocationMethod != NULL) {
        originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod);
    }
       //去定義新的forwardInvocation實現(xiàn)
    id newForwardInvocation = ^(id self, NSInvocation *invocation) {
               //如果已經(jīng)被rac_signalForSelector過了柑营,直接走一遍RACForwardInvocation屈雄,然后返回
        BOOL matched = RACForwardInvocation(self, invocation);
        if (matched) return;
               //如果沒有的話就直接走遠(yuǎn)來的實現(xiàn)
        if (originalForwardInvocation == NULL) {
            [self doesNotRecognizeSelector:invocation.selector];
        } else {
            originalForwardInvocation(self, forwardInvocationSEL, invocation);
        }
    };

    class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@");
}

關(guān)于上段的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_前綴的方法由境,就執(zhí)行
    if (respondsToAlias) {
        invocation.selector = aliasSelector;
        [invocation invoke];
    }

    if (subject == nil) return respondsToAlias;
        //給訂閱這發(fā)送invovation的入?yún)?    [subject sendNext:invocation.rac_argumentsTuple];
    return YES;
}

下面在繼續(xù)看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) {
               //這個方法就是去根據(jù)selector去找一個class的method讥蟆,其實系統(tǒng)有個方法
class_getInstanceMethod,不懂為啥要自己寫纺阔。瘸彤。
        Method method = rac_getImmediateInstanceMethod(class, selector);

        if (method != NULL && method_getImplementation(method) == _objc_msgForward) {
              //如果有alias的selector則返回yes,否則就返回原來的respondsToSelector的實現(xiàn)結(jié)果笛钝。
            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));
}

下面繼續(xù)看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這個函數(shù)。下面就開始看這個方法囤捻。

static void RACSwizzleMethodSignatureForSelector(Class class) {
    IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) {
                //這時候你去調(diào)self.class并不會拿到真實的臼朗,因為被我們swizzle掉了
        Class actualClass = object_getClass(self);
        Method method = class_getInstanceMethod(actualClass, selector);
 //如果有這個方法,那直接返回簽名蝎土,如果沒有的話视哑,那就調(diào)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;
//這個相當(dāng)于用[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));
}

以上是基礎(chǔ)部分現(xiàn)在可以開始看signalforselector這個方法了,其核心邏輯在下面這個方法中

static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
        //獲取帶rac_alias_頭的方法誊涯,正常第一次都是沒有的
    SEL aliasSelector = RACAliasForSelector(selector);

    @synchronized (self) {
//如果已經(jīng)被rac_signalForSelector過了挡毅,那已經(jīng)有這個subject了,直接返回就行了
        RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
        if (subject != nil) return subject;
//這個方法上面已經(jīng)說了暴构,就是swizzzle這個class一些列協(xié)議轉(zhuǎn)發(fā)的方法跪呈。
        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,會走下面的邏輯庆械,首先去定義入?yún)⒌念愋娃崩#琧lassaddmethod的時候會用到。
            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);
添加一個帶標(biāo)志為的方法。
            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的實現(xiàn)堕绩,讓他繼續(xù)把消息發(fā)送出去這樣就能走到我們上面的forwardInvocation策幼,那個地方我們做了自己的邏輯,_objc_msgForward就是發(fā)送消息奴紧。特姐。
            // Redefine the selector to call -forwardInvocation:.
            class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
        }

        return subject;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市黍氮,隨后出現(xiàn)的幾起案子唐含,更是在濱河造成了極大的恐慌,老刑警劉巖沫浆,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捷枯,死亡現(xiàn)場離奇詭異,居然都是意外死亡专执,警方通過查閱死者的電腦和手機淮捆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來本股,“玉大人攀痊,你說我怎么就攤上這事≈粝裕” “怎么了苟径?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長躬审。 經(jīng)常有香客問我涩笤,道長,這世上最難降的妖魔是什么盒件? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮舱禽,結(jié)果婚禮上炒刁,老公的妹妹穿的比我還像新娘。我一直安慰自己誊稚,他們只是感情好翔始,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布罗心。 她就那樣靜靜地躺著,像睡著了一般城瞎。 火紅的嫁衣襯著肌膚如雪渤闷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天脖镀,我揣著相機與錄音飒箭,去河邊找鬼。 笑死蜒灰,一個胖子當(dāng)著我的面吹牛弦蹂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播强窖,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼凸椿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翅溺?” 一聲冷哼從身側(cè)響起脑漫,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咙崎,沒想到半個月后优幸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡叙凡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年劈伴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片握爷。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡跛璧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出新啼,到底是詐尸還是另有隱情追城,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布燥撞,位于F島的核電站座柱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏物舒。R本人自食惡果不足惜色洞,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冠胯。 院中可真熱鬧火诸,春花似錦、人聲如沸荠察。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盯荤,卻和暖如春馋吗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秋秤。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工宏粤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人航缀。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓商架,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芥玉。 傳聞我的和親對象是個殘疾皇子蛇摸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,720評論 0 9
  • 文中的實驗代碼我放在了這個項目中灿巧。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 923評論 0 6
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,560評論 33 466
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 763評論 0 1
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 735評論 0 2