RAC中类的继承关系
1.RACDynamicSignal:RACSignal:RACStream
2.RACSequence:RACStream
3.RACCompoundDisposable : RACDisposable
4.RACSubscriptionScheduler : RACScheduler
5.RACQueueScheduler : RACScheduler
RAC优势(提高开发效率)
1.把target-Action/代理/通知/KVO统一封装成信号,即信号就可以实现这些功能。
2.监听属性的代码少
3.代码直观,代码复杂度减小m3u8
4.通过block降低代码间的耦合
订阅和订阅者
- subscribeNext:订阅
subscribeNext:^(id x) { } - subscriber:订阅者,例如订阅者调用sendNext方法。
[subscriber sendNext:responseObject];
25个事例+1个小项目帮助你解开RAC的面纱
1.监听UITextField内容的输入信号
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self Username];// 监听UITextField内容的输入信号
}
-(void)Username{
/* 1+2可以写成一行代码
[self.usernameField.rac_textSignal subscribeNext:^(id x) {/
NSLog(@"%@",x);
}];
*/
// 文本信号:监听UITextField内容的输入
RACSignal *textSignal = self.usernameField.rac_textSignal;// 1
// 订阅者block
[textSignal subscribeNext:^(id x) {// 2
NSLog(@"%@",x);
}];
}
@end
截图
2.监听按钮的点击信号(信号理解为事件)
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *login;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self Login];// 监听按钮的点击信号(信号理解为事件)
}
-(void)Login{
/* 1+2可以写成一行代码
[[self.login rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:
^(id x) {
NSLog(@"%@",x);
}];
*/
RACSignal *clickSignal = [self.login rac_signalForControlEvents:UIControlEventTouchUpInside];// 1
// 订阅者block
[clickSignal subscribeNext:^(id x) { // 2
NSLog(@"%@",x);
}];
}
@end
截图
3.取消按钮的点击信号
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *login;
@property(nonatomic,strong)RACDisposable *btnDisposable;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self Dispose];// 取消按钮的点击信号。先点击屏幕,然后再点击按钮,发现没有打印。因为点击信号已经取消了,点击多少次都不会打印。
}
-(void)Dispose{
self.btnDisposable = [[self.login rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.btnDisposable dispose];
}
@end
截图
4.信号的map方法
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self Map];// 信号的map方法
}
-(void)Map{
// 文本信号:监听UITextField内容的输入
RACSignal *textSignal = self.usernameField.rac_textSignal;
// 调用map方法:更改订阅者block输出的参数的类型
textSignal = [textSignal map:^id(id value){
return [NSString stringWithFormat:@"username = %@",value];
}];
// 订阅者block
[textSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
5.信号的filter方法
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self filterText];// 信号的filter方法
}
-(void)filterText{
// 文本信号:监听UITextField内容的输入
RACSignal *textSignal = self.usernameField.rac_textSignal;
textSignal = [textSignal filter:^BOOL(NSString *value) {// 将(id value)改为(NSString *value),因为我们已经确定了输入的内容是字符串(即使输入的是数字也行)
NSLog(@"filter %@",value);
// 当输入的内容大于5时。才会调用订阅者block
return value.length > 5;
}];
// 订阅者block
[textSignal subscribeNext:^(id x) {
NSLog(@"输出%@",x);// 只有当输入的个数大于5时才打印这行内容
}];
}
@end
截图
6.组合信号combineLatest:reduce:
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@property (weak, nonatomic) IBOutlet UIButton *login;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
@property(nonatomic,strong)RACDisposable *btnDisposable;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self combine]; // 组合信号combineLatest:reduce:
}
-(void)combine{
NSArray *signals = @[self.usernameField.rac_textSignal,self.pwd.rac_textSignal];
// 组合信号
RACSignal *combineSignal = [RACSignal combineLatest:signals reduce:^id(NSString *username,NSString *password){
// 无论用户名和密码有没有值,都会执行订阅block,只是返回的值有0和1,让登录按钮变为点击状态的话,x必须为1.
return @(username.length > 0 && password.length >0);
}];
// 订阅者block
[combineSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
// x为1时,登录按钮才可以点击
self.login.enabled = [x boolValue];
}];
}
@end
截图
7.创建一个用于封装自定义的网络请求的信号
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self net];// 创建信号,用于封装自定义的网络请求
}
-(void)net{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
RACSignal *ipSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
// 调用sendNext,底层就会调用subscribeNext.只有调用了subscribeNext,才会调用订阅者block,才会执行block中的内容,从而打印responseStr中的内容,然后接着会打印x中的内容。没有这句代码,什么也不会打印
[subscriber sendNext:responseStr];//A:调用subscribeNext的block
[subscriber sendNext:@"CoderZb"];// B:调用subscribeNext的block
// 注意:调用sendCompleted或者sendError,程序会终止向下执行,所以当C出现,D不执行。反之亦然。
[subscriber sendCompleted];// C:调用sendCompleted的block
// 模拟错误信息
NSError *ErrorInfo = [NSError errorWithDomain:@"服务未启动" code:2016 userInfo:nil]; [subscriber sendError:ErrorInfo];//D:调用error的block
}else{
NSLog(@"+++++%@",error);
}
}];
// 3.执行下载任务
[dataTask resume];
return nil;
}];
// 订阅者block
[ipSignal subscribeNext:^(id x) {// sendNext:调用的。
NSLog(@"网络请求成功,内容为%@",x);// 打印responseStr中的内容。验证:将responseStr替换为@"CoderZb"
} error:^(NSError *errorInfo){
NSLog(@"%@",errorInfo);
}
completed:^{// sendCompleted调用的。
NSLog(@"终止了订阅");
}];
}
@end
截图
8.信号嵌套
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@property (weak, nonatomic) IBOutlet UILabel *ipLabel;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self flatterMap];//信号嵌套
}
-(void)flatterMap{
// 按钮信号
RACSignal *btnSignal = [self.ipAddressBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
[[btnSignal flattenMap:^RACStream *(id value) {
return [self ipSignal];// 又嵌套了一个封装网络请求的信号
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
self.ipLabel.text = x;
}];
}
-(RACSignal *)ipSignal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"---%@",[NSThread currentThread]); // 通过打印可知,block代码块所在的线程为子线程,所以应将x,y处的代码写在主线程中,否则ipLabel上显示不出来内容.因为显示UI要在主线程(主队列)执行
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{// 主队列即主线程。验证:打印下面的注释
// NSLog(@"+++%@",[NSThread currentThread]);
[subscriber sendNext:responseStr];//x:调用subscribeNext的block
}];
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return [RACDisposable disposableWithBlock:^{
// 取消网络请求
[dataTask cancel];
}];
}];
}
@end
截图
9.信号注入
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@property (weak, nonatomic) IBOutlet UILabel *ipLabel;
// 可视化创建UIActivityIndicatorView控件并勾选面板右侧的Hides When Stopped,即静态(停止)的时候隐藏该控件。
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *Indicator;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self doNext];// 信号注入:doNext在网络请求之前可以提前做一些操作,例如提前开始Indicator的动画
}
-(void)doNext{
RACSignal *btnSignal = [self.ipAddressBtn rac_signalForControlEvents:UIControlEventTouchUpInside ];
[btnSignal doNext:^(id x) {
// 开始Indicator的动画,此时Indicator会显示,因为在可视化面板右侧勾选了Hides When Stopped
[self.Indicator startAnimating];
}];
[[btnSignal flattenMap:^RACStream *(id value) {
// 开始Indicator的动画,此时Indicator会显示,因为在可视化面板右侧勾选了Hides When Stopped
[self.Indicator startAnimating];
// 执行网络请求的信号
return [self ipSignal];// 当有ruturn操作时,后面的内容不能再用btnSignal对象调用,否则最终的结果不是自己想要的。
}] subscribeNext:^(id x) {
self.ipLabel.text = x;
// 停止Indicator的动画.此时Indicator会隐藏,因为在可视化面板右侧勾选了Hides When Stopped
[self.Indicator stopAnimating];
}];
}
-(void)flatterMap{
RACSignal *btnSignal = [self.ipAddressBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
[[btnSignal flattenMap:^RACStream *(id value) {
return [self ipSignal];
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
self.ipLabel.text = x;
}];
}
-(RACSignal *)ipSignal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"---%@",[NSThread currentThread]); // 通过打印可知,block代码块所在的线程为子线程,所以应将x,y处的代码写在主线程中,否则ipLabel上显示不出来内容.因为显示UI要在主线程(主队列)执行
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{// 主队列即主线程。验证:打印下面的注释
// NSLog(@"+++%@",[NSThread currentThread]);
[subscriber sendNext:responseStr];//x:调用subscribeNext的block
}];
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return [RACDisposable disposableWithBlock:^{
// 取消网络请求
[dataTask cancel];
}];
}];
}
@end
截图
10.信号延迟
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self throttle];// 信号延迟:订阅block中的内容延时输出
}
-(void)throttle{
RACSignal *btnSignal = [self.ipAddressBtn rac_signalForControlEvents:UIControlEventTouchUpInside ];
// 延迟5秒输出block中的内容
[[btnSignal throttle:5] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
11.信号串联
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self concat];// 信号串联(一条线程),任务依次执行.需要执行sendCompleted操作
}
-(void)concat{
//调用sendCompleted,表示信号的完成。只有信号完成了,才能执行串联操作。就比如串行队列,只有前一个任务执行完成了,后面的任务才能执行。如何判断任务完成了,类比Rac中的sendCompleted操作。
// 起床信号
RACSignal *getup =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
[subscriber sendCompleted];// 信号串联,必须在两个信号之间执行sendCompleted操作
return nil;
}];
// 吃饭信号
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在吃饭"];
[subscriber sendNext:@"吃完饭了"];
return nil;
}];
// 信号串联
[[getup concat:eat] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
12.信号并联
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self merge];// 信号并联(多条线程),任务同时执行.不用执行sendCompleted操作
}
-(void)merge{
// 起床信号
RACSignal *getup =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
return nil;
}];
// 吃饭信号
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在吃饭"];
[subscriber sendNext:@"吃完饭了"];
return nil;
}];
// 信号并联
[[getup merge:eat] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
13.信号忽略
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self then];// 信号忽略(一条线程),任务依次执行.忽略的是then:前面的信号,执行then后面的信号,所以then前面的信号不执行,then后面的信号依次执行.需要执行sendCompleted操作
}
-(void)then{
// 起床信号
RACSignal *getup =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
[subscriber sendCompleted];
return nil;
}];
// 吃饭信号
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"正在吃饭"];
[subscriber sendNext:@"吃完饭了"];
return nil;
}];
// 信号忽略.忽略了getup信号
[[getup then:^RACSignal *{
return eat;
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
14.信号延迟
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self delay];// 信号延迟:等同throttle
}
-(void)delay{
RACSignal *btnSignal = [self.ipAddressBtn rac_signalForControlEvents:UIControlEventTouchUpInside ];
// 点击按钮,延迟5秒执行
[[btnSignal delay:5] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
15.信号超时
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
[self timeout];//信号超时.用于网络请求的信号
}
-(void)timeout{
// 过了7秒就超时,就不再执行订阅者block.因为Signal方法中线程休眠了5秒,5秒<7秒,所以没有超过超时的时间,所以会执行订阅者block.如果线程休眠10秒,那么就不会执行订阅者block
[[[self Signal] timeout:7 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
-(RACSignal *)Signal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"---%@",[NSThread currentThread]); // 通过打印可知,block代码块所在的线程为子线程,所以应将x,y处的代码写在主线程中,否则ipLabel上显示不出来内容.因为显示UI要在主线程(主队列)执行
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
#warning 线程休眠5秒
[NSThread sleepForTimeInterval:10];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{// 主队列即主线程。验证:打印下面的注释
// NSLog(@"+++%@",[NSThread currentThread]);
[subscriber sendNext:responseStr];//x:调用subscribeNext的block
}];
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return [RACDisposable disposableWithBlock:^{
// 取消网络请求
[dataTask cancel];
}];
}];
}
@end
截图
16.监听选中的图片,监听选中图片的手势
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)RACDisposable *btnDisposable;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
static dispatch_once_t onceToken;
// 一次性代码,保证只执行一次.这样第二次点击屏幕时候不会执行这段代码,但是3处的代码会执行
dispatch_once(&onceToken,^{ // 功能1:监听选中的图片
UIImagePickerController *pickVC = [[UIImagePickerController alloc] init];
pickVC.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// 通过信号获取用户选中的图片
[pickVC.rac_imageSelectedSignal subscribeNext:^(NSDictionary *Info) {
// NSLog(@"%@",x);
// key根据上面的打印内容粘贴
UIImage *selectImage = Info[@"UIImagePickerControllerOriginalImage"];
self.imageView.image = selectImage;
// 选中完图片,直接到返回之前的控制器
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self presentViewController:pickVC animated:YES completion:nil];
});
// 功能2:监听点击图片的手势
self.imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[self.imageView addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(id x) {
NSLog(@"图片被点击");
}];
}
@end
截图
17.监听键盘的弹出
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@end
@implementation ViewController
// rac给每一个UI控件都增加了分类,在分类里面给UI控件扩充了新的方法。利用rac编程时用到的就是这个方法
- (void)viewDidLoad {
[super viewDidLoad];
// 功能3:监听键盘的弹出
NSNotificationCenter *notification = [NSNotificationCenter defaultCenter];
[[notification rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"键盘弹出,键盘信息如下----%@",x);
}];
}
@end
截图
18.监听属性值的改变
#import "ViewController.h"
#import
#import "Person.h"
@interface ViewController ()
@property(nonatomic,strong)Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self kvo];// kvo监听属性的改变
}
-(void)kvo{
self.person = [[Person alloc] init];
// kvo监听属性的改变,一旦属性发生改变就会调用订阅block.所以点击一下屏幕,就会调用订阅block一次。
[RACObserve(self.person, name) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.person.name = @"CoderZb";// 修改name属性
}
@end
截图
19.绑定属性
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *ipLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self Bundle];// 绑定属性.将修改的内容显示到属性上
}
-(void)Bundle{
// 绑定ipLabel对象的text属性,将[self ipSignal]返回的结果赋值给text属性。相当于给系统的属性赋值。
RAC(self.ipLabel,text) = [self ipSignal];
}
-(RACSignal *)ipSignal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"---%@",[NSThread currentThread]); // 通过打印可知,block代码块所在的线程为子线程,所以应将x,y处的代码写在主线程中,否则ipLabel上显示不出来内容.因为显示UI要在主线程(主队列)执行
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
#warning 线程休眠5秒
[NSThread sleepForTimeInterval:5];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{// 主队列即主线程。验证:打印下面的注释
// NSLog(@"+++%@",[NSThread currentThread]);
[subscriber sendNext:responseStr];//x:调用subscribeNext的block
}];
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return [RACDisposable disposableWithBlock:^{
// 取消网络请求
[dataTask cancel];
}];
}];
}
@end
截图
20.监听UITextField文本信号的改变
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@property (weak, nonatomic) IBOutlet UILabel *ipLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self Change]; // 监听UITextField文本信号的改变,只要文本发生改变,就会将改变的内容显示在UILabel上
}
-(void)Change{
// 监听UITextField文本信号的改变,只要文本发生改变,就会将改变的内容添加到UILabel上
RAC(self.ipLabel,text) = self.usernameField.rac_textSignal;
}
@end
截图
21.RAC中,解决block造成的循环引用的两种做法
#import "ViewController.h"
#import
#import "Person.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@property (weak, nonatomic) IBOutlet UIButton *login;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 解决block造成的循环引用的两种做法
// 做法1:
/*
__weak typeof(self) weakSelf = self;
[[RACSignal combineLatest:@[self.usernameField.rac_textSignal,self.pwd.rac_textSignal] reduce:^id(NSString *username,NSString *pwd){
return @(username.length > 0 && pwd.length > 0);
}] subscribeNext:^(id x) {
weakSelf.login.enabled = [x boolValue];
}];
*/
// 做法2:
@weakify(self)
[[RACSignal combineLatest:@[self.usernameField.rac_textSignal,self.pwd.rac_textSignal] reduce:^id(NSString *username,NSString *pwd){
return @(username.length > 0 && pwd.length > 0);
}] subscribeNext:^(id x) {
@strongify(self)
self.login.enabled = [x boolValue];
}];
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
截图
22.(一)rac_command
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self btnClick];//rac_command
}
-(void)btnClick{
// RACCommand:点击按钮,执行订阅者block
self.ipAddressBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [self ipSignal];
}];
}
-(RACSignal *)ipSignal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
NSLog(@"%@",responseStr);
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return nil;
}];
}
@end
截图
23.(二)rac_command
#import "ViewController.h"
#import
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *usernameField;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
@property (weak, nonatomic) IBOutlet UIButton *ipAddressBtn;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self EnabledSingle];// rac_command
}
-(void)EnabledSingle{
// 按钮的可用信号
RACSignal *btnEnabledSingle = [RACSignal combineLatest:@[self.usernameField.rac_textSignal,self.pwd.rac_textSignal] reduce:^id(NSString *username,NSString *pwd){
// 只有用户名和密码不为空时,ipAddressBtn按钮才会变为可以点击的状态。
return @(username.length > 0 && pwd.length > 0);
}];
// 按钮点击后触发信号,从而会打印出ip地址。如果按钮不可点击,是无法触发信号的。
self.ipAddressBtn.rac_command = [[RACCommand alloc]initWithEnabled:btnEnabledSingle signalBlock:^RACSignal *(id input) {
return [self ipSignal];
}];
}
-(RACSignal *)ipSignal{
// 创建信号,用于封装自定义的网络请求。即:把网络请求的代码封装在createSignal的代码块中
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 1.创建请求对象
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pv.sohu.com/cityjson"]];
// 2.创建下载任务
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"---%@",[NSThread currentThread]); // 通过打印可知,block代码块所在的线程为子线程,所以应将x,y处的代码写在主线程中,否则ipLabel上显示不出来内容.因为显示UI要在主线程(主队列)执行
// data不是UTF-8编码,而是GBK编码
if (error == nil) {// 没错误就执行
//将data对象转换成字符串
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *responseStr = [[NSString alloc] initWithData:data encoding:enc];
NSLog(@"%@",responseStr);
}else{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"+++++%@",error);// y
}];
}
}];
// 3.执行下载任务
[dataTask resume];
return nil;
}];
}
@end
截图
24.RACSequence
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self racSequence];// RACSequence:主要用于遍历
}
-(void)racSequence{
NSArray *names = @[@"Tom",@"Mary",@"Jack"];
// 获取RACSequence里面的信号进行遍历
[names.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
25.RACSequence和filter结合使用
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self racSequenceAndfilter];// RACSequence和filter结合使用
}
-(void)racSequenceAndfilter{
// RACSequence与filter(过滤)的结合使用
NSString *text = @"19293842456647";
[[text.rac_sequence.signal filter:^BOOL(id value) {
return [value intValue] >5;// 只打印大于5的数字
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截图
登录小项目
- 查看隐藏的Apache的安装目录:/etc/apache2/
- 做法1:dock下右键Finder,选择"前往文件夹",输入"/etc"
- 做法2:finder下->前往->前往文件夹->输入/etc
- 做法3:Terminal 输入 "open /etc"
apache相关指令:
开启apache服务 sudo apachectl start
停止apache服务 sudo apachectl stop
重启服务 sudo apachectl restart
查看版本 httpd -v
注意点:
Apache+Mysql+PHP的集成环境的配置(一、二、三)
(一)Apache服务器的开启和配置、php的配置
httpd.conf配置错误的话,重新下载未修改的httpd.conf文件 密码
具体步骤推荐这个参考链接
参考链接1
参考链接2
参考链接3
参考链接4
- 1.在自己的用户目录(zhangbin)里新建一个Sites文件夹
- 2.进到cd /etc/apache2/users/目录下,sudo vim zhangbin.conf,增加内容为:
AllowOverride All
Options Indexes MultiViews FollowSymLinks
Require all granted
- 3.修改zhangbin.conf这个文件的权限
sudo chmod 644 zhangbin.conf
- 4.cd到/etc/apache2/目录,sudo vim httpd.conf 将下面五句话的注释去掉
LoadModule authz_core_module libexec/apache2/mod_authz_core.so
LoadModule authz_host_module libexec/apache2/mod_authz_host.so
LoadModule userdir_module libexec/apache2/mod_userdir.so
Include /private/etc/apache2/extra/httpd-userdir.conf
LoadModule php5_module libexec/apache2/libphp5.so
- 5.进到/etc/apache2/extra/目录,sudo vim httpd-userdir.conf ,将这句话的注释去掉
Include /private/etc/apache2/users/*.conf
- 6.sudo apachectl restart 重启服务器
- 7.将php后台代码放到Sites文件夹下
- 8.浏览器输入: loacal/~zhangbin/
拓展知识点修改apache默认页面的内容
cd /Library/WebServer/Documents/
sudo vim index.html.en
(二)开启MySQL服务器(必不可少),安装Navicat数据库管理工具
-
1.只安装MySQL.prefPane,用于开启MySQL服务器
2.终端输入(必须)
mysqladmin -u root password "123456"-
3.安装Navicat数据库管理工具. 目的:创建表,创建数据库等等。
4.创建数据库,创建表
(三)效果展示
注册
登录
发表日志
细节问题
-
1.开启和关闭MySQL的影响
-
2.1.Apache系统级的根目录及对应网址是:
/Library/WebServer/Documents/http://localhost
-
2.2.用户级的根目录及对应网址是:
~/Siteshttp://localhost/~zhangbin/
由2.1和2.2可知关闭和开启apache服务器对访问网址的影响如下截图
[软件+项目LS](https://pan.baidu.com/s/1gffo8IB 密码 43kp )