iOS开发基础72-ReactiveObjC :从核心概念到高频使用

ReactiveObjC 深度解析:从核心概念到高频使用与底层原理

ReactiveObjC(简称 RAC)是 GitHub 开源的函数响应式编程(FRP)框架,它将 UI 事件、KVO、通知、代理、网络请求等各种异步操作统一抽象为"信号流",让异步代码像同步代码一样链式书写,极大简化了状态管理和事件处理。本文将从最基础的概念讲起,用通俗易懂的方式带你掌握 RAC 的高频使用场景,并深入其底层原理。


一、ReactiveObjC 简介

1. 什么是 ReactiveObjC

ReactiveObjC 是函数响应式编程(Functional Reactive Programming, FRP)在 Objective-C 上的实现。它的核心思想是:把一切变化都看作数据流(信号),通过操作符对数据流进行变换、过滤、合并,最终订阅数据流来响应变化。

简单来说,传统的 iOS 开发中,我们要处理各种事件:

  • 按钮点击 → addTarget:action:
  • 文本变化 → delegate
  • 属性变化 → KVO
  • 通知 → NSNotificationCenter
  • 网络请求 → callback block

这些事件的处理方式各不相同,代码分散,状态管理混乱。RAC 把它们全部统一为信号(Signal),用同一套 API 处理:

// 按钮点击
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
    NSLog(@"按钮被点击了");
}];

// 文本框输入
[[self.textField rac_textSignal] subscribeNext:^(NSString *text) {
    NSLog(@"当前输入: %@", text);
}];

// KVO
[RACObserve(self.user, name) subscribeNext:^(NSString *name) {
    NSLog(@"用户名变化: %@", name);
}];

2. RAC 解决了什么问题

传统开发的痛点 RAC 的解决方案
事件处理方式不统一(target/delegate/KVO/通知/callback) 统一为信号,一套 API 处理所有事件
状态管理混乱,多个变量相互依赖 信号流自动传播变化,无需手动管理状态
异步回调嵌套(callback hell) 链式调用,代码扁平化
跨模块通信耦合 信号可以自由传递和组合,降低耦合
错误处理分散 信号统一处理 error 事件

3. 安装

通过 CocoaPods 安装:

pod 'ReactiveObjC', '~> 3.1'

导入头文件:

#import <ReactiveObjC/ReactiveObjC.h>

二、核心概念

1. 信号(RACSignal)

信号是 RAC 的核心,它代表一个"未来会产生一系列值的数据流"。信号本身不会主动发送值,只有当被订阅时才会开始发送。

信号可以发送三种事件:

  • next:发送一个新值(可以是任意对象)
  • error:发送一个错误,信号终止
  • completed:信号正常完成,终止
时间轴 ──────────────────────────────────▶
         next(1)   next(2)   next(3)   completed
            │          │          │          │
            ▼          ▼          ▼          ▼
订阅者收到:  1          2          3        结束

2. 订阅者(RACSubscriber)

订阅者是信号的接收者。当你调用 subscribeNext: 时,RAC 内部会创建一个订阅者,接收信号发送的 next/error/completed 事件。

3. 冷信号 vs 热信号

类型 特点 示例
冷信号(Cold Signal) 每次订阅都会重新开始发送值,不订阅就不发送 网络请求、文件读取、定时器(RAC 中大部分信号)
热信号(Hot Signal) 事件的产生与订阅无关,订阅者只能收到订阅之后的事件 UI 事件(按钮点击)、通知

大部分 RACSignal 是冷信号。UI 事件类的信号(如 rac_signalForControlEvents)虽然表现为热信号,但底层也是通过冷信号 + 共享订阅实现的。

4. RACStream(流)

RACStreamRACSignalRACSequence 的父类,定义了通用的流操作(如 map、filter、flattenMap 等)。可以理解为:RACSignal 是随时间推送值的流,RACSequence 是空间上排列值的流(类似数组)。


三、基础使用(高频场景)

1. 按钮点击事件

#import <ReactiveObjC/ReactiveObjC.h>

// 监听按钮点击
[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
 subscribeNext:^(UIButton *button) {
     NSLog(@"登录按钮被点击");
     [self login];
 }];

2. 文本框输入监听

// 监听文本框输入(每次输入变化都会触发)
[[self.usernameTextField rac_textSignal]
 subscribeNext:^(NSString *text) {
     NSLog(@"用户名: %@", text);
 }];

// 也可以用 rac_valuesForKeyPath:observer: 监听 text 属性

3. KVO(属性变化监听)

// RACObserve 是宏,等价于 [self.user rac_valuesForKeyPath:@"name" observer:self]
[RACObserve(self.user, name)
 subscribeNext:^(NSString *newName) {
     NSLog(@"用户名从旧值变为: %@", newName);
 }];

// 监听 self.view 的 frame 变化
[RACObserve(self.view, frame)
 subscribeNext:^(NSValue *frameValue) {
     NSLog(@"view frame: %@", frameValue);
 }];

RACObserve(TARGET, KEYPATH) 是 RAC 最常用的宏之一,它会自动管理 KVO 的注册和移除,无需手动 removeObserver。

4. 通知

// 监听 UIKeyboardWillShowNotification
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil]
 subscribeNext:^(NSNotification *notification) {
     NSDictionary *userInfo = notification.userInfo;
     NSValue *keyboardFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
     CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
     NSLog(@"键盘高度: %f", keyboardFrame.size.height);
 }];

RAC 的通知信号在订阅者释放时自动移除观察者,无需手动 removeObserver。

5. 代理方法

RAC 可以把 delegate 方法转化为信号。以 UITextFieldDelegate 为例:

// 监听 textFieldShouldReturn:(点击键盘 return 键)
[[self.textField rac_signalForSelector:@selector(textFieldShouldReturn:)]
 subscribeNext:^(RACTuple *tuple) {
     // tuple 中包含代理方法的参数
     UITextField *textField = tuple.first;
     NSLog(@"点击了 return 键: %@", textField.text);
     [textField resignFirstResponder];
 }];

rac_signalForSelector: 会自动把对象设置为自己的 delegate(如果对象遵循了对应协议),代理方法被调用时转化为信号发送值。参数打包在 RACTuple 中。

6. 定时器

// 每隔 1 秒发送一个值,从 0 开始递增
[[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]]
 subscribeNext:^(NSDate *date) {
     NSLog(@"定时触发: %@", date);
 }];

// 带自动计数的定时器(用 scan 累加)
[[[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]]
  scanWithStart:@(0) reduce:^NSNumber *(NSNumber *running, NSDate *date) {
      return @(running.integerValue + 1);
  }]
 subscribeNext:^(NSNumber *count) {
     NSLog(@"第 %@ 秒", count);
 }];

7. 手势识别

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[self.view addGestureRecognizer:tap];

[[tap rac_gestureSignal]
 subscribeNext:^(UITapGestureRecognizer *gesture) {
     CGPoint location = [gesture locationInView:self.view];
     NSLog(@"点击位置: %@", NSStringFromCGPoint(location));
 }];

四、RAC 宏(高频使用)

1. RACObserve(KVO 监听)

// 监听 self.user 的 name 属性变化
[RACObserve(self.user, name) subscribeNext:^(id x) { ... }];

2. RAC(绑定属性)

RAC(TARGET, KEYPATH) 宏把信号的值自动绑定到对象的属性上,信号每次发送 next,属性就自动更新:

// 把文本框的输入实时绑定到 label 的 text
RAC(self.nameLabel, text) = self.usernameTextField.rac_textSignal;

// 把用户名是否为空绑定到按钮的 enabled 属性
RAC(self.loginButton, enabled) = [RACSignal combineLatest:@[
    self.usernameTextField.rac_textSignal,
    self.passwordTextField.rac_textSignal
] reduce:^id(NSString *username, NSString *password) {
    return @(username.length > 0 && password.length > 0);
}];

RAC 宏是 RAC 最强大的特性之一,实现了数据的单向绑定,无需手动在回调中赋值。

3. @weakify / @strongify(避免循环引用)

RAC 中的 block 容易造成循环引用,RAC 提供了 @weakify@strongify 宏来安全处理:

@weakify(self);
[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
 subscribeNext:^(id x) {
     @strongify(self);
     if (!self) return;
     [self login];
 }];

@weakify(self) 把 self 转为 weak 引用,@strongify(self) 在 block 内重新转为 strong 引用(局部变量),避免循环引用,同时防止 self 在 block 执行过程中被释放。

4. RACCommand(命令封装)

RACCommand 封装了一个动作的执行,常用于按钮点击触发网络请求等场景,它可以自动管理按钮的 enabled 状态和执行状态:

// 创建一个命令,执行时返回一个信号(如网络请求)
self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
    NSLog(@"执行登录");
    return [self loginRequestSignal]; // 返回网络请求信号
}];

// 把命令绑定到按钮(点击按钮自动执行命令,执行期间按钮自动禁用)
self.loginButton.rac_command = self.loginCommand;

// 监听命令执行结果
[self.loginCommand.executionSignals.switchToLatest
 subscribeNext:^(id response) {
     NSLog(@"登录成功: %@", response);
 }];

// 监听错误
[self.loginCommand.errors subscribeNext:^(NSError *error) {
    NSLog(@"登录失败: %@", error);
}];

五、高阶操作符(高频使用)

操作符是 RAC 的灵魂,它们对信号流进行变换、过滤、合并。以下是最常用的操作符。

1. map(映射)

把信号发送的每个值转换为另一个值:

// 把文本输入映射为文本长度
[[self.textField.rac_textSignal
  map:^id(NSString *text) {
      return @(text.length);
  }]
 subscribeNext:^(NSNumber *length) {
     NSLog(@"文本长度: %@", length);
 }];

2. filter(过滤)

只让满足条件的值通过:

// 只接收长度大于 3 的输入
[[self.textField.rac_textSignal
  filter:^BOOL(NSString *text) {
      return text.length > 3;
  }]
 subscribeNext:^(NSString *text) {
     NSLog(@"有效输入: %@", text);
 }];

3. flattenMap(扁平映射)

map 返回的是值,flattenMap 返回的是信号,它会把内部信号的值"拍平"到外层信号中。最常用于网络请求链式调用

// 点击登录按钮 → 发起登录请求 → 登录成功后获取用户信息
[[[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
   flattenMap:^RACStream *(id value) {
       // 返回登录请求信号
       return [self loginRequestSignal];
   }]
  flattenMap:^RACStream *(id loginResponse) {
      // 登录成功后,返回获取用户信息的请求信号
      return [self fetchUserInfoSignal];
  }]
 subscribeNext:^(id userInfo) {
     NSLog(@"获取用户信息成功: %@", userInfo);
 } error:^(NSError *error) {
     NSLog(@"出错: %@", error);
 }];

flattenMap 是 RAC 中最重要的操作符之一,它解决了异步回调嵌套问题,让异步代码像同步代码一样链式书写。

4. combineLatest(合并最新值)

合并多个信号的最新值,当任意一个信号发送新值时,用所有信号的最新值组合发送:

// 用户名和密码都有值时,登录按钮才可用
RAC(self.loginButton, enabled) = [RACSignal
    combineLatest:@[self.usernameTextField.rac_textSignal,
                    self.passwordTextField.rac_textSignal]
    reduce:^id(NSString *username, NSString *password) {
        return @(username.length >= 3 && password.length >= 6);
    }];

combineLatest:reduce: 是表单验证的利器,多个输入字段的状态自动联动。

5. zip(压缩)

把多个信号的值按顺序一一配对,只有当所有信号都发送了第 N 个值时,才发送第 N 个组合值:

// 两个请求都完成后才触发
[[RACSignal zip:@[
    [self fetchUserInfoSignal],
    [self fetchConfigSignal]
]] subscribeNext:^(RACTuple *tuple) {
    id userInfo = tuple.first;
    id config = tuple.second;
    NSLog(@"两个请求都完成了");
}];

zipcombineLatest 的区别:zip 按顺序一一配对(第1个配第1个),combineLatest 总是用最新值组合。

6. merge(合并)

把多个信号合并为一个信号,任意一个信号发送值,合并后的信号就发送:

// 合并两个按钮的点击事件
RACSignal *merged = [RACSignal merge:@[
    [self.button1 rac_signalForControlEvents:UIControlEventTouchUpInside],
    [self.button2 rac_signalForControlEvents:UIControlEventTouchUpInside]
]];

[merged subscribeNext:^(UIButton *button) {
    NSLog(@"点击了按钮: %@", button);
}];

7. take / skip(取前 N 个 / 跳过前 N 个)

// 只取前 3 次输入
[[self.textField.rac_textSignal take:3]
 subscribeNext:^(NSString *text) {
     NSLog(@"前3次输入: %@", text);
 }];

// 跳过前 2 次,从第 3 次开始
[[self.textField.rac_textSignal skip:2]
 subscribeNext:^(NSString *text) {
     NSLog(@"从第3次开始: %@", text);
 }];

8. throttle / debounce(节流 / 防抖)

  • throttle::在指定时间间隔内,只发送最新的值(忽略中间值)。
  • delay: 是延迟发送,不是防抖。RAC 中防抖常用 throttle: 实现。
// 搜索框防抖:用户停止输入 0.5 秒后才触发搜索
[[[self.searchTextField.rac_textSignal
   throttle:0.5]
  flattenMap:^RACStream *(NSString *keyword) {
      return [self searchSignalWithKeyword:keyword];
  }]
 subscribeNext:^(id results) {
     NSLog(@"搜索结果: %@", results);
 }];

防抖是搜索框的经典优化,避免用户每输入一个字就发一次请求。

9. distinctUntilChanged(去重)

只有当新值与上一个值不同时才发送:

// 忽略重复的输入(如连续输入相同内容)
[[self.textField.rac_textSignal distinctUntilChanged]
 subscribeNext:^(NSString *text) {
     NSLog(@"变化后的输入: %@", text);
 }];

10. switchToLatest(切换到最新)

用于信号的信号(signal of signals),只订阅最新的内部信号,取消之前的订阅:

// 每次点击搜索按钮,发起新请求;如果新请求发起时旧请求还没完成,自动取消旧请求
[[[[self.searchButton rac_signalForControlEvents:UIControlEventTouchUpInside]
   map:^id(id value) {
       return [self searchSignal]; // 返回请求信号
   }]
  switchToLatest]
 subscribeNext:^(id results) {
     NSLog(@"最新搜索结果: %@", results);
 }];

switchToLatest 常用于搜索场景,自动取消过期的旧请求,只保留最新请求的结果。


六、实战场景

1. 登录表单完整逻辑

- (void)setupLoginLogic {
    @weakify(self);
    
    // 1. 用户名密码都有效时,登录按钮才可点击
    RAC(self.loginButton, enabled) = [RACSignal
        combineLatest:@[self.usernameField.rac_textSignal,
                        self.passwordField.rac_textSignal]
        reduce:^id(NSString *username, NSString *password) {
            return @(username.length >= 3 && password.length >= 6);
        }];
    
    // 2. 登录命令
    self.loginCommand = [[RACCommand alloc] initWithEnabled:self.loginButton.rac_enabled
                                                  signalBlock:^RACSignal *(id input) {
        @strongify(self);
        return [self loginRequestSignal];
    }];
    
    self.loginButton.rac_command = self.loginCommand;
    
    // 3. 监听登录结果
    [self.loginCommand.executionSignals.switchToLatest
     subscribeNext:^(id response) {
         @strongify(self);
         NSLog(@"登录成功");
         [self.navigationController popViewControllerAnimated:YES];
     }];
    
    [self.loginCommand.errors
     subscribeNext:^(NSError *error) {
         NSLog(@"登录失败: %@", error.localizedDescription);
     }];
    
    // 4. 执行中显示 loading
    RAC(self, isLoading) = self.loginCommand.executing;
}

2. 搜索框防抖 + 自动取消旧请求

- (void)setupSearchLogic {
    @weakify(self);
    
    [[[[self.searchField.rac_textSignal
       // 跳过初始空值
       skip:1]
      // 防抖 0.5 秒
      throttle:0.5]
     // 去重
     distinctUntilChanged]
     subscribeNext:^(NSString *keyword) {
         @strongify(self);
         [self searchWithKeyword:keyword];
     }];
}

3. 网络请求链式调用(登录 → 获取用户信息 → 获取配置)

- (void)loginAndFetchData {
    @weakify(self);
    
    [[[[self loginSignal]
       flattenMap:^RACStream *(id loginResponse) {
           @strongify(self);
           return [self fetchUserInfoSignal];
       }]
      flattenMap:^RACStream *(id userInfo) {
          @strongify(self);
          return [self fetchConfigSignal];
      }]
     subscribeNext:^(id config) {
         NSLog(@"全部数据加载完成");
     } error:^(NSError *error) {
         NSLog(@"出错: %@", error);
     }];
}

七、底层原理

1. RACSignal 的本质

RACSignal 是一个对象,它的核心是一个 didSubscribe block。当信号被订阅时,这个 block 被执行,block 内部可以向订阅者发送 next/error/completed 事件。

简化的核心结构:

@interface RACSignal ()
@property (nonatomic, copy) void (^didSubscribe)(id<RACSubscriber> subscriber);
@end

@implementation RACSignal

+ (RACSignal *)createSignal:(void (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACSignal *signal = [[RACSignal alloc] init];
    signal.didSubscribe = didSubscribe;
    return signal;
}

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    // 订阅时执行 didSubscribe block
    self.didSubscribe(subscriber);
    // 返回 disposable,用于取消订阅
    return [RACDisposable disposableWithBlock:^{
        // 清理工作
    }];
}

@end

2. 订阅流程

// 1. 创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    // 3. 订阅时执行这里
    [subscriber sendNext:@"hello"];
    [subscriber sendNext:@"world"];
    [subscriber sendCompleted];
    return nil;
}];

// 2. 订阅信号
[signal subscribeNext:^(id x) {
    NSLog(@"收到: %@", x);
}];

执行流程:

  1. createSignal: 创建信号对象,保存 didSubscribe block,不执行任何操作
  2. subscribeNext: 创建订阅者,调用 [signal subscribe:subscriber]
  3. subscribe: 内部执行保存的 didSubscribe block,传入订阅者。
  4. didSubscribe block 中调用 [subscriber sendNext:],触发订阅者的 next block。
  5. sendCompleted 后,信号终止,自动清理资源。

3. 操作符的实现原理

操作符(如 map、filter、flattenMap)本质上是创建一个新的信号,在新信号的 didSubscribe 中订阅原信号,并在原信号的事件中做变换后转发给新信号的订阅者。

map 为例的简化实现:

- (RACSignal *)map:(id (^)(id value))block {
    // 创建一个新信号
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 在新信号的 didSubscribe 中,订阅原信号(self)
        return [self subscribeNext:^(id x) {
            // 原信号发送值时,用 block 变换后转发给新信号的订阅者
            id transformed = block(x);
            [subscriber sendNext:transformed];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            [subscriber sendCompleted];
        }];
    }];
}

这就是为什么操作符可以链式调用:每个操作符都返回一个新的 RACSignal,新信号又可以继续调用操作符。

4. 冷信号的惰性执行

RACSignal 是冷信号,意味着:

  • 创建信号时不执行任何操作(不发请求、不计时)。
  • 只有被订阅时才开始执行(发请求、开始计时)。
  • 每次订阅都会重新执行(如网络请求信号,订阅两次就发两次请求)。
// 创建信号时不发请求
RACSignal *requestSignal = [self networkRequestSignal];

// 第一次订阅 → 发第一次请求
[requestSignal subscribeNext:^(id x) { ... }];

// 第二次订阅 → 发第二次请求
[requestSignal subscribeNext:^(id x) { ... }];

如果希望多个订阅者共享一次执行(热信号),可以用 multicast:replay 操作符。

5. 内存管理(自动取消订阅)

RAC 的内存管理设计精巧:

  • 信号被订阅者持有(订阅者在订阅时持有信号的引用)。
  • 信号在 didSubscribe 中持有订阅者(通过 block 捕获)。
  • 这形成了一个循环引用,但 RAC 在信号完成(completed)或错误(error)时,会自动断开这个循环引用,释放资源。
  • 如果信号永远不完成(如 UI 事件信号),订阅者会一直持有信号,直到订阅者自己被释放(此时 disposable 被取消,循环引用断开)。
// 订阅返回的 disposable 可以手动取消
RACDisposable *disposable = [signal subscribeNext:^(id x) { ... }];

// 手动取消订阅(如页面消失时)
[disposable dispose];

6. RACScheduler(调度器)

RACScheduler 是 RAC 的线程调度器,类似 GCD 的队列。常用的调度器:

调度器 说明
+[RACScheduler mainThreadScheduler] 主线程调度器,用于 UI 更新
+[RACScheduler currentScheduler] 当前线程调度器
+[RACScheduler scheduler] 创建一个后台串行队列调度器
+[RACScheduler immediateScheduler] 立即执行(不切换线程)

subscribeOn:deliverOn: 控制线程:

[[[networkRequestSignal
    subscribeOn:[RACScheduler scheduler]] // 在后台线程执行请求
   deliverOn:[RACScheduler mainThreadScheduler]] // 结果回主线程
  subscribeNext:^(id response) {
      // 主线程更新 UI
      self.label.text = response;
  }];

八、优缺点与注意事项

1. 优点

  • 统一事件处理:UI 事件、KVO、通知、代理、网络请求全部统一为信号,一套 API。
  • 链式调用:异步代码扁平化,避免回调嵌套(callback hell)。
  • 数据绑定:RAC 宏实现属性自动绑定,减少手动赋值代码。
  • 组合能力强:combineLatest、zip、merge 等操作符轻松组合多个异步流。
  • 自动内存管理:信号完成后自动释放,KVO/通知自动移除观察者。

2. 缺点

  • 学习曲线陡峭:函数响应式编程范式与传统命令式编程差异大,操作符多,入门难。
  • 调试困难:信号流是异步的、链式的,断点调试不直观,出错时难以定位。
  • 性能开销:信号和操作符会创建大量对象,高频场景(如滚动)可能有性能问题。
  • 代码可读性:不熟悉 RAC 的人难以读懂链式调用代码。
  • OC 版本维护减少:ReactiveObjC 已进入维护模式,新项目可考虑 Swift 的 Combine 或 RxSwift。

3. 注意事项

  • 循环引用:block 中引用 self 必须用 @weakify/@strongify
  • 冷信号重复执行:网络请求信号被多次订阅会发多次请求,注意共享或缓存。
  • 信号必须完成或被取消:否则可能造成内存泄漏。
  • 线程切换:UI 更新必须在主线程,用 deliverOn:mainThreadScheduler
  • 避免过度使用:简单的事件处理用传统方式即可,RAC 适合复杂的异步流组合场景。

九、Swift 对应框架

概念 ReactiveObjC(OC) RxSwift(Swift) Combine(Apple 原生,iOS 13+)
信号 RACSignal Observable Publisher
订阅者 RACSubscriber Observer / Subscriber Subscriber
订阅 subscribeNext: subscribe(onNext:) sink(receiveValue:)
映射 map: map map
扁平映射 flattenMap: flatMap flatMap
合并 combineLatest: combineLatest combineLatest
压缩 zip: zip zip
防抖 throttle: debounce / throttle debounce / throttle
KVO RACObserve - @Published / observe
调度器 RACScheduler Scheduler Scheduler / DispatchQueue

如果你熟悉了 ReactiveObjC 的概念,学习 RxSwift 或 Combine 会非常轻松,核心思想完全一致,只是 API 名称不同。


十、总结

  • ReactiveObjC 是什么:函数响应式编程框架,把一切异步事件(UI 事件、KVO、通知、代理、网络请求)统一抽象为信号(RACSignal),通过操作符变换、过滤、合并信号流,订阅信号来响应变化。
  • 核心概念:信号(RACSignal,发送 next/error/completed)、订阅者(RACSubscriber)、冷信号(订阅才执行,每次订阅重新执行)、热信号(事件与订阅无关)、RACStream(流的父类)。
  • 高频基础使用:按钮点击 rac_signalForControlEvents、文本输入 rac_textSignal、KVO RACObserve、通知 rac_addObserverForName、代理 rac_signalForSelector、定时器 RACSignal interval:、手势 rac_gestureSignal。
  • 高频宏:RACObserve(KVO)、RAC(属性绑定)、@weakify/@strongify(防循环引用)、RACCommand(命令封装,自动管理 enabled 和执行状态)。
  • 高频操作符:map(映射)、filter(过滤)、flattenMap(扁平映射,异步链式调用核心)、combineLatest(合并最新值,表单验证)、zip(按序压缩)、merge(合并)、take/skip(取/跳)、throttle(防抖)、distinctUntilChanged(去重)、switchToLatest(切换最新,自动取消旧请求)。
  • 底层原理:RACSignal 核心是 didSubscribe block,订阅时执行;操作符创建新信号,在新信号的 didSubscribe 中订阅原信号并变换转发;冷信号惰性执行;信号完成/错误时自动断开循环引用释放资源;RACScheduler 管理线程调度。
  • 实战场景:登录表单(combineLatest 验证 + RACCommand 执行)、搜索框防抖(throttle + switchToLatest)、网络请求链式调用(flattenMap 嵌套)。
  • 选型建议:ReactiveObjC 适合复杂异步流组合、多状态联动的场景;简单事件用传统方式即可;新项目可考虑 Swift 的 Combine(Apple 原生)或 RxSwift。

posted @ 2015-10-29 03:29  Mr.陳  阅读(1545)  评论(2)    收藏  举报