iOS开发基础111-RAC
1.iOS开发基础77-一像素线的几种实现方式2.iOS开发基础78-iOS 国际化3.iOS开发基础82-关于iOS目录4.iOS开发基础81-Runtime实战5.iOS开发基础80-关于Xcode86.iOS开发基础79-强制退出程序7.iOS开发基础90-密码学8.iOS开发基础89-Runloop9.iOS开发基础88-涂鸦效果10.iOS开发基础87-抽屉效果11.iOS开发基础86-FMDB12.iOS开发基础85-线程dispatch apply13.iOS开发基础84-HTTP请求方法详解与增删改查的应用14.iOS开发基础83-线程组15.iOS开发基础106-Instruments16.iOS开发基础105-Xcode收集Crashs的各种方法17.iOS开发基础104-正向代理和反向代理18.iOS开发基础103-APP之间跳转19.iOS开发基础102-后台保活方案20.iOS开发基础101-指纹和面部识别21.iOS开发基础100-MDM证书申请流程22.iOS开发基础99-iOS 内购的防范与优化23.iOS开发基础98-跳转淘宝案例24.iOS开发基础97-应用内购(In-App Purchase)的安全性解析与收据处理流程25.iOS开发基础96-UI类继承关系图26.iOS开发基础95-程序内评价27.iOS开发基础94-xcode1028.iOS开发基础93-GCD死锁29.iOS开发基础92-线程保活30.iOS开发基础91-线程同步技术与资源共享详解31.iOS开发基础138-视频编码32.iOS开发基础137-音视频编解码简介33.iOS开发基础136-防暴力点击34.iOS开发基础135-Core Data35.iOS开发基础134-异步并行上传问题36.iOS开发基础133-崩溃预防37.iOS开发基础132-POSIX线程库38.iOS开发基础131-isa指针39.iOS开发基础130-视频录制上传40.iOS开发基础129-音频录制上传41.iOS开发基础128-应用本地化42.iOS开发基础127-深入探讨KVO43.iOS开发基础126-深入探索设计模式44.iOS开发基础125-深入探索SDWebImage45.iOS开发基础124-RunLoop实现卡顿检测46.iOS开发基础123-自动释放池原理47.iOS开发基础122-RunLoop48.iOS开发基础121-APP启动优化49.iOS开发基础120-通知与线程50.iOS开发基础119-组件化51.iOS开发基础118-Runtime52.iOS开发基础117-Hybrid53.iOS开发基础116-性能监控54.iOS开发基础115-Socket55.iOS开发基础114-YYCache56.iOS开发基础113-Unity3D57.iOS开发基础112-GCD常见场景
58.iOS开发基础111-RAC
59.iOS开发基础110-Core Graphics应用场景60.iOS开发基础109-网络安全61.iOS开发基础108-常见的编程范式62.iOS开发基础107-iOS直播63.iOS开发基础148-ABM vs MDM64.iOS开发基础147-ABM集中管理Apple设备65.iOS开发基础146-深入解析WKWebView66.iOS开发基础145-Apple Search Ads67.iOS开发基础144-逐字打印效果68.iOS开发基础143-性能优化69.iOS开发基础142-广告归因70.iOS开发基础141-音频解码71.iOS开发基础140-音频编码72.iOS开发基础139-视频解码73.iOS开发基础149-由UUIDString引发的思考ReactiveCocoa(RAC)是一个基于函数响应式编程(FRP)的框架,广泛用于iOS开发中。其核心思想是通过流和信号(signal)来处理多变、复杂的事件。以下是ReactiveCocoa常见的一些用法场景,并深入解析其原理。
1. 响应用户输入
场景:表单验证
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
RACSignal *validUsernameSignal = [self.usernameTextField.rac_textSignal
map:^id _Nullable(NSString * _Nullable username) {
return @(username.length > 3);
}];
RACSignal *validPasswordSignal = [self.passwordTextField.rac_textSignal
map:^id _Nullable(NSString * _Nullable password) {
return @(password.length > 3);
}];
RACSignal *formValidSignal = [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id (NSNumber *usernameValid, NSNumber *passwordValid){
return @([usernameValid boolValue] && [passwordValid boolValue]);
}];
[formValidSignal subscribeNext:^(NSNumber *formValid) {
self.loginButton.enabled = [formValid boolValue];
}];
原理解析:
rac_textSignal
将UITextField的文字变化转化为信号。map:
操作符将输入的字符串转化为有效性的布尔值(长度大于3)。combineLatest:reduce:
将多个信号结合成一个新的信号,并通过reduce
块生成最终结果。subscribeNext:
订阅信号,且在信号值变化时对登录按钮进行启用/禁用。
2. 数据绑定
场景:双向绑定ViewModel和View
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
RAC(self.viewModel, username) = self.usernameTextField.rac_textSignal;
RAC(self.usernameTextField, text) = RACObserve(self.viewModel, username);
原理解析:
RAC(target, keyPath)
将信号绑定到ViewModel的属性。RACObserve
创建一个观察信号,当viewModel.username
变化时,更新UITextField的text。- 双向绑定确保View和ViewModel的属性同步变化。
3. 处理异步操作
场景:网络请求
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
- (RACSignal *)fetchDataFromURL:(NSURL *)url {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[subscriber sendError:error];
} else {
[subscriber sendNext:data];
[subscriber sendCompleted];
}
}];
[task resume];
return [RACDisposable disposableWithBlock:^{
[task cancel];
}];
}];
}
[[self fetchDataFromURL:url] subscribeNext:^(NSData *data) {
// Process data
} error:^(NSError *error) {
// Handle error
}];
原理解析:
createSignal:
创建一个信号并手动控制其数据(如下一步、完成、错误)流的发送。- 在信号内部,执行异步操作(例如网络请求),并在操作完成时发送事件(数据、完成、错误)。
subscribeNext:error:
对信号的成功和失败情况进行订阅和处理。
4. 信号组合和变换
场景:信号合并和过滤
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
// 合并信号
RACSignal *mergedSignal = [RACSignal merge:@[self.signalA, self.signalB]];
[mergedSignal subscribeNext:^(id _Nullable x) {
NSLog(@"Received: %@", x);
}];
// 过滤信号
RACSignal *filteredSignal = [self.inputSignal filter:^BOOL(id _Nullable value) {
return [value length] > 3;
}];
[filteredSignal subscribeNext:^(id _Nullable x) {
NSLog(@"Filtered value: %@", x);
}];
原理解析:
merge:
将多个信号合并成一个信号,同时订阅多个信号的所有事件。filter:
只允许满足条件的事件通过,其他事件将被过滤掉。
5. 基于时间的操作
场景:延迟、定时器、节流
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
// 延迟操作
[[RACSignal return:@"Hello, World!"] delay:2.0];
// 定时器
RACSignal *timerSignal = [RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]];
[timerSignal subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"Timer tick: %@", x);
}];
// 节流操作
RACSignal *throttledSignal = [[self.usernameTextField.rac_textSignal throttle:0.5];
[throttledSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"Throttled value: %@", x);
}];
原理解析:
delay:
将信号的所有事件延迟指定的时间发送。interval:
创建一个定时器信号,每隔指定的时间发出一个事件。throttle:
在指定的时间内只发送最新的一次事件,防止高频率触发。
6. 错误处理
场景:网络请求错误与重试机制
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
- (RACSignal *)fetchDataFromURL:(NSURL *)url {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[subscriber sendError:error];
} else {
[subscriber sendNext:data];
[subscriber sendCompleted];
}
}];
[task resume];
return [RACDisposable disposableWithBlock:^{
[task cancel];
}];
}];
}
[[[self fetchDataFromURL:url] retry:3] subscribeNext:^(NSData *data) {
// Process data
} error:^(NSError *error) {
// Handle error
}];
原理解析:
retry:
在信号遇到错误时重新订阅信号,最多重试指定的次数。- 使用
sendError:
,sendNext:
,sendCompleted:
手动控制信号的错误和数据发送。
7. 依赖信号
场景:顺序执行多个异步任务
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
- (RACSignal *)loginSignal:(NSString *)username password:(NSString *)password {
// 登录 signal implementation
}
- (RACSignal *)fetchUserProfileSignal {
// 获取用户Profile的信号
}
[[[self loginSignal:@"user" password:@"pass"]
flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
return [self fetchUserProfileSignal];
}]
subscribeNext:^(id _Nullable x) {
NSLog(@"User Profile: %@", x);
} error:^(NSError * _Nullable error) {
NSLog(@"Error: %@", error);
}];
原理解析:
flattenMap:
将第一个信号的值映射为另一个信号,并订阅这个新信号。这通常用于依赖关系,即在一个任务完成后启动另一个任务。- 在登录成功后,使用
flattenMap:
触发获取用户Profile的信号。
8. 控件事件处理
场景:按钮点击事件
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *myButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[self.myButton rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *button) {
NSLog(@"Button clicked");
}];
}
@end
原理解析:
rac_signalForControlEvents:
将UI控件的事件(例如按钮的点击)转化为信号。- 通过
subscribeNext:
订阅该信号,以处理点击事件。
9. 定时操作
场景:倒计时功能
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *countdownLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block int remainingTime = 60;
RACSignal *countdownSignal = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]]
take:remainingTime];
[countdownSignal subscribeNext:^(NSDate * _Nullable x) {
self.countdownLabel.text = [NSString stringWithFormat:@"%d", remainingTime];
remainingTime--;
} completed:^{
self.countdownLabel.text = @"Time's up!";
}];
}
@end
原理解析:
interval:onScheduler:
创建一个每秒发出事件的信号。take:
指定信号发送的事件次数。- 通过
subscribeNext:
更新界面。
10. 表格视图刷新
场景:下拉刷新
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
#import <MJRefresh/MJRefresh.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[[self fetchData] subscribeNext:^(NSArray *data) {
// Update table view with new data
[self.tableView reloadData];
[self.tableView.mj_header endRefreshing];
}];
}];
}
- (RACSignal *)fetchData {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// Simulate network request
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSArray *data = @[@"Item 1", @"Item 2", @"Item 3"];
[subscriber sendNext:data];
[subscriber sendCompleted];
});
return nil;
}];
}
@end
原理解析:
- 使用第三方库MJRefresh添加下拉刷新控件。
- 在刷新时,发起数据请求的信号并更新表格视图数据。
- 在订阅数据之后结束刷新状态。
11. 链式反应
场景:密码和确认密码验证
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
RACSignal *validPasswordSignal = [self.passwordTextField.rac_textSignal
map:^id _Nullable(NSString * _Nullable password) {
return @(password.length > 3);
}];
RACSignal *validConfirmPasswordSignal = [RACSignal
combineLatest:@[self.passwordTextField.rac_textSignal, self.confirmPasswordTextField.rac_textSignal]
reduce:^id (NSString *password, NSString *confirmPassword) {
return @(password.length > 3 && [password isEqualToString:confirmPassword]);
}];
[[RACSignal combineLatest:@[validPasswordSignal, validConfirmPasswordSignal]
reduce:^id (NSNumber *passwordValid, NSNumber *confirmPasswordValid){
return @([passwordValid boolValue] && [confirmPasswordValid boolValue]);
}]
subscribeNext:^(NSNumber *formValid) {
self.registerButton.enabled = [formValid boolValue];
}];
原理解析:
rac_textSignal
处理密码和确认密码输入框的文本变化。combineLatest:reduce:
将两个信号结合,并在两个信号联动时验证密码和确认密码。- 通过组合后的信号来控制注册按钮是否可用。
12. 进度条更新
场景:文件下载进度
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
- (RACSignal *)downloadFileWithProgress {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"https://example.com/file.zip"];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[subscriber sendError:error];
} else {
[subscriber sendCompleted];
}
}];
[task resume];
return [RACDisposable disposableWithBlock:^{
[task cancel];
}];
}];
}
[[self downloadFileWithProgress] subscribeCompleted:^{
self.progressView.progress = 1.0;
} error:^(NSError * _Nullable error) {
NSLog(@"Download failed: %@", error);
}];
[RACSignal interval:0.1 onScheduler:[RACScheduler mainThreadScheduler]]
subscribeNext:^(NSDate * _Nullable x) {
self.progressView.progress = (arc4random_uniform(100) / 100.0);
}];
原理解析:
createSignal:
进行文件下载,并在下载完成时发送完成事件。- 创建一个定时信号来模拟下载进度,并实时更新UI。
13. 监听值变化
场景:属性绑定
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
@interface MyViewModel : NSObject
@property (nonatomic, strong) NSString *name;
@end
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (nonatomic, strong) MyViewModel *viewModel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.viewModel = [[MyViewModel alloc] init];
RAC(self.nameLabel, text) = RACObserve(self.viewModel, name);
self.viewModel.name = @"Initial name";
}
@end
原理解析:
RACObserve
监听ViewModel属性变化。- 使用
RAC(target, keyPath)
绑定属性值到UILabel的text属性。
14. 动态计算属性
场景:计算两数之和
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *firstNumberTextField;
@property (weak, nonatomic) IBOutlet UITextField *secondNumberTextField;
@property (weak, nonatomic) IBOutlet UILabel *sumLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *firstNumberSignal = [self.firstNumberTextField.rac_textSignal map:^id _Nullable(NSString * _Nullable text) {
return @([text integerValue]);
}];
RACSignal *secondNumberSignal = [self.secondNumberTextField.rac_textSignal map:^id _Nullable(NSString * _Nullable text) {
return @([text integerValue]);
}];
RACSignal *sumSignal = [RACSignal combineLatest:@[firstNumberSignal, secondNumberSignal] reduce:^id (NSNumber *firstNumber, NSNumber *secondNumber) {
return @([firstNumber integerValue] + [secondNumber integerValue]);
}];
RAC(self.sumLabel, text) = [sumSignal map:^id _Nullable(NSNumber * _Nullable sum) {
return [sum stringValue];
}];
}
@end
原理解析:
map:
将文本转化为数字。combineLatest:reduce:
计算两数之和。- 使用
RAC(target, keyPath)
绑定结果到UILabel。
15. 处理通知
场景:监听键盘弹出和隐藏
示例代码:
#import <ReactiveObjC/ReactiveObjC.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *keyboardShowSignal = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] takeUntil:self.rac_willDeallocSignal];
RACSignal *keyboardHideSignal = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillHideNotification object:nil] takeUntil:self.rac_willDeallocSignal];
RACSignal *keyboardFrameSignal = [RACSignal merge:@[keyboardShowSignal, keyboardHideSignal]]
.map(^id (NSNotification *notification) {
return [notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
}];
[keyboardFrameSignal subscribeNext:^(NSValue *keyboardFrame) {
CGRect frame = keyboardFrame.CGRectValue;
self.bottomConstraint.constant = frame.size.height;
[UIView animateWithDuration:0.25 animations:^{
[self.view layoutIfNeeded];
}];
}];
}
@end
原理解析:
rac_addObserverForName:
监听键盘显示和隐藏通知。map:
将通知转化为键盘的frame值。- 通过订阅信号动态调整底部约束。
这些场景展示了ReactiveCocoa在iOS开发中的多种应用,通过信号和操作符,能够简化异步操作和事件处理,使代码更加简洁和可维护。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2015-07-17 iOS开发基础10-UIButton内边距和图片拉伸模式
2015-07-17 iOS开发基础9-提示框(UIAlertController)