精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

基于ReSwift和App Coordinator的iOS架構

開發(fā) 開發(fā)工具
本文想從另一個角度和大家探討一個新的iOS應用架構方案,架構的本質(zhì)是管理復雜性,在討論具體的架構方案前,我們首先應該明確一個iOS應用的開發(fā),其復雜性在哪里?

一、iOS架構漫談

當我們在談iOS應用架構時,我們聽到最多的是MVC,MVVM,VIPER這三個Buzz Word,他們的邏輯一脈相承,不斷的從ViewController中把邏輯拆分出去。從蘋果官方推薦的MVC:

蘋果官方推薦的MVC

(圖片來自:http://t.cn/R4vP8Ko)

隨著系統(tǒng)的復雜,把功能進行細化,把整合View展示數(shù)據(jù)的邏輯獨立出來形成ViewModel模塊,架構風格就變成了MVVM:

MVVM

(圖片來自:http://t.cn/R4vP8Ko)

隨著系統(tǒng)的更加復雜,把路由的職責,獲取數(shù)據(jù)的職責也獨立出去,架構風格就變成了VIPER:

VIPER

(圖片來自:http://t.cn/R4vP8Ko)

本文則想從另一個角度和大家探討一個新的iOS應用架構方案,架構的本質(zhì)是管理復雜性,在討論具體的架構方案前,我們首先應該明確一個iOS應用的開發(fā),其復雜性在哪里?

二、iOS應用的開發(fā)復雜度

對于一個iOS應用來說,其開發(fā)的復雜性主要體現(xiàn)在三個方面:

1. 復雜界面設計的實現(xiàn)和樣式管理

iOS App最終呈現(xiàn)給用戶的是一組組的UI界面,而對于一個特定的App來說,其UI的設計元素(如配色,字體大小,間距等)基本上是固定的,另外,組成該App的基礎組件(如Button種類,輸入框種類等)也是有限的。但是如何管理、組合、重用組件則是架構師需要考慮的問題,尤其是一些App在開發(fā)過程中可能出現(xiàn)大量的UI樣式重構,更需要清晰的控制住重構的影響范圍。這兒的復雜性本質(zhì)上是UI組件自身設計實現(xiàn)的復雜性,多UI組件之間的組合方式和UI組件的重用機制。

2. 路由設計

對于一個大型的iOS應用,通常會把其功能按Feature拆分,經(jīng)過這樣的拆分之后,其可能出現(xiàn)的路由有以下幾種:

路由

APP間路由: 從其它App調(diào)起當前App,并進入一個很深層次的頁面(圖示1)。

APP內(nèi)路由:

  • 啟動進入App的Home頁面(圖示2)
  • 從Home頁面到進Feature Flow(圖示3)
  • Feature內(nèi)按流程的頁面的路由(圖示4)
  • 各Feature之間的頁面跳轉(zhuǎn)(圖示5)
  • 各Feature共享的單點信息頁的跳轉(zhuǎn)(圖示6)

根據(jù)Apple官方的MVC架構,這些復雜的各種跳轉(zhuǎn)邏輯,以及跳轉(zhuǎn)前的ViewController的準備工作等邏輯纏繞在AppDelegate的初始化,ViewController的UI邏輯中。這兒的復雜性主要是UI和業(yè)務之間纏繞不清的相互耦合。

3. 應用狀態(tài)管理

一個iOS應用本質(zhì)上就是一個狀態(tài)機,從一個狀態(tài)的UI由User Action或者API調(diào)用返回的Data Action觸發(fā)達到下一個狀態(tài)的UI。為了準確的控制應用功能,開發(fā)者需要能夠清楚的知道:

  • 應用的當前UI是由哪些狀態(tài)決定的?
  • User Action會影響哪些應用狀態(tài)?如何影響的?
  • Data Action會影響哪些應用狀態(tài)?如何影響的?

在MVC,MVVM,VIPER的架構中,應用的狀態(tài)分散在Model或者Entity中,甚至有些狀態(tài)直接保存在View Controller中,在跟蹤狀態(tài)時經(jīng)常需要跨越多個Model,很難獲取到一個全貌的應用狀態(tài)。另外,對于Action會如何影響應用的狀態(tài)跟蹤起來也比較困難,尤其是當一個Action產(chǎn)生的影響路徑不同,或最終可能導致多個Model的狀態(tài)發(fā)生改變時。這兒的復雜性主要體現(xiàn)在治理分散的狀態(tài),以及管理不統(tǒng)一的狀態(tài)改變機制帶來的復雜性。

三、如何管理這些復雜度

前面明確了iOS應用開發(fā)的復雜性所在,那么從架構層面上應該如何去管理這些復雜性呢?

1. 使用Atomic Design和Component Driven Development管理界面開發(fā)的復雜度

UI界面的復雜度本質(zhì)上是一個點上的復雜度,其復雜性集中在系統(tǒng)的某些小細節(jié)處,不會增加系統(tǒng)整體規(guī)劃的復雜度,所以控制其復雜度的主要方式是隔離,避免一個UI組件之間的相互交織,變成一個面上的復雜度,導致復雜度不可控。在UI層,***的隔離方式就是組件化,在筆者之前的一篇文章《前端組件化方案》中詳細解釋了前端組件化方案的實施細節(jié),這兒就不再贅述。

2. 使用App Coordinator統(tǒng)一管理應用路由

應用的路由主要分為App間路由和App內(nèi)路由,對它們需要分別處理

(1) App間路由

對于APP之間的路由,主要通過兩種方式實現(xiàn):

一種是URL Scheme 通過在當前App中配置進行相應的設置,即可從別的APP跳轉(zhuǎn)到當前APP。進入當前App之后,直接在AppDelegate中的方法:

  1. func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool 

轉(zhuǎn)換進App內(nèi)的路由。

另一種是Universal Links,同樣的通過在當前App中進行配置,當用戶點擊URL就會跳轉(zhuǎn)到當前的App里。進入當前APP之后,直接在AppDelegate中的方法:

  1. func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool 

中轉(zhuǎn)進App內(nèi)路由。

所以App間的路由邏輯相對簡單,就是一個把外部URL映射到內(nèi)部路由中。這部分只需要增加一個URL Scheme或Universal Link對應到App內(nèi)路由的處理邏輯即可。

(2) App內(nèi)路由

對于內(nèi)部路由,我們可以引入App Coordinator來管理所有路由。App Coordinator是Soroush Khanlou在2015年的NSSpain演講上提出的一個模式,其本質(zhì)上是Martin Fowler在《Patterns of Enterprise Application Architecture》中描述的Application Controller模式在iOS開發(fā)上的應用。其核心理念如下:

  • 抽象出一個Coordinator對象概念。
  • 由該Coordinator對象負責ViewController的創(chuàng)建和配置。
  • 由該Coordinator對象來管理所有的ViewController跳轉(zhuǎn)。
  • Coordinator可以派生子Coordinator來管理不同的Feature Flow。

經(jīng)過這層抽象之后,一個復雜App的路由對應關系就會如下:

一個復雜App的路由對應關系

從圖中可以看出,應用的UI和業(yè)務邏輯被清晰的拆分開,各自有了自己清晰的職責。ViewController的初始化,ViewController之間的鏈接邏輯全部都轉(zhuǎn)移到App Coordinator的體系中去了,ViewController則徹底變成了一個個獨立的個體,其只負責:

  • 自己界面內(nèi)的子UIView組織;
  • 接收數(shù)據(jù)并把數(shù)據(jù)綁定到對應的子UIView展示;
  • 把界面上的user action轉(zhuǎn)換為業(yè)務上的user intents,然后轉(zhuǎn)入App Coordinator中進行業(yè)務處理。

通過引入AppCoordinator之后,UI和業(yè)務邏輯被拆分開,各自處理自己負責的邏輯。在iOS應用中,路由的底層實現(xiàn)還是UINavigationController提供的present,push,pop等函數(shù),在其之上,iOS社區(qū)出了各種封裝庫來更好的封裝ViewController之間的跳轉(zhuǎn)接口,如JLRoutes,routable-ios,MGJRouter等,在這個基礎上我們來進一步思考App Coordinator,其概念核心是把ViewController跳轉(zhuǎn)和業(yè)務邏輯一起抽象為user intents(用戶意圖),對于開發(fā)者具體使用什么樣的方式實現(xiàn)的跳轉(zhuǎn)邏輯并沒有限制,而路由的實現(xiàn)方式在一個應用中的影響范圍非常廣,切換路由的實現(xiàn)方式基本上就是一次全App的重構(做過React應用的react-router0.13升級的朋友應該深有體會)。所以在App Coordinator的基礎之上,還可以引入Protocol-Oriented Programming的概念,在App Coordinator的具體實現(xiàn)和ViewController之間抽象一層Protocols,把UI和業(yè)務邏輯的實現(xiàn)徹底抽離開。經(jīng)過這層抽象之后,路由關系變化如下:

經(jīng)過App Coordinator統(tǒng)一處理路由之后,App可以得到如下好處:

  • ViewController變得非常簡單,成為了一個概念清晰的,獨立的UI組件。這極大的增加了其可復用性。
  • UI和業(yè)務邏輯的抽離也增加了業(yè)務代碼的可復用性,在多屏時代,當你需要為當前應用增加一個iPad版本時,只需要重新做一套iPad UI對接到當前iPhone版的App Coordinator中就完成了。
  • App Coordinator定義與實現(xiàn)的分離,UI和業(yè)務的分離讓應用在做A/B Testing時變得更加容易,可以簡單的使用不同實現(xiàn)的Coordinator,或者不同版本的ViewController即可。

3. 使用ReSwift管理應用狀態(tài)

前面提到引入App Coordinator之后,ViewController剩下的職責之一就是“接收數(shù)據(jù)并把數(shù)據(jù)綁定到對應的子UIView展示”,這兒的數(shù)據(jù)來源就是應用的狀態(tài)。它山之石,可以攻玉,不只是iOS應用有復雜狀態(tài)管理的問題,在越來越多的邏輯往前端遷移的時代,所有的前端都面臨著類似的問題,而目前Web前端最火的Redux就是為了解決這個問題誕生的狀態(tài)管理機制,而ReSwift則把這套機制帶入了iOS的世界。這套機制中主要有一下幾個概念:

  • App State: 在一個時間點上,應用的所有狀態(tài). 只要App State一樣,應用的展現(xiàn)就是一樣的。
  • Store: 保存App State的對象,其還負責發(fā)送Action更新App State。
  • Action: 表示一次改變應用狀態(tài)的行為,其本身可以攜帶用以改變App State的數(shù)據(jù)。
  • Reducer: 一個接收當前App State和Action,返回新的App State的小函數(shù)。

在這個機制下, 一個App的狀態(tài)轉(zhuǎn)換如下:

  • 啟動初始化App State -> 初始化UI,并把它綁定到對應的App State的屬性上
  • 業(yè)務操作 -> 產(chǎn)生Action -> Reducer接收Action和當前App State產(chǎn)生新的AppState -> 更新當前State -> 通知UI AppState有更新 -> UI顯示新的狀態(tài) -> 下一個業(yè)務操作……

在這個狀態(tài)轉(zhuǎn)換的過程中,需要注意,業(yè)務操作會有兩類:

  • 無異步調(diào)用的操作,如點擊界面把界面數(shù)據(jù)存儲到App State上;這類操作處理起來非常簡單,按照上面提到的狀態(tài)轉(zhuǎn)換流程走一圈即可。
  • 有異步調(diào)用的操作。如點擊查詢,調(diào)用API,數(shù)據(jù)返回之后再存儲到App State上。這類操作就需要引入一個新的邏輯概念(Action Creators)來處理,通過Action Creators來處理異步調(diào)用并分發(fā)新的Action。

整個App的狀態(tài)變換過程如下:

整個App的狀態(tài)變換過程

無異步調(diào)用操作的狀態(tài)流轉(zhuǎn)

無異步調(diào)用操作的狀態(tài)流轉(zhuǎn)

有異步調(diào)用操作的狀態(tài)流轉(zhuǎn)

經(jīng)過ReSwift統(tǒng)一管理應用狀態(tài)之后,App開發(fā)可以得到如下好處:

  • 統(tǒng)一管理應用狀態(tài),包括統(tǒng)一的機制和唯一的狀態(tài)容器,這讓應用狀態(tài)的改變更容易預測,也更容易調(diào)試。
  • 清晰的邏輯拆分,清晰的代碼組織方式,讓團隊的協(xié)作更加容易。
  • 函數(shù)式的編程方式,每個組件都只做一件小事并且是獨立的小函數(shù),這增加了應用的可測試性。
  • 單向數(shù)據(jù)流,數(shù)據(jù)驅(qū)動UI的編程方式。

四、整理后的iOS架構

經(jīng)過上面的大篇幅介紹,我們來歸納下結合了App Coordinator和ReSwift的一個iOS App的整體架構圖:

iOS App的整體架構圖

五、架構實戰(zhàn)

上面已經(jīng)講解了整體的架構原理,”Talk is cheap”, 接下來就以Raywendlich上面的這個App為例來看看如何實踐這個架構。

以Raywendlich上面的這個App為例來看看如何實踐這個架構

(圖片來自:http://t.cn/RCO2Sa0)

1. ***步:構建UI組件

在構建UI組件時,因為每個組件都是獨立的,所以團隊可以并發(fā)的做多個UI頁面,在做頁面時,需要考慮:

  • 該ViewController包含多少子UIView?子UIView是如何組織在一起的?
  • 該ViewController需要的數(shù)據(jù)及該數(shù)據(jù)的格式?
  • 該ViewController需要支持哪些業(yè)務操作?

以***個頁面為例:

  1. class SearchSceneViewController: BaseViewController {     
  2.   //定義業(yè)務操作的接口     
  3.   var searchSceneCoordinator:SearchSceneCoordinatorProtocol?     
  4.   //子組件     
  5.   var searchView:SearchView? 
  1. //該UI接收的數(shù)據(jù)結構     
  2. private func update(state: AppState) {         
  3.   if let searchCriteria = state.property.searchCriter   {             
  4.   searchView?.update(searchCriteria: searchCriteria)        }    }?     
  5. //支持的業(yè)務操作     
  6. func searchByCity(searchCriteria:SearchCriteria) {         
  7.   searchSceneCoordinator?.searchByCity(searchCriteria: searchCriteria)     
  8.   }?     
  9. func searchByCurrentLocation() {         
  10.     searchSceneCoordinator?.searchByCurrentLocation()     
  11. }     
  12.  //子組件的組織     
  13. override func viewDidLoad() {         
  14.   super.viewDidLoad()         
  15.   searchView = SearchView(frame: self.view.bounds)         
  16.   searchView?.goButtonOnClick = self.searchByCity         
  17.   searchView?.locationButtonOnClick = self.searchByCurrentLocation         
  18.   self.view.addSubview(searchView!)     

注:子組件支持的操作都以property的形式從外部注入,組件內(nèi)命名更組件化,不應包含業(yè)務含義。

其它的幾個ViewController也依法炮制,完成所有UI組件,這步完成之后,我們就有了App的所有UI組件,以及UI支持的所有操作接口。下一步就是把他們串聯(lián)起來,根據(jù)業(yè)務邏輯完成User Journey。

2. 第二步:構建App Coordinators串聯(lián)所有的ViewController

首先,在AppDelegate中加入AppCoordinator,把路由跳轉(zhuǎn)的邏輯轉(zhuǎn)移到AppCoordinator中。

  1. var appCoordinator: AppCoordinator! 
  2. func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 
  3.   window = UIWindow() 
  4.   let rootVC = UINavigationController() 
  5.   window?.rootViewController = rootVC 
  6.   appCoordinator = AppCoordinator(rootVC) 
  7.   appCoordinator.start() 
  8.   window?.makeKeyAndVisible() 
  9.   return true 

然后,在AppCoordinator中實現(xiàn)首頁SeachSceneViewController的加載

  1. class AppCoordinator { 
  2.     var rootVC: UINavigationController 
  3.     init(_ rootVC: UINavigationController){ 
  4.         self.rootVC = rootVC 
  5.     } 
  6.     func start() { 
  7.         let searchVC = SearchSceneViewController(); 
  8.         let searchSceneCoordinator = SearchSceneCoordinator(self.rootVC) 
  9.         searchVC.searchSceneCoordinator = searchSceneCoordinator 
  10.         self.rootVC.pushViewController(searchVC, animated: true) 
  11.     } 

在上一步中我們已經(jīng)為每個ViewController定義好對應的CoordinatorProtocol,也會在這一步中實現(xiàn)

  1. protocol SearchSceneCoordinatorProtocol {     
  2.   func searchByCity(searchCriteria:SearchCriteria)     
  3.   func searchByCurrentLocation() 
  4.  
  5. class SearchSceneCoordinator: AppCoordinator, SearchSceneCoordinatorProtocol {     
  6.   func searchByCity(searchCriteria:SearchCriteria) {         
  7.     self.pushSearchResultViewController()     
  8.   }         
  9.   func searchByCurrentLocation() {         
  10.     self.pushSearchResultViewController()     
  11.   }         
  12.   private func pushSearchResultViewController() {         
  13.       let searchResultVC = SearchResultSceneViewController();         
  14.       let searchResultCoordinator = SearchResultsSceneCoordinator(self.rootVC)         
  15.       searchResultVC.searchResultCoordinator = searchResultCoordinator         
  16.       self.rootVC.pushViewController(searchResultVC, animated: true)     
  17.   } 

以同樣的方式完成SearchResultSceneCoordinator. 從上面的的代碼中可以看出,我們跳轉(zhuǎn)邏輯中只做了兩件事:初始化ViewController和裝配該ViewController對應的Coordinator。這步完成之后,所有UI之間就已經(jīng)按照業(yè)務邏輯串聯(lián)起來了。下一步就是根據(jù)業(yè)務邏輯,讓用App State在UI之間流轉(zhuǎn)起來。

3. 第三步:引入ReSwift架構構建Redux風格的應用狀態(tài)管理機制

首先,跟著ReSwift官方指導選取你喜歡的方式引入ReSwift框架,筆者使用的是Carthage。

(1) 定義App State

然后,需要根據(jù)業(yè)務定義出整個App的State,定義State的方式可以從業(yè)務上建模,也可以根據(jù)UI需求來建模,筆者偏向于從UI需求建模,這樣的State更容易和UI進行綁定。在本例中主要的State有:

  1. struct AppState: StateType { 
  2.     var property:PropertyState 
  3.     ... 
  4. struct PropertyState { 
  5.     var searchCriteria:SearchCriteria? 
  6.     var properties:[PropertyDetail]? 
  7.     var selectedProperty:Int = -1 
  8. struct SearchCriteria { 
  9.     let placeName:String? 
  10.     let centerPoint:String? 
  11. struct PropertyDetail { 
  12.     var title:String 
  13.     ... 

定義好State的模型之后,接著就需要把AppState綁定到Store上,然后直接把Store以全局變量的形式添加到AppDelegate中。

  1. let mainStore = Store<AppState>(     
  2.   reducer: AppReducer(),     
  3.   state: nil 
  4.   ) 

(2) 把App State綁定到對應的UI上

注入之后,就可以把AppState中的屬性綁定到對應的UI上了,注意,接收數(shù)據(jù)綁定應該是每個頁面的頂層ViewController,其它的子View都應該只是以property的形式接收ViewController傳遞的值。綁定AppState需要做兩件事:訂閱AppState

  1. override func viewWillAppear(_ animated: Bool) { 
  2.       super.viewWillAppear(animated) 
  3.       mainStore.subscribe(self) { state in state } 
  4.   } 
  5.   override func viewWillDisappear(_ animated: Bool) { 
  6.       super.viewWillDisappear(animated) 
  7.       mainStore.unsubscribe(self) 
  8.   } 

和實現(xiàn)StoreSubscriber的newState方法

  1. class SearchSceneViewController: StoreSubscriber {     
  2.   ......     
  3.   override func newState(state: AppState) {         
  4.     self.update(state: state)         
  5.     super.newState(state: state)     
  6.   }     
  7.   ...... 

經(jīng)過綁定之后,每一次的AppState修改都會通知到ViewController,ViewController就可以根據(jù)AppState中的內(nèi)容更新自己的UI了。

(3) 定義Actions和Reducers實現(xiàn)App State更新機制

綁定好UI和AppState之后,接下來就應該實現(xiàn)改變AppState的機制了,首先需要定義會改變AppState的Action們

  1. struct UpdateSearchCriteria: Action {     
  2.   let searchCriteria:SearchCriteria 
  3. ...... 

然后,在AppCoordinator中根據(jù)業(yè)務邏輯把對應的Action分發(fā)出去, 如果有異步請求,還需要使用ActionCreator來請求數(shù)據(jù),然后再生成Action發(fā)送出去

  1. func searchProperties(searchCriteria: SearchCriteria, _ callback:(() -> Void)?) -> ActionCreator { 
  2.         return { state, store in 
  3.             store.dispatch(UpdateSearchCriteria(searchCriteria: searchCriteria)) 
  4.             self.propertyApi.findProperties( 
  5.                 searchCriteria: searchCriteria, 
  6.                 success: { (response) in 
  7.                     store.dispatch(UpdateProperties(response: response)) 
  8.                     store.dispatch(EndLoading()) 
  9.                     callback?() 
  10.             }, 
  11.                 failure: { (error) in 
  12.                     store.dispatch(EndLoading()) 
  13.                     store.dispatch(SaveErrorMessage(errorMessage: (error?.localizedDescription)!)) 
  14.             } 
  15.             ) 
  16.             return StartLoading() 
  17.         } 
  18.     } 

Action分發(fā)出去之后,初始化Store時注入的Reducer就會接收到相應的Action,并根據(jù)自己的業(yè)務邏輯和當前App State的狀態(tài)生成一個新的App State

  1. func propertyReducer(_ state: PropertyState?, action: Action) -> PropertyState { 
  2.         var statestate = state ?? PropertyState() 
  3.         switch action { 
  4.         case let action as UpdateSearchCriteria: 
  5.             state.searchCriteria = action.searchCriteria 
  6.         ... 
  7.         default: 
  8.             break 
  9.         } 
  10.         return state 
  11.     } 

最終Store以Reducer生成的新App State替換掉老的App State完成了應用狀態(tài)的更新。

以上三步就是一個完整的架構實踐步驟,該示例的所有源代碼可以在筆者的Github上找到。

六、總結

以解決掉Massive ViewController的iOS應用架構之爭持續(xù)多年,筆者也參與了公司內(nèi)外的多場討論,架構本無好壞,只是各自適應不同的上下文而已。本文中提到的架構方式使用了多種模式,它們各自解決了架構上的一些問題,但并不是一定要捆綁在一起使用,大家完全可以根據(jù)需要裁剪出自己需要的模式,希望本文中提到的架構模式能夠給你帶來一些啟迪。

【本文是51CTO專欄作者“ThoughtWorks”的原創(chuàng)稿件,微信公眾號:思特沃克,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2021-07-21 16:30:38

iOSAPP架構

2017-12-25 14:59:47

APP架構iOS協(xié)議

2019-05-21 10:45:44

Docker架構容器

2015-02-28 09:49:22

lua

2023-09-25 10:19:01

模型App開源

2013-07-26 14:21:21

2011-07-29 11:28:33

基于IOS的交換機基于CLI的交換機

2021-11-23 10:25:35

性能優(yōu)化iOS App 啟動優(yōu)化

2018-09-12 21:25:15

iOSAppcrash

2012-01-05 09:19:25

iOSApp應用

2023-09-27 07:56:25

2019-09-27 15:11:14

iOS Android 操作系統(tǒng)

2015-05-25 19:15:39

2015-05-04 14:25:55

UPYUN Open

2024-05-21 08:00:00

架構數(shù)字化分布式系統(tǒng)

2015-04-14 10:03:10

iOS應用架構

2015-05-27 09:57:28

2015-07-09 15:04:53

JSPatch動態(tài)更新ios app

2019-03-26 04:47:28

iOSWireshark抓包

2021-06-28 14:35:36

iOSAPP緩存
點贊
收藏

51CTO技術棧公眾號

亚洲欧美日韩综合aⅴ视频| 久久精品网址| 亚洲国产成人精品久久久国产成人一区 | 亚洲欧美偷拍一区| 成人一区不卡| 欧美videos大乳护士334| 男女视频网站在线观看| 高清av电影在线观看| 国产高清在线精品| 国产999视频| 欧洲第一无人区观看| 日韩超碰人人爽人人做人人添| 91精品办公室少妇高潮对白| 91看片淫黄大片91| 国产在线一二| 国产成人aaaa| 国产欧美亚洲视频| 1级黄色大片儿| 99视频精品全国免费| 亚洲福利视频免费观看| √天堂资源在线| 在线日韩影院| 亚洲国产精品影院| 一区二区三区欧美成人| 性猛交xxxx| 国产成人免费视频网站| 国产欧美韩国高清| 黄色片网站在线免费观看| 欧美日韩ab| www.欧美免费| 国产又粗又硬视频| 日本暖暖在线视频| 中文.日本.精品| 日韩一区在线免费观看| 欧美一区二区三区成人久久片| 性猛交富婆╳xxx乱大交天津 | 亚洲少妇30p| 日韩一本精品| 九色在线视频蝌蚪| 99这里只有精品| 99在线首页视频| 亚洲无码久久久久久久| 老**午夜毛片一区二区三区 | 四虎4545www国产精品| 五月综合激情日本mⅴ| 国产精品啪啪啪视频| 日本在线观看视频| 国产精品卡一卡二| 色播亚洲视频在线观看| 免费在线高清av| 26uuu国产一区二区三区| 国产精品美女久久久久av福利| 99久久久无码国产精品免费| 狠狠色综合播放一区二区| 国产精品一区二区三| 欧美男人天堂网| 日本91福利区| 国产精品亚洲综合天堂夜夜| 精品国产www| 免费人成网站在线观看欧美高清| 国产成人精品在线播放| 免费av中文字幕| 日本vs亚洲vs韩国一区三区二区 | 国产欧美一区二区精品久导航| 美国av一区二区三区| 九色在线播放| 欧美国产97人人爽人人喊| 亚洲欧洲另类精品久久综合| 丁香在线视频| 亚洲欧美综合网| 亚洲小视频在线播放| av片在线观看永久免费| 欧美1234区| 久久久久久久久一| 亚洲国产午夜伦理片大全在线观看网站 | 欧美gay视频| 欧洲av一区二区嗯嗯嗯啊| 91精品无人成人www| 高清久久一区| 亚洲成人精品视频| 久久久久久亚洲中文字幕无码| 欧美日韩精品一区二区视频| 久久激情五月丁香伊人| 九九热视频精品| 亚洲制服少妇| 国产在线观看精品一区二区三区| 精品人妻aV中文字幕乱码色欲 | 91禁男男在线观看| 综合日韩在线| 欧洲成人性视频| 国产又粗又长又大视频| 不卡的av在线播放| 视频三区二区一区| 日韩av毛片| 色婷婷一区二区| 中文字幕第10页| 影视先锋久久| 欧美极品第一页| www.国产毛片| 国产成人高清在线| 日韩电影免费观看在| 国产激情在线视频| 色哟哟在线观看一区二区三区| www.com污| 综合干狼人综合首页| 欧美成人午夜剧场免费观看| 久久久成人免费视频| 国产乱子伦视频一区二区三区 | 色综合色综合色综合色综合色综合 | 亚洲视频电影图片偷拍一区| 国产探花在线免费观看| 久久影院亚洲| 国产在线精品一区二区三区| 日本在线视频站| 日韩欧美福利视频| 男生和女生一起差差差视频| 亚洲影院天堂中文av色| 欧美激情亚洲综合一区| 伊人网免费视频| 久久亚洲欧美国产精品乐播| 成人免费观看在线| www.欧美视频| 伊人久久久久久久久久久久久 | 欧美日韩国产一级| 日本激情小视频| 1000部精品久久久久久久久| 亚洲aⅴ男人的天堂在线观看 | www.国产免费| 综合久久国产九一剧情麻豆| 九色porny91| 亚洲综合小说图片| 91精品国产91久久久久久不卡| 精品国产一级片| 亚洲人成精品久久久久久| 欧美男女交配视频| 精品久久成人| 日韩美女主播视频| 欧美91精品久久久久国产性生爱| 亚洲国产aⅴ成人精品无吗| 91大神免费观看| 亚洲乱码精品| 亚洲最大的成人网| 成人免费网站在线观看视频| 6080国产精品一区二区| a一级免费视频| 久久精品久久精品| 在线观看一区欧美| 在线免费观看亚洲| 日韩视频免费在线| 国产色综合视频| 亚洲美女区一区| xxxxwww一片| 今天的高清视频免费播放成人| 粉嫩av一区二区三区免费观看| 污污的网站在线免费观看| 日韩欧美中文字幕公布| 免费无遮挡无码永久在线观看视频 | 亚洲精品极品少妇16p| 91视频九色网站| 天堂va在线| 亚洲国产精品视频在线观看| 奇米影视第四色777| 91蜜桃网址入口| 国产a级片免费观看| 成人网18免费网站| 成人免费视频网址| 在线观看午夜av| 亚洲精品www久久久| 毛片视频网站在线观看| 国产无一区二区| 亚洲一区精品视频在线观看| 亚洲综合专区| 精品一区久久久久久| www.精品| 久久影视电视剧免费网站| 亚洲成a人片77777精品| 欧美日韩国产一区二区| 国产精品综合激情| 国产精品一区不卡| 日韩少妇内射免费播放| 秋霞欧美视频| 成人av资源网| 亚洲精品在线影院| 久久成人一区二区| 四虎影视2018在线播放alocalhost| 色综合色综合色综合| 日韩av手机在线免费观看| 国产99久久久精品| 国产激情在线观看视频| 欧美在线不卡| 欧洲视频一区二区三区| 激情久久免费视频| 欧美自拍视频在线观看| 黄色在线免费网站| 日韩av在线一区| 国产麻豆一精品一男同| 欧美日韩在线一区| 波多野结衣亚洲一区二区| 99久久免费国产| 91亚洲精品久久久蜜桃借种| 99综合在线| 成年人三级视频| 国产成人高清| 成人18视频| 成人1区2区| 97成人在线视频| 成人短视频在线观看| 亚洲视频在线观看| 农村少妇久久久久久久| 8v天堂国产在线一区二区| 久久久精品福利| 亚洲国产成人91porn| 男人av资源站| 久久久亚洲精品一区二区三区| 91视频福利网| 久久精品国产99国产| 久久久久久久久久久久久久国产| 国产精品av一区二区| 亚洲欧洲日夜超级视频| 伊人久久大香线蕉av不卡| 国产高清自拍99| 国产精品亚洲综合在线观看| 国产精品99久久久久久www| 久草在线资源福利站| 久久综合色88| 日本在线免费网| 国产亚洲人成网站在线观看| 午夜av免费观看| 亚洲国产黄色片| www.蜜臀av.com| 日韩女同互慰一区二区| 国产精品嫩草影院桃色| 欧美性视频一区二区三区| 国产精品免费精品一区| 亚洲成人777| 久久一级黄色片| 亚洲综合av网| 久久亚洲国产成人精品性色| 亚洲激情av在线| 欧美人禽zoz0强交| 亚洲免费色视频| 国产极品国产极品| 亚洲欧美一区二区三区极速播放| www.xx日本| 日韩理论片网站| 黑鬼狂亚洲人videos| 亚洲视频1区2区| 免费一级a毛片夜夜看| 亚洲综合久久av| 久久综合色综合| 亚洲电影一区二区三区| 日本三级2019| 黑人狂躁日本妞一区二区三区| 中日韩黄色大片| 色综合久久综合网欧美综合网 | 玛丽玛丽电影原版免费观看1977| 青青草久久爱| 色99中文字幕| 亚洲va在线| www.69av| 日韩午夜av| 五月婷婷激情久久| 久久aⅴ国产欧美74aaa| 久久综合桃花网| 99国产精品久久久| 91网站免费入口| 中文字幕在线一区二区三区| 久久久久亚洲AV成人| 亚洲图片欧美色图| 特级西西444www大精品视频免费看| 欧美色videos| 在线免费看91| 精品毛片乱码1区2区3区| 午夜福利一区二区三区| 国产一区二区三区在线视频| www久久日com| 97久久精品国产| 成人精品国产亚洲| 亚洲综合中文字幕在线观看| 欧美亚洲tv| 亚洲图片欧洲图片日韩av| 国内精品福利| av无码精品一区二区三区| 激情av综合网| 中文精品在线观看| 日韩一区在线免费观看| 日韩欧美中文字幕一区二区| 欧美三级电影一区| 亚洲精品无码专区| 国产午夜精品视频免费不卡69堂| a级影片在线观看| 国产精品草莓在线免费观看 | 在线免费三级电影网站| 成人激情黄色网| 少妇一区二区三区| 中文字幕一区二区三区四区五区人 | 99国产精品免费网站| 亚洲精品乱码久久久久久蜜桃91| 欧美日韩日本国产亚洲在线 | 青青草免费观看视频| 欧美丰满嫩嫩电影| 色综合888| 欧美大片网站在线观看| a∨色狠狠一区二区三区| 国产精品美女诱惑| 久久久久久久久久久9不雅视频| 浮妇高潮喷白浆视频| 国产一区999| 国产精品久久久久久久av| 亚洲成人在线观看视频| 国产精品日韩无码| 国产香蕉精品视频一区二区三区| 欧洲在线视频| 91在线网站视频| 成人羞羞网站入口| 免费在线观看毛片网站| 成人h动漫精品一区二区| 国产人妻精品一区二区三区不卡| 欧美三电影在线| 狠狠狠综合7777久夜色撩人| 91av免费观看91av精品在线| 亚洲精品在线a| 日韩 欧美 自拍| 日本美女一区二区三区视频| 国产成人无码一区二区在线观看| 一区二区激情小说| 999免费视频| 久久精品视频在线| 国产成人精选| 日韩免费av一区二区三区| 午夜在线视频观看日韩17c| 四虎精品一区二区| 亚洲综合偷拍欧美一区色| 国产精品熟女久久久久久| 综合久久五月天| 国产亚洲精彩久久| 日韩在线第一区| 日韩va欧美va亚洲va久久| 能免费看av的网站| 色婷婷av一区二区三区大白胸| 天天干在线观看| 欧美亚洲国产视频| 美女网站色精品尤物极品姐弟| 国产真人做爰毛片视频直播| 国产成人午夜精品影院观看视频 | 国内精品久久久久久久97牛牛 | 91黄视频在线观看| 国产三级在线| 国产精品久久久久久av福利| 欧美人与物videos另类xxxxx| 国产麻花豆剧传媒精品mv在线| 久久久久久一级片| 国产精品午夜一区二区| 色诱女教师一区二区三区| 日本免费一区二区三区等视频| 中文视频一区视频二区视频三区| 国内不卡的二区三区中文字幕 | 日本黄色一区二区| 国产精品一二三区视频| 国产精品手机播放| 亚洲mv大片欧洲mv大片| 最新中文字幕日本| 亚洲va欧美va国产va天堂影院| 无码精品视频一区二区三区| 日韩av理论片| 欧美freesextv| 国产无套精品一区二区三区| 精品二区三区线观看| 男操女在线观看| 国产女精品视频网站免费| 欧美成人一品| 欧美 日本 国产| 欧美日韩精品一区二区三区蜜桃 | 天堂a√在线| 国产精品久久久久免费a∨大胸| 日韩.com| 香蕉在线观看视频| 欧美性猛交xxxx免费看久久久| 91官网在线| 国产精品区一区| 蜜桃一区二区三区在线| 欧美黄色免费在线观看| 精品一区二区三区四区在线| 久久精品资源| 99久久国产综合精品五月天喷水| 久久网站最新地址| 国产精品久久久久久久一区二区 | 国产亚洲人成a一在线v站| 亚洲精品一区国产| 男女无套免费视频网站动漫| 一区二区视频免费在线观看| 欧美日韩在线中文字幕| 成人在线国产精品| 久久99伊人| 在线观看成人毛片| 国产一区二区三区视频在线观看 | 久久久久久久国产视频| 亚洲免费人成在线视频观看| 精品一区二区三区四区五区| 无码无遮挡又大又爽又黄的视频|