Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
239 lines (192 sloc) 7.58 KB

#代码中 Signal 的文档描述约定

##基础格式

HotSignal<T, E>   // or ColdSignal<T, E>
Completion: ...
Error: ...
Scheduler: ...
Multicast: ...

...

##关键字解释

  • HotSignal And ColdSignal:
    • 'HotSignal': Signal 已经处于活动状态(activated);
    • 'ColdSignal': Signal 需要订阅(subscribed)才会活动(activate);
  • T: Signal sendNext 的类型, 可以下面几种情况:
    • T: 表示只会发送 1 次 next 事件, 内容是类型 T 的实例;
    • T?: 表示只会发送 1 次 next 事件, 内容是类型 T 的实例或者 nil;
    • [T]: 表示会发送 0 到 n 次 next 事件, 内容是类型 T 的实例;
    • [T?]: 表示会发送 0 到 n 次 next 事件, 内容是类型 T 的实例或者 nil;
    • None: 表示不会发送 next 事件;
  • E: Signal sendError 的类型, 通常是 NSErrorNoError; NoError 表示 Signal 不会 sendError;
  • Completion: 描述什么情况 sendCompleted;
    • 如果使用者无需关心 Signal 的 Completed 事件, 这一行可以不写;
    • 如果 next 事件的发送次数是 无穷多次,相当于使用者永远也接收不到 Completed 事件,所以这一行也没有必要写;
  • Error: 描述什么情况 sendError; 如果 Signal 不会 sendError, 这一行可以不写;
  • Scheduler: Signal 所在的线程,通常是 main specified current, 默认是 current
    • main 模块内部的pipeline有切换不同的scheduler,所以模块内部有责任确保最终的signal始终是在main schedular上的
    • specified 模块内部自定义了一个任务队列,模块会确保最终返回的signal都在这个特定的schedular中(或者是使用全局默认的后台schedular)
    • current 模块内部pipeline没有做任何scheduler的切换,且不指定特定的schedular,所以最终返回的signal和外部调用者的线程保持一致
  • Multicast: 是否广播,通常是 YES NO, 默认是 NO

##所有可能出现的有意义的非嵌套 Signal

*    HotSignal<T, NoError>
*    HotSignal<T?, NoError>
*    HotSignal<[T], NoError>
*    HotSignal<[T?], NoError>
*    HotSignal<None, NoError>
*    HotSignal<T, NSError>
*    HotSignal<T?, NSError>
*    HotSignal<[T], NSError>
*    HotSignal<[T?], NSError>
*    HotSignal<None, NSError>

*    ColdSignal<T, NoError>
*    ColdSignal<T?, NoError>
*    ColdSignal<[T], NoError>
*    ColdSignal<[T?], NoError>
*    ColdSignal<None, NoError>
*    ColdSignal<T, NSError>
*    ColdSignal<T?, NSError>
*    ColdSignal<[T], NSError>
*    ColdSignal<[T?], NSError>
*    ColdSignal<None, NSError>

##e.g.

    用户登录接口: 登录成功后 `sendNext` `User` 实例, 然后 `sendCompleted`; 出错则 `sendError`;

    /*
    ColdSignal<User, NSError>
    Completion: 登陆成功;
    Error: 网络错误或数据错误;
    Scheduler: specified;
    Multicast: YES;
    */
    - (RACSignal *)loginWithUsername:(NSString *)username password:(NSString *)password;
    获取列表

    /*
     ColdSignal<ZTPTopMovies, NSError>
     Completion: 通过http获取topMovies数据,并且解析数据成功结束;
     Error: 网络错误 或 MTLJSONAdapter解析数据错误;
     Scheduler: specified;
     Multicast: YES;
     */

    - (RACSignal *)getTopMovies;
    RACCommand的错误通知接口: 会发送 0 到 n 次 next 事件, 内容是 `NSError` 实例;
    Signal 本身不会 `sendError`;

    /*
    HotSignal<[NSError], NoError>
    Scheduler: main;
    Multicast: YES;
    */
    - (RACSignal *)errors;
    用于显示的文本信息, 文本可变也可能为空

    /*
    ColdSignal<[NSString?], NoError>
    Scheduler: main;
    Multicast: NO;
    */
    - (RACSignal *)textSignal;
    定位当前位置接口: 会发送多次当前位置信息(CLLocation 实例);
    可能会出错; 调用 `cancelLocating` 或获得了准确位置后 `sendCompleted`;

    /*
    HotSignal<[CLLocation], NSError>
    Completion: 获得准确位置或者调用了 -cancelLocating;
    Error: 定位出错, 例如没有权限;
    Scheduler: main;
    Multicast: YES;
    */
    - (RACSignal *)currentLocation;
    - (void)cancelLocating;
    UIButton 触发的 RACCommand;

    /*
     ColdSignal<[Signal<NSString, NoError>], NoError>  //这一行可省略,RACCommand 外层的 Signal 都是这种风格

     RACCommand input: _ // _ 表示 command 会忽略 input;

     RACCommand.executionSignals inner Signal      //这一行也可以省略
     Signal<NSString, NoError>
     Scheduler: main;
     Multicast: NO;
     */
     @property (strong, nonatomic, readonly) RACCommand *showPopupBtnCommand;
    Associate View 触发的 RACCommand;

    /*
     RACCommand input: NSString

     ColdSignal<NSString, NoError>
     Scheduler: main;
     Multicast: NO;

     businessLogic2 is triggered by showPopupBtnCommand --> alertView
     Associate View trigger this command, so UI need call [self.viewModel.businessLogic2Command execute:@"XXX"]
     */
    @property (strong, nonatomic, readonly) RACCommand *businessLogic2Command;
    用于 tableView 下拉刷新的 RACCommand  (RACCommand.executionSignals 相当于 Signal of Signals):
    Command 每次执行则发送一次 next 事件, 内容是刷新并且获取新数据的 Signal;
    `executionSignals` 本身不会发送 error 事件;
    error 事件被汇聚到 RACCommand.errors 上了;

    /*
     ColdSignal<[Signal<NSString, NoError>], NoError>    //这一行可省略,RACCommand 外层的 Signal 都是这种风格

     RACCommand input: _

     RACCommand.executionSignals inner Signal        //这一行也可以省略
     Signal<None, NSError>
     Completion: refresh结束;
     Error: 网络错误 或 MTLJSONAdapter解析数据错误;
     Scheduler: main;
     Multicast: YES;

     Associate View trigger this command
     */
    @property (strong, nonatomic, readonly) RACCommand *refreshTopMoviesCommand;
    tableViewCell 被选中时的 RACCommand;

    /*
     RACCommand input: NSIndexPath

     ColdSignal<ZTPMovieItem, NoError>
     Scheduler: main;
     Multicast: NO;

     Associate View trigger this command
     */
    @property (strong, nonatomic, readonly) RACCommand *didSelectRowCommand;
    didSelectRowCommand 被触发的时候,链式执行的 RACCommand;

    /*
     RACCommand input: ZTPMovieItem

     ColdSignal<NSString, NoError>
     Scheduler: main;
     Multicast: NO;

     viewModel self trigger this command

     viewModel 中的代码片段如下
     - (void)initPipline {
          self.didSelectMovieCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(ZTPMovieItem *input) {
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
              [subscriber sendNext:[input.title copy]];
              //here can send a subViewModel, e.g. MovieDetailViewModel
              [subscriber sendCompleted];

              return nil;
            }];
          }];


          @weakify(self);
          [[self.topMoviesContainerViewViewModel.didSelectRowCommand.executionSignals switchToLatest]
           subscribeNext:^(ZTPMovieItem *movieItem) {
             DDLogVerbose(@"%s, %d, movieItem is: %@", __PRETTY_FUNCTION__, __LINE__, movieItem);

             @strongify(self);
             [self.didSelectMovieCommand execute:movieItem];
           }];
    }
     */
    @property (strong, nonatomic, readonly) RACCommand *didSelectMovieCommand;