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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.btnDisposable dispose];
}
@end
截圖
4.信號的map方法
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@interface ViewController ()
@end
@implementation ViewController
// rac給每一個UI控件都增加了分類,在分類里面給UI控件擴充了新的方法。利用rac編程時用到的就是這個方法
- (void)viewDidLoad {
[super viewDidLoad];
[self net];// 創建信號,用于封裝自定義的網絡請求
}
-(void)net{
// 創建信號,用于封裝自定義的網絡請求。即:把網絡請求的代碼封裝在createSignal的代碼塊中
RACSignal *ipSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
[subscriber sendCompleted];// 信號串聯,必須在兩個信號之間執行sendCompleted操作
return nil;
}];
// 吃飯信號
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在吃飯"];
[subscriber sendNext:@"吃完飯了"];
return nil;
}];
// 信號串聯
[[getup concat:eat] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截圖
12.信號并聯
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@interface ViewController ()
@end
@implementation ViewController
// rac給每一個UI控件都增加了分類,在分類里面給UI控件擴充了新的方法。利用rac編程時用到的就是這個方法
- (void)viewDidLoad {
[super viewDidLoad];
[self merge];// 信號并聯(多條線程),任務同時執行.不用執行sendCompleted操作
}
-(void)merge{
// 起床信號
RACSignal *getup =[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
return nil;
}];
// 吃飯信號
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在吃飯"];
[subscriber sendNext:@"吃完飯了"];
return nil;
}];
// 信號并聯
[[getup merge:eat] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截圖
13.信號忽略
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在穿衣服"];
[subscriber sendNext:@"起床"];
[subscriber sendCompleted];
return nil;
}];
// 吃飯信號
RACSignal *eat =[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"正在吃飯"];
[subscriber sendNext:@"吃完飯了"];
return nil;
}];
// 信號忽略.忽略了getup信號
[[getup then:^RACSignal *{
return eat;
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
@end
截圖
14.信號延遲
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)RACDisposable *btnDisposable;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
#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<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.name = @"CoderZb";// 修改name屬性
}
@end
截圖
19.綁定屬性
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
#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
截圖
101.21.gif
22.(一)rac_command
#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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<RACSubscriber> 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 <ReactiveCocoa/ReactiveCocoa.h>
@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 <ReactiveCocoa/ReactiveCocoa.h>
@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.在自己的用戶目錄(zhangbin)里新建一個Sites文件夾
- 2.進到cd /etc/apache2/users/目錄下,sudo vim zhangbin.conf,增加內容為:
<Directory "/Users/zhangbin/Sites/">
AllowOverride All
Options Indexes MultiViews FollowSymLinks
Require all granted
</Directory>
- 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服務器
102.1gif.gif 2.終端輸入(必須)
mysqladmin -u root password "123456"-
3.安裝Navicat數據庫管理工具. 目的:創建表,創建數據庫等等。
102.2.gif 4.創建數據庫,創建表
(三)效果展示
注冊
登錄
發表日志
細節問題
-
1.開啟和關閉MySQL的影響
101.29.gif
-
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 )