介绍
1、RAC是受函数响应式编程的启发而设计开发。
2、RAC不使用可变变量开发,而是提供捕获当前值和未来值的信号(由RACSignal表示)。
3、通过操作信号进行链式、组合、响应,程序可以被声明式的实现,代码不需要对值持续地观察和更新。
4、信号支持异步操作,可以简化包括网络操作在内的异步场景。非常类似于
futures and promises.
5、RAC的一个优势是:它把多种异步机制统一起来,诸如delegate,notifications,callback blocks, target-action, kvo。
例子
1、最简单的case,self的属性username 只要变化,就会打印出来新值。
不用在self的类中重写observeValueForKeyPath:ofObject:change:context:
[RACObserve(self, username) subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}];
2、链式调用
只有新值的首个字符是"j"时,才会打印。filter也会返回一个RACSingnal,它只会在blk中返回yes的时候,才会发送新值。
[[RACObserve(self, username)
filter:^(NSString *newName) {
return [newName hasPrefix:@"j"];
}]
subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}];
3、状态传递。一个属性值的改变可能依赖多个值的改变,并互相作用。RAC提供了一个使用信号组及操作组来解决的策略。
RAC(self, createEnabled) = [RACSignal
combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ]
reduce:^(NSString *password, NSString *passwordConfirm) {
return @([passwordConfirm isEqualToString:password]);
}];
createEnabled的值是否改变依赖于 password和passwordConfirm两个值是否相等,而这两个值可能都在变,这时就需要任何一个最新的变化都触发一次对比。 +combineLatest:reduce: 刚好支持这样的操作,第一个参数是个信号数组,第二个参数是判定的条件。
self.createEnabled这个变量的值会跟随这个绑定的信号组发生改变时,值是否相等而定。
4、信号的值流依赖。它可以建立在一段时间的任何值流上,而非只是KVO.
如按钮点击相应
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
NSLog(@"button was pressed!");
return [RACSignal empty];
}];
4、点击按钮,登录操作
self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^(id sender) {
// The hypothetical -logIn method returns a signal that sends a value when
// the network request finishes.
return [client logIn];
}];
//login command每次执行的时候,都会调用上方的那个block,操作由上方blk信号组成的信号组的信号,就是executionSignals对应的信号
[self.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) {
// Log a message whenever we log in successfully.
[loginSignal subscribeCompleted:^{
NSLog(@"Logged in successfully!");
}];
}];
self.loginButton.rac_command = self.loginCommand; //点击按钮执行login command.
5、信号也可以表示timer、ui事件、以及一切随着事件改变的事物
6、复杂的组操作
[[RACSignal
merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]]
subscribeCompleted:^{
NSLog(@"They're both done!");
}];
两个网络操作都完成时,会打印出They're both done!
7、避免block嵌套
[[[[client
logInUser]
flattenMap:^(User *user) {
// Return a signal that loads cached messages for the user.
return [client loadCachedMessagesForUser:user];
}]
flattenMap:^(NSArray *messages) {
// Return a signal that fetches any remaining messages.
return [client fetchMessagesAfterMessage:messages.lastObject];
}]
subscribeNext:^(NSArray *newMessages) {
NSLog(@"New messages: %@", newMessages);
} completed:^{
NSLog(@"Fetched all messages.");
}];
登录 --> 用户 --> 缓存的消息 --> 最后一条消息获取最新消息列表 --> 打印新消息列表 --> 所有结束后打印结束。
8、线程间通信
RAC(self.imageView, image) = [[[[client
fetchUserWithUsername:@"joshaber"]
deliverOn:[RACScheduler scheduler]]
map:^(User *user) {
// Download the avatar (this is done on a background queue).
return [[NSImage alloc] initWithContentsOfURL:user.avatarURL];
}]
// Now the assignment will be done on the main thread.
deliverOn:RACScheduler.mainThreadScheduler];
获取用户信息 --> 子线程中下载用户头像 -->主线程中将图片设置到imageview上。
RAC擅长
1、异步
2、事件驱动数据源
- (void)viewDidLoad {
[super viewDidLoad];
@weakify(self);
RAC(self.logInButton, enabled) = [RACSignal
combineLatest:@[
self.usernameTextField.rac_textSignal,
self.passwordTextField.rac_textSignal,
RACObserve(LoginManager.sharedManager, loggingIn),
RACObserve(self, loggedIn)
] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {
return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);
}];
[[self.logInButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {
@strongify(self);
RACSignal *loginSignal = [LoginManager.sharedManager
logInWithUsername:self.usernameTextField.text
password:self.passwordTextField.text];
[loginSignal subscribeError:^(NSError *error) {
@strongify(self);
[self presentError:error];
} completed:^{
@strongify(self);
self.loggedIn = YES;
}];
}];
RAC(self, loggedIn) = [[NSNotificationCenter.defaultCenter
rac_addObserverForName:UserDidLogOutNotification object:nil]
mapReplace:@NO];
}
3、操作链式依赖
普通代码
[client logInWithSuccess:^{
[client loadCachedMessagesWithSuccess:^(NSArray *messages) {
[client fetchMessagesAfterMessage:messages.lastObject success:^(NSArray *nextMessages) {
NSLog(@"Fetched all messages.");
} failure:^(NSError *error) {
[self presentError:error];
}];
} failure:^(NSError *error) {
[self presentError:error];
}];
} failure:^(NSError *error) {
[self presentError:error];
}];
rac
[[[[client logIn]
then:^{
return [client loadCachedMessages];
}]
flattenMap:^(NSArray *messages) {
return [client fetchMessagesAfterMessage:messages.lastObject];
}]
subscribeError:^(NSError *error) {
[self presentError:error];
} completed:^{
NSLog(@"Fetched all messages.");
}];
4、并行独立工作
传统
__block NSArray *databaseObjects;
__block NSArray *fileContents;
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{
databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate];
}];
NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{
NSMutableArray *filesInProgress = [NSMutableArray array];
for (NSString *path in files) {
[filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
}
fileContents = [filesInProgress copy];
}];
NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{
[self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
NSLog(@"Done processing");
}];
[finishOperation addDependency:databaseOperation];
[finishOperation addDependency:filesOperation];
[backgroundQueue addOperation:databaseOperation];
[backgroundQueue addOperation:filesOperation];
[backgroundQueue addOperation:finishOperation];
rac
RACSignal *databaseSignal = [[databaseClient
fetchObjectsMatchingPredicate:predicate]
subscribeOn:[RACScheduler scheduler]];
RACSignal *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
NSMutableArray *filesInProgress = [NSMutableArray array];
for (NSString *path in files) {
[filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
}
[subscriber sendNext:[filesInProgress copy]];
[subscriber sendCompleted];
}];
[[RACSignal
combineLatest:@[ databaseSignal, fileSignal ]
reduce:^ id (NSArray *databaseObjects, NSArray *fileContents) {
[self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
return nil;
}]
subscribeCompleted:^{
NSLog(@"Done processing");
}];
5、简化集合转换
传统
NSMutableArray *results = [NSMutableArray array];
for (NSString *str in strings) {
if (str.length < 2) {
continue;
}
NSString *newString = [str stringByAppendingString:@"foobar"];
[results addObject:newString];
}
rac
RACSequence *results = [[strings.rac_sequence
filter:^ BOOL (NSString *str) {
return str.length >= 2;
}]
map:^(NSString *str) {
return [str stringByAppendingString:@"foobar"];
}];