亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

iOS開發之ReactiveCocoa下的MVVM(干貨分享)

2019-11-14 17:58:25
字體:
來源:轉載
供稿:網友

  最近工作比較忙,但還是出來更新博客了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,干活還是比較足的。在之前發表過一篇博文,名字叫做iOS開發之淺談MVVM的架構設計與團隊協作,大體上講的就是使用Block回調的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函數響應式編程的框架,并且有許多人用它來更好的實現MVVM。所以在上篇博客發表后,有些同行給評論建議看一下ReactiveCocoa的東西,所以就系統的看了一下ReactiveCocoa的東西。不過有一點要說明的就是,不使用ReactiveCocoa是可以實現MVVM的,并非使用MVVM模式你就必須的使用ReactiveCocoa的東西,你可以使用KVO,Block,Delegate,Navigation等手段,而ReactiveCocoa更優雅的實現了這個過程。ReactiveCocoa就是一個響應式編程的框架,它會使MVVM每層之間交互起來更為方便,所以長和MVVM聯系在一起。

 

  一.函數響應式編程(Function Reactive PRogramming)

  關于函數響應式編程的東西,我想引用國外這個ReactiveCocoa教學視頻(視頻鏈接https://vimeo.com/65637501)中的一張PPT來簡單的說一下什么是函數響應式編程。那就直接上圖,下圖是上方視頻鏈接的截圖,很形象的解釋了什么是函數響應式編程。簡單的說下方c = a + b 定義好后,當a的值變化后,c的值就會自動變化。不過a的值變化時會產生一個信號,這個信號會通知c根據a變化的值來變化自己的值。b的值變化同樣也影響c的值。下圖很好的表達了這個思想。在此就不做贅述了。

 

 

 

 

  二. ReactiveCocoa簡介

  先簡單的介紹一下什么是ReactiveCocoa框架,然后通過實例好好的去搞一搞這個框架,最后就是如何在項目中使用了。關于ReactiveCocoa的理解一些博客(見本篇博客中的鏈接分享)中把ReactiveCocoa比喻成管道,ReactiveCocoa中的Signal就是管道中的水流。使用ReactiveCocoa可以方便的在MVVM各層之間架起溝通的管道,便于每層之間的交互?,F在在我們做的工程中已經在使用ReactiveCocoa框架了,用起來的感覺是非常爽的,好用!

  可以說ReactiveCocoa中核心是信號量機制,Signal在ReactiveCocoa中發揮著強大的不可代替的作用,可謂是ReactiveCocoa的靈魂所。Signal是可以攜帶一些對象和參數的,你可以獲取該對象并且可以對該信號量攜帶的值進行map, filter等常用操作,操作后的值會和該信號量進行綁定。先簡單的這么一說,后邊的部分回詳細的介紹如何讓信號量發揮強大的作用。

  ReactiveCocoa中對Block的使用可謂是淋漓盡致,如果對Block使用不熟的朋友可以補一下Block的東西,然后在回頭看一下ReactiveCocoa的東西。關于ReactiveCocoa更多的東西,請參考Github上的鏈接(https://github.com/ReactiveCocoa/ReactiveCocoa)。

 

 

 

  三. 在工程中引入ReactiveCocoa

    1.你可以使用Github上的加入方式如下所示,本人感覺比較麻煩,就沒有使用,采用的第二種方法(CocoaPods)。

    2.上面的步驟難免有些麻煩,所以用CocoaPods更為便捷一些,Profile文件中的內容如下所示,我用的是2.5版本。3.0后就支持Swift了,因為我沒有用Swift寫東西,所以就用的是2.5版本,設置完Profile文件后,pod install即可。

    你可以pod search ReactiveCocoa看一下版本,選擇你需要的版本即可。

 

 

 

 

 

 

  四.使用ReactiveCocoa

  下方會通過一些簡單的實例來介紹一下信號量機制和一些常用的方法。

    1.引入相應的頭文件

    在工程中引入下方的頭文件(建議在Pch文件中引入)就可以使用我們的ReactiveCocoa框架了

1 #import <ReactiveCocoa/ReactiveCocoa.h>2 #import <ReactiveCocoa/RACEXTScope.h>

 

    2. Sequence和Map

    Sequence:隊列,是ReactiveCocoa中引入的一個類型,它類似于數組,我們可以暫且把Sequence看做綁定信號量的數組吧。在OC中的NSArray可以通過rac_sequence方法轉換成ReactiveCocoa中的Sequence,然后就可以調用處理信號的一些方法了。

    參考以下實例代碼:

     ?。?)把NSArray通過rac_sequence方法生成RAC中的Sequence

     ?。?)獲取該Sequence對象的信號量

      (3)調用Signal的Map方法,使每個元素的首字母大寫

      (4)通過subscribNext方法對其進行遍歷輸出

 1 //uppercaseString use map 2 - (void)uppercaseString { 3      4     RACSequence *sequence = [@[@"you", @"are", @"beautiful"] rac_sequence]; 5  6     RACSignal *signal =  sequence.signal; 7      8     RACSignal *capitalizedSignal = [signal map:^id(NSString * value) { 9                                return [value capitalizedString];10                             }];11     12     [signal subscribeNext:^(NSString * x) {13         NSLog(@"signal --- %@", x);14     }];15     16     [capitalizedSignal subscribeNext:^(NSString * x) {17         NSLog(@"capitalizedSignal --- %@", x);18     }];19 }

    下方截圖是上個這個方法中的運行結果,從運行結果不難看出,通過Signal相應的方法處理完后,處理的結果會與新返回的信號量所綁定。原信號量中的值保持不變。每次信號量調用相應的方法處理完數據后,都會返回一個新的信號量,而這個信號量是獨立于原信號量的。

    

    由上面的介紹可知,上面方法中的一坨代碼可以寫成下方的一串。因為一個方法調用后會返回一個持有新結果的新的信號量,然后在這個信號量的基礎上再次調用信號量其他的方法。Signal還有其他一些好用的方法,用法和map方法類似,在此就不一一贅述了,gitHub上有相應的實例文檔。

1 - (void)uppercaseString {2 3     [[[@[@"you", @"are", @"beautiful"] rac_sequence].signal4      map:^id(NSString * value) {5         return [value capitalizedString];6     }] subscribeNext:^(id x) {7         NSLog(@"capitalizedSignal --- %@", x);8     }];9 }

 

    3.信號量開關(Switch)

    上面把信號量比喻成水管,那么Switch就是水龍頭呢。通過Switch我們可以控制那個信號量起作用,并且可以在信號量之間進行切換。也可以這么理解,把Switch看成另一段水管,Switch對接那個水管,就流那個水管的水,這樣比喻應該更為貼切一些。下方是一個關于Switch的一個小實例。

      (1) 首先創建3個自定義信號量(3個水管),前兩個水管是用來接通不同的水源的(google, baidu), 而最后一個信號量是用來對接不同水源水管的水管(signalOfSignal)。signalOfSignal接baidu水管上,他就流baidu水源的水,接google水管上就流google水源的水。

      (2) 把signalOfSignal信號量通過switchToLatest方法加工成開關信號量。

      (3) 緊接著是對通過開關數據進行處理。

      (4) 開關對接baidu信號量,然后baidu和google信號量同時往水管里灌入數據,那么起作用的是baidu信號量。

      (5) 開關對接google信號量,google和baidu信號量發送數據,則google信號量輸出到signalOfSignal中

 1 //信號開關Switch 2 - (void)signalSwitch { 3     //創建3個自定義信號 4     RACSubject *google = [RACSubject subject]; 5     RACSubject *baidu = [RACSubject subject]; 6     RACSubject *signalOfSignal = [RACSubject subject]; 7      8     //獲取開關信號 9     RACSignal *switchSignal = [signalOfSignal switchToLatest];10     11     //對通過開關的信號量進行操作12     [[switchSignal  map:^id(NSString * value) {13         return [@"https//www." stringByAppendingFormat:@"%@", value];14     }] subscribeNext:^(NSString * x) {15         NSLog(@"%@", x);16     }];17     18     19     //通過開關打開baidu20     [signalOfSignal sendNext:baidu];21     [baidu sendNext:@"baidu.com"];22     [google sendNext:@"google.com"];23     24     //通過開關打開google25     [signalOfSignal sendNext:google];26     [baidu sendNext:@"baidu.com/"];27     [google sendNext:@"google.com/"];28 }

    上面代碼輸出結果如下:

 

    4.信號量的合并

    信號量的合并說白了就是把兩個水管中的水合成一個水管中的水。但這個合并有個限制,當兩個水管中都有水的時候才合并。如果一個水管中有水,另一個水管中沒有水,那么有水的水管會等到無水的水管中來水了,在與這個水管中的水按規則進行合并。下面這個實例就是把兩個信號量進行合并。

    (1) 首先創建兩個自定義的信號量letters和numbers

    (2) 吧兩個信號量通過combineLatest函數進行合并,combineLatest說明要合并信號量中最后發送的值

    (3) reduce塊中是合并規則:把numbers中的值拼接到letters信號量中的值后邊。

    (4) 經過上面的步驟就是創建所需的相關信號量,也就是相當于架好運輸的管道。接著我們就可以通過sendNext方法來往信號量中發送值了,也就是往管道中進行灌水。

 1 //組合信號 2 - (void)combiningLatest{ 3     RACSubject *letters = [RACSubject subject]; 4     RACSubject *numbers = [RACSubject subject]; 5      6     [[RACSignal 7      combineLatest:@[letters, numbers] 8      reduce:^(NSString *letter, NSString *number){ 9          return [letter stringByAppendingString:number];10      }]11      subscribeNext:^(NSString * x) {12          NSLog(@"%@", x);13      }];14     15     //B1 C1 C216     [letters sendNext:@"A"];17     [letters sendNext:@"B"];18     [numbers sendNext:@"1"];19     [letters sendNext:@"C"];20     [numbers sendNext:@"2"];21 }

   上面示例的運行輸出結果如下:

    下面是自己畫的原理圖,思路應該還算是清晰。

 

    5.信號的合并(merge)

      信號合并就理解起來就比較簡單了,merge信號量規則比較簡單,就是把多個信號量,放入數組中通過merge函數來合并數組中的所有信號量為一個。類比一下,合并后,無論哪個水管中有水都會在merge產生的水管中流出來的。下方是merge信號量的代碼:

      (1) 創建三個自定義信號量, 用于merge

      (2) 合并上面創建的3個信號量

      (3) 往信號里灌入數據

 1 //合并信號 2 - (void)merge { 3     RACSubject *letters = [RACSubject subject]; 4     RACSubject *numbers = [RACSubject subject]; 5     RACSubject *chinese = [RACSubject subject]; 6      7     [[RACSignal 8      merge:@[letters, numbers, chinese]] 9      subscribeNext:^(id x) {10         NSLog(@"merge:%@", x);11     }];12     13     [letters sendNext:@"AAA"];14     [numbers sendNext:@"666"];15     [chinese sendNext:@"你好!"];16 }

    上面代碼運行結果如下:

    上面示例的原理圖如下:

 

 

 

 

  五. 在MVVM中引入RactiveCocoa

    學以致用,最后來個簡單的實例,來感受一下如何在MVVM中使用RactiveCocoa。當然今天RAC的應用是非常簡單的,但原理就是這樣的。接下啦我們要使用RAC模擬一下登錄功能,當然,網絡請求也是模擬的,這不是重點。重點在于如何在MVVM各層之間使用RAC的信號量來更方便的在各個層之間進行響應式數據交互。下面這個實例的UI是非常簡單的,并且實現起來也是灰常簡單的,關鍵還是在于RAC的應用。

    1.搭建Demo所需UI,用戶界面非常簡單,公有兩個用戶界面,一個是登錄頁面(兩個輸入框,一個登錄按鈕),一個是登錄后跳轉的頁面(一個展示用戶名和密碼的Label)。下方是使用Storyboard實現的用戶登錄頁面。實現完后,個兩個頁面各自關聯一個ViewContorller類。

 

    2.下方是整個小Demo的工程目錄,因為我們今天的重點是如何在MVVM中使用RAC, 所以重點在于RAC的應用,對于MVVM的分層就簡化一些。下方有VC層,在VC層中有兩個視圖控制器,一個是登錄使用的視圖控制器(ViewContorller)另一個是登錄成功后的視圖控制器(LoginSuccessViewController)。而ViewModel中則是負責登錄的ViewModel業務邏輯層,該層中負責數據驗證,網絡請求,數據存儲等一些與UI無關的業務邏輯。

 

    3.實現登錄的ViewModel層

    因為ViewModel層是獨立于UI層而存在的,所以可以在沒有UI的情況下我們就可以去實現相應模塊的ViewModel層。這正好減少了個個層次間的耦合性,同時也提高了可測試性,總體上改善了可維護性。好廢話少說,接下來要實現登錄的ViewModel層。

    (1) 登錄ViewModel層對應的類的頭文件中的內容如下所示(VCViewModel.h), 其實下方一些常用的信號量可以抽象出來放到ViewModel的父類中,這為了簡化Demo沒有做父類的抽象。下方就是VCViewModel中interface定義的公有屬性和公有方法(Public)。userName和passWord(NSString類型) 用來綁定用戶輸入的用戶名和密碼。下方三個自定義信號量successObject, failureObject, errorObject 用來發送網絡請求的數據。successObject負責處理網絡請求成功且符合正常業務邏輯的事件, failureObject負責網絡請求成功不符合正常業務邏輯的處理,errorObject負責網絡異常處理。

 

 1 // 2 //  VCViewModel.h 3 //  ReactiveCocoaDemo 4 // 5 //  Created by Mr.LuDashi on 15/10/19. 6 //  Copyright © 2015年 ZeluLi. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 11 @interface VCViewModel : NSObject12 @property (nonatomic, strong) NSString *userName;13 @property (nonatomic, strong) NSString *password;14 @property (nonatomic, strong) RACSubject *successObject;15 @property (nonatomic, strong) RACSubject *failureObject;16 @property (nonatomic, strong) RACSubject *errorObject;17 18 - (id) buttonIsValid;19 - (void)login;20 @end

    

   上面可能說的有些抽象,結合項目中的實例來解釋一下什么時候發送successObject信號量,如何發送failureObject信號量,何時使用errorObject信號量。

   以某些理財App中購買理財產品的業務流程為例。在用戶下單之前先去判斷用戶是否實名認證以及綁定銀行卡,如果用戶已經實名和綁定銀行卡就走正常支付流程(用戶就是想去下單購買),VM就往VC發送successObject信號,當前VC就會根據信號量的指示跳轉到下單支付頁面。  但是如果用戶沒有實名或者綁卡,那么VM就給VC發送failureObject信號,根據信號量中的參數來判斷是走實名認證流程還是走綁定銀行卡流程。 errorObject就比較簡單了,網絡異常,后臺服務器拋出的異常等不需要iOS這邊做業務邏輯處理的,就放在errorObject中負責錯誤信息的展示。

    文字說完了,如果有些小伙伴還不太明白,那看下面這張原理圖吧。把三種信號量我們可以類比成十字路口的紅綠燈。successObject就是綠燈,可以走正常流程。failureObject是黃燈,先等一下,完成該做的就可以走綠燈了。而errorObject就是一紅燈,報錯異常,終止業務流程并提升錯誤信息。有圖有真相,到這兒如果還不理解我就沒招了。

    在Public方法中- (id) buttonIsValid; 負責返回登錄按鈕是否可用的信號量。- (void)login;發起網絡請求,調用登錄網絡接口。

  

    (2)代碼的具體實現如下(VCViewModel.m中的代碼),私有屬性如下。userNameSignal用來存儲用戶名的信號量,passwordSignal是用來存儲密碼的信號量。reqestData則是用來存儲返回數據的。

1 @interface VCViewModel ()2 @property (nonatomic, strong) RACSignal *userNameSignal;3 @property (nonatomic, strong) RACSignal *passwordSignal;4 @property (nonatomic, strong) NSArray *requestData;5 @end

 

    (3)VCViewModel的初始化方法如下,負責初始化屬性。

 1 - (instancetype)init 2 { 3     self = [super init]; 4     if (self) { 5         [self initialize]; 6     } 7     return self; 8 } 9 10 - (void)initialize {11     _userNameSignal = RACObserve(self, userName);12     _passwordSignal = RACObserve(self, password);13     _successObject = [RACSubject subject];14     _failureObject = [RACSubject subject];15     _errorObject = [RACSubject subject];16 }

  

    (4) 發送登錄按鈕是否可用信號的方法如下,主要用到了信號量的合并。

//合并兩個輸入框信號,并返回按鈕bool類型的值- (id) buttonIsValid {        RACSignal *isValid = [RACSignal                          combineLatest:@[_userNameSignal, _passwordSignal]                          reduce:^id(NSString *userName, NSString *password){                              return @(userName.length >= 3 && password.length >= 3);                          }];        return isValid;}

 

    (5) 模擬網絡請求的發送,并發出網絡請求成功的信號,具體代碼如下

 1 - (void)login{ 2      3     //網絡請求進行登錄 4     _requestData = @[_userName, _password]; 5      6     //成功發送成功的信號 7     [_successObject sendNext:_requestData]; 8      9     //業務邏輯失敗和網絡請求失敗發送fail或者error信號并傳參10 11 }

 

  4. 上面是VM的實現,如果要進行單元測試的話,就對相應的VM類進行初始化,調用相應的函數進行單元測試即可。接著就是看如何在相應的VC模塊中使用VM。

    (1) 在VC中實例化相應的VM類,并綁定相應的參數和實現接收不同信號的方法,具體代碼如下:

 1 //關聯ViewModel 2 - (void)bindModel { 3     _viewModel = [[VCViewModel alloc] init]; 4      5      6     RAC(self.viewModel, userName) = self.userNameTextField.rac_textSignal; 7     RAC(self.viewModel, password) = self.passwordTextField.rac_textSignal; 8     RAC(self.loginButton, enabled) = [_viewModel buttonIsValid]; 9     10     @weakify(self);11     12     //登錄成功要處理的方法13     [self.viewModel.successObject subscribeNext:^(NSArray * x) {14         @strongify(self);15         LoginSuccessViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"LoginSuccessViewController"];16         vc.userName = x[0];17         vc.password = x[1];18         [self presentViewController:vc animated:YES completion:^{19             20         }];21     }];22     23     //fail24     [self.viewModel.failureObject subscribeNext:^(id x) {25         26     }];27     28     //error29     [self.viewModel.errorObject subscribeNext:^(id x) {30         31     }];32 33 }

 

    (2) 點擊登錄按鈕,調用VM中登錄相應的網絡請求方法即可

1 - (void)onClick {2     //按鈕點擊事件3     [[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]4      subscribeNext:^(id x) {5          [_viewModel login];6      }];7 }

 

    到此為止,一個完整模擬登錄模塊的RAC下的MVVM就實現完畢。當然上面的Demo是非常簡陋的,還有好多地方需要進化。不過麻雀雖小,道理你懂得。主要是通過上面的Demo來感受一下RAC中的信號量機制以及應用場景。

 

    5.上面代碼寫完,我們就可以運行看一下運行效果了,下方是運行后的效果,

  

 

  上述工程GitHub分享鏈接:https://github.com/lizelu/MVVMWithReactiveCocoa

  其他參考資料:

        https://github.com/ReactiveCocoa/ReactiveViewModel

        http://www.teehanlax.com/blog/model-view-viewmodel-for-ios/

        http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/

        http://nshipster.cn/reactivecocoa/

        http://limboy.me/ios/2013/06/19/frp-reactivecocoa.html

        https://vimeo.com/65637501

        http://southpeak.github.io/blog/2014/08/08/mvvmzhi-nan-yi-:flickrsou-suo-shi-li/

        http://southpeak.github.io/blog/2014/08/02/reactivecocoazhi-nan-%5B%3F%5D-:xin-hao/

        http://southpeak.github.io/blog/2014/08/02/reactivecocoazhi-nan-er-:twittersou-suo-shi-li/

 

 

 

        ViewModel:

          Kicking off network or database requests

          Determining when information should be hidden or shown

          Date and number formatting

          Localization

 

        ViewController:

          Layout

          Animations

          Device rotation 

          View and window transitions

          Presenting loaded UI

 

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩欧美在线中文字幕| 日韩欧美国产黄色| 亚洲欧美综合v| 欧美老女人性生活| 国产精品美女999| 久久久女人电视剧免费播放下载| 久久精品最新地址| 色老头一区二区三区在线观看| 久久久在线免费观看| 亚洲精品动漫久久久久| 粗暴蹂躏中文一区二区三区| 在线观看视频99| 久久综合网hezyo| 亚洲国产精品va在线观看黑人| 欧美日韩中文字幕| 欧美精品免费在线观看| 97精品久久久中文字幕免费| 91在线无精精品一区二区| 欧美性xxxxx| 国产成人综合一区二区三区| 久久国产加勒比精品无码| 欧美又大又硬又粗bbbbb| 欧美日韩国产综合视频在线观看中文| 欧美久久精品一级黑人c片| 欧美视频专区一二在线观看| 国产精品久久婷婷六月丁香| 成人黄色中文字幕| 国产精品视频久久久久| 亚洲精品美女在线观看播放| 日韩欧美在线免费观看| 成人av在线天堂| 亚洲韩国青草视频| 国产精品啪视频| 成人国内精品久久久久一区| 日韩中文字幕免费| 亚洲精品白浆高清久久久久久| 国产91精品最新在线播放| 欧美精品久久久久久久久| 亚洲第一中文字幕在线观看| 精品中文字幕在线| www.欧美精品| 亚洲欧洲一区二区三区在线观看| 久久婷婷国产麻豆91天堂| 97免费视频在线| 中文字幕精品一区久久久久| 欧美最猛性xxxxx(亚洲精品)| 亚洲第一网中文字幕| 欧美激情在线观看| 久久久噜噜噜久久| 这里只有精品在线观看| 国产亚洲综合久久| 亚洲视频网站在线观看| 国产一区二区成人| 亚洲日韩欧美视频| 成人妇女淫片aaaa视频| 91精品国产综合久久久久久蜜臀| 日韩电影视频免费| 中文字幕亚洲色图| 国产精品国产三级国产专播精品人| 亚洲成人久久电影| 国产一区二中文字幕在线看| 欧美三级欧美成人高清www| 欧美人与性动交| 欧美成人国产va精品日本一级| 欧美性20hd另类| 九九视频这里只有精品| 久久91精品国产91久久久| 亚洲第一网站免费视频| 国产精品入口免费视频一| 久久这里有精品视频| 97在线视频免费播放| 亚州av一区二区| 国产精品偷伦免费视频观看的| 国产日韩欧美日韩大片| 欧美国产欧美亚洲国产日韩mv天天看完整| 日韩欧美国产一区二区| 久久亚洲国产精品| 欧美激情视频网站| 国产精品高清在线| 日韩av免费在线看| 中文字幕不卡在线视频极品| 欧美日本中文字幕| 久久久久久网址| 色婷婷久久一区二区| 69av在线视频| 成人av资源在线播放| 久久久久久av| 色综合久久精品亚洲国产| 久久久久久久久91| 亚洲国产欧美久久| 欧美激情免费观看| 欧美激情按摩在线| 久99久在线视频| 亚洲国产成人久久综合| 久久免费视频在线| 亚洲欧美在线x视频| 日韩av最新在线观看| 欧美精品在线网站| 亚洲影院色无极综合| 欧美一级淫片丝袜脚交| 亚洲大尺度美女在线| 日韩成人激情在线| 精品国产一区二区三区久久久| 欧美激情一区二区三区在线视频观看| 欧美国产日韩在线| 国产中文字幕日韩| 国产美女精品视频免费观看| 成人综合网网址| 国产成人aa精品一区在线播放| 91久久久国产精品| 国产欧美婷婷中文| 欧美疯狂做受xxxx高潮| 国产日韩综合一区二区性色av| 亚洲美女av在线| 久久久99免费视频| 一区二区三区高清国产| 亚洲图片欧洲图片av| 麻豆国产va免费精品高清在线| 精品动漫一区二区三区| 国产精品女视频| 国产丝袜精品第一页| 亚洲一区二区三| 欧美日韩国产麻豆| 欧美野外猛男的大粗鳮| 欧美有码在线观看视频| 欧美一级视频在线观看| 亚洲国产欧美日韩精品| 亚洲一区二区在线播放| 国产成人精品免费视频| 午夜精品久久久久久久99热| 精品久久久久久久中文字幕| 国产区精品视频| 日韩精品日韩在线观看| 国产最新精品视频| 国产欧美日韩综合精品| 在线观看中文字幕亚洲| 国产成人a亚洲精品| 亚洲欧美日韩在线一区| 欧美亚洲第一区| 亚洲毛片一区二区| 伊人久久久久久久久久| 欧美日韩成人在线播放| 日韩在线观看网址| 亚洲视频一区二区三区| 精品亚洲一区二区三区在线观看| 8050国产精品久久久久久| 国产亚洲视频中文字幕视频| 欧美激情二区三区| 成人动漫网站在线观看| 成人免费激情视频| 免费97视频在线精品国自产拍| 91久久久久久久久久久| 国产不卡在线观看| 日韩精品中文在线观看| 91精品国产91久久久久久不卡| 欧美亚洲免费电影| 久久久久久久影院| 成人黄色大片在线免费观看| 国产一区二区三区四区福利| 精品一区二区三区三区| 中日韩美女免费视频网站在线观看| 九九精品视频在线| 韩国福利视频一区| 欧美精品在线网站|