③、iOS-RAC-底层分析-RAC的宏-RACCommand

iOS RAC系列
①、iOS-RAC的开发用法-底层分析以及总结
②、iOS-RAC-核心类分析-RACPassthroughSubscriber订阅者-RACScheduler调度者-RACDisposable销毁者-RACObseve监听者-RACSubject
③、iOS-RAC-底层分析-RAC的宏-RACCommand
(完)④、iOS-RAC-在实际开发的使用-以登录注册为例子

Demo
Demo-iOS-RAC-高阶函数-带注释
iOS-RAC-实际开发案例-登录注册
资料
iOS-RAC底层源码分析-思维导图-MindNote

①、RAC的宏

1.RAC的宏 @weakify拆分

// 宏定义
- (void)Define
{
    // 宏:
    // # 宏参数 答题参数值为内容的字符常量
    // A##B ----> AB
    // .... 参数 ---- __VA_ARGS  -- LOG
    // 封装流程 ---- 装逼
    
    @weakify(self);
    // 用宏封装了这么多层 更多是面向用户开发者
    // @weakify 最终生成的是  __weak __typeof__(self) self_weak_ = (self);
   
    
    
    /**
     1、@weakify =
     #define weakify(...) \
         rac_keywordify \
         metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
     
     
     2、rac_keywordify = autoreleasepool {}
     metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
     
     3、得到  autoreleasepool {
        metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
     }
     
     4、metamacro_foreach_cxt = #define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
     metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
     
     5、#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
             metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
     将数据代入
     MACRO = rac_weakify_
     SEP =
     CONTEXT = __weak
     
     6、#define metamacro_foreach_cxt(rac_weakify_, , __weak, ...) \
             metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

     7、metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))
     ==
     #define metamacro_concat(A, B) \
     metamacro_concat_(A, B)
     
     // metamacro_argcount 什么意思 : argcount 参数个数
     8、metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))
     metamacro_argcount(__VA_ARGS__)  ==  #define metamacro_argcount(...) \
     metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
        
     
     9、     metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) =
     #define metamacro_at(N, ...) \
             metamacro_concat(metamacro_at, N)(__VA_ARGS__)
     10、metamacro_at20(__VA_ARGS__)  === #define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, self.name) metamacro_head(__VA_ARGS__)

    11、     #define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, 20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) metamacro_head(__VA_ARGS__)
      self.name = metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

    12、metamacro_head   === #define metamacro_head_(FIRST, ...) FIRST
            //只接受前面的1个值
   13、  metamacro_at20 只能填充20个 所以这里只能从前面一直数 数到20个    self.name = metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
        剩下 2跟1
    16、
     //得出 metamacro_foreach_cxt2
         metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, __VA_ARGS__)
     #define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
         metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
         SEP \
         MACRO(1, CONTEXT, _1)
     // 最终 变成了 #define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
     #define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

         metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0)
         // SEP是空
         rac_weakify_(1,__weak,_1)
     // #define rac_weakify_(INDEX, CONTEXT, VAR) \
         CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
         
         // VAR 就是传过来的 self
         __weak __typeof__(self) self_weak_ = (self);

     */

}


②、RACCommand

1.RACCommand的使用 executionSignals 执行信号switchToLatest 最新errors 错误executing 执行

// RACCommand
- (void)RACCommand
{
    //  RACCommand 是个 object
    // 用来监听 状态 是否正在执行 --- 是否有 error 信息 -- 是否成功
//    self.loginBtn.rac_command =
//    [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
//        NSLog(@"RACCommand is %@",input);
//        return [RACSignal empty];
//    }];
    
    
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        // 执行2 输入命令
        NSLog(@"RACCommand createSignal  %@",input);
        
        // 登录 ---> 注册 ---- ?????
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"yy"];
            [subscriber sendCompleted];
            return  [RACDisposable disposableWithBlock:^{
                // 执行6
                NSLog(@"销毁了---");
            }];
        }];
    }];
    
    // 执行信号
    [command.executionSignals subscribeNext:^(id  _Nullable x) {
        // 执行4
        NSLog(@"RACCommand executionSignals  %@",x);

    }];
    
    // 最新
    [[command.executionSignals switchToLatest] subscribeNext:^(id  _Nullable x) {
        // 执行5 [subscriber sendNext:@"yy"];
        NSLog(@"RACCommand switchToLatest  %@",x);

    }];
    
    // 是否正在执行  // 执行1 执行3 执行8
    // addedExecutionSignalsSubject --> signal ---
    // 班长 ---- 小兵   ---- 拉练习
    // 排长 --- 班长
    [command.executing subscribeNext:^(id  _Nullable x) {
        NSLog(@"RACCommand executing  %@",x);

    }];
    
    // 错误
    [command.errors subscribeNext:^(NSError * _Nullable x) {
        NSLog(@"RACCommand errors  %@",x);

    }];

    // 开始执行命令
    [command execute:@"命令"];

}

2.RACCommand底层逻辑分析

2.1我们先看一下RACCommand的初始化方法

RACCommand初始化方法

2.2我们先看一下RACCommandinitWithSignalBlockinitWithEnabled:signalBlock
我们看到 RACCommand initWithEnabled:signalBlock有很多属性 是用来状态 判断是否 正在来、是否在执行、是否发生错误

RACCommand.h

我们最重要的核心就是看addedExecutionSignalsSubject,他是属于RACSubject.RACSubject是即可攻也可以守的。
- (instancetype)initWithSignalBlock:(RACSignal<id> * (^)(id input))signalBlock {
	return [self initWithEnabled:nil signalBlock:signalBlock];
}

- (instancetype)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal<id> * (^)(id input))signalBlock {
	NSCParameterAssert(signalBlock != nil);

	self = [super init];

	_addedExecutionSignalsSubject = [RACSubject new];
	_allowsConcurrentExecutionSubject = [RACSubject new];
	_signalBlock = [signalBlock copy];

	_executionSignals = [[[self.addedExecutionSignalsSubject
		map:^(RACSignal *signal) {
			return [signal catchTo:[RACSignal empty]];
		}]
		deliverOn:RACScheduler.mainThreadScheduler]
		setNameWithFormat:@"%@ -executionSignals", self];
	
	// `errors` needs to be multicasted so that it picks up all
	// `activeExecutionSignals` that are added.
	//
	// In other words, if someone subscribes to `errors` _after_ an execution
	// has started, it should still receive any error from that execution.
	RACMulticastConnection *errorsConnection = [[[self.addedExecutionSignalsSubject
		flattenMap:^(RACSignal *signal) {
			return [[signal
				ignoreValues]
				catch:^(NSError *error) {
					return [RACSignal return:error];
				}];
		}]
		deliverOn:RACScheduler.mainThreadScheduler]
		publish];
	
	_errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self];
	[errorsConnection connect];

	RACSignal *immediateExecuting = [[[[self.addedExecutionSignalsSubject
		flattenMap:^(RACSignal *signal) {
			return [[[signal
				catchTo:[RACSignal empty]]
				then:^{
					return [RACSignal return:@-1];
				}]
				startWith:@1];
		}]
		scanWithStart:@0 reduce:^(NSNumber *running, NSNumber *next) {
			return @(running.integerValue + next.integerValue);
		}]
		map:^(NSNumber *count) {
			return @(count.integerValue > 0);
		}]
		startWith:@NO];

	_executing = [[[[[immediateExecuting
		deliverOn:RACScheduler.mainThreadScheduler]
		// This is useful before the first value arrives on the main thread.
		startWith:@NO]
		distinctUntilChanged]
		replayLast]
		setNameWithFormat:@"%@ -executing", self];
	
	RACSignal *moreExecutionsAllowed = [RACSignal
		if:[self.allowsConcurrentExecutionSubject startWith:@NO]
		then:[RACSignal return:@YES]
		else:[immediateExecuting not]];
	
	if (enabledSignal == nil) {
		enabledSignal = [RACSignal return:@YES];
	} else {
		enabledSignal = [enabledSignal startWith:@YES];
	}
	
	_immediateEnabled = [[[[RACSignal
		combineLatest:@[ enabledSignal, moreExecutionsAllowed ]]
		and]
		takeUntil:self.rac_willDeallocSignal]
		replayLast];
	
	_enabled = [[[[[self.immediateEnabled
		take:1]
		concat:[[self.immediateEnabled skip:1] deliverOn:RACScheduler.mainThreadScheduler]]
		distinctUntilChanged]
		replayLast]
		setNameWithFormat:@"%@ -enabled", self];

	return self;
}

2.3 addedExecutionSignalsSubject 有了signal信号 subscribeNext订阅才有用

订阅是由addedExecutionSignalsSubject 来的 。
我们来查看addedExecutionSignalsSubject的signal如何来的。
我们主要看execute

subscribeNext
   [command.executing subscribeNext:^(id  _Nullable x) {
        NSLog(@"RACCommand executing  %@",x);

    }];
execute

打个比如:班长带兵进行排练。流程就是班长-发送指令-士兵-士兵进行排练 班长->士兵->排练,但是班长不能自己下达这个命令,那么谁下命令给班长 .排长.排长的作用就是相当于execute,那么最终的班长相当于addedExecutionSignalsSubject

[command execute:@"命令"];

---

- (RACSignal *)execute:(id)input {
	// `immediateEnabled` is guaranteed to send a value upon subscription, so
	// -first is acceptable here.
	BOOL enabled = [[self.immediateEnabled first] boolValue];
	if (!enabled) {
		NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{
			NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil),
			RACUnderlyingCommandErrorKey: self
		}];

		return [RACSignal error:error];
	}

    // 当前的block
	RACSignal *signal = self.signalBlock(input);
	NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input);

	// We subscribe to the signal on the main thread so that it occurs _after_
	// -addActiveExecutionSignal: completes below.
	//
	// This means that `executing` and `enabled` will send updated values before
	// the signal actually starts performing work.
    // RAC关联者
    // 保证信号只能执行一次
	RACMulticastConnection *connection = [[signal
		subscribeOn:RACScheduler.mainThreadScheduler]
		multicast:[RACReplaySubject subject]];
	
    // subscribers
    // 遍历 --- subscriber --- sendNext
	[self.addedExecutionSignalsSubject sendNext:connection.signal];

    // 保证每个信号都能够传递
	[connection connect];
	return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, RACDescription(input)];
}

在这里插入图片描述

我们signalBlock跟踪block回到外部 block内部可以根据input的指令不同。操作其他事情。比如登录注册
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        // 执行2 输入命令
        NSLog(@"RACCommand createSignal  %@",input);
        
        // 登录 ---> 注册 ---- ?????
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"yy"];
            [subscriber sendCompleted];
            return  [RACDisposable disposableWithBlock:^{
                // 执行6
                NSLog(@"销毁了---");
            }];
        }];
    }];
    
    // 执行信号
    [command.executionSignals subscribeNext:^(id  _Nullable x) {
        // 执行4
        NSLog(@"RACCommand executionSignals  %@",x);

    }];

我们继续往signalblock下看
	// We subscribe to the signal on the main thread so that it occurs _after_
	// -addActiveExecutionSignal: completes below.
	//
	// This means that `executing` and `enabled` will send updated values before
	// the signal actually starts performing work.
    // RAC关联者
    // 保证信号只能执行一次
	RACMulticastConnection *connection = [[signal
		subscribeOn:RACScheduler.mainThreadScheduler]
		multicast:[RACReplaySubject subject]];
	
    // subscribers
    // 遍历 --- subscriber --- sendNext
	[self.addedExecutionSignalsSubject sendNext:connection.signal];

    // 保证每个信号都能够传递
	[connection connect];


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇夜iOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值