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

ReactiveCocoa中潛在的內存泄漏及解決方案

安全 移動安全
ReactiveCocoa是GitHub開源的一個函數響應式編程框架,剛開始使用的時候,可能容易疏忽掉一些隱藏的細節,從而導致內存泄漏等問題。本文就帶大家深入了解下ReactiveCocoa中隱藏的一些細節,幫助大家以更加正確的姿勢使用ReactiveCocoa。

ReactiveCocoa是GitHub開源的一個函數響應式編程框架,目前在美團App中大量使用。用過它的人都知道很好用,也確實為我們的生活帶來了很多便利,特別是跟MVVM模式結合使用,更是如魚得水。不過剛開始使用的時候,可能容易疏忽掉一些隱藏的細節,從而導致內存泄漏等問題。本文就帶大家深入了解下ReactiveCocoa中隱藏的一些細節,幫助大家以更加正確的姿勢使用ReactiveCocoa。

以下代碼和示例基于ReactiveCocoa v2.5

RACObserve引發的血案

RACObserve是ReactiveCocoa中一個相當常用也相當好用的宏,它可以用來監聽屬性值的改變,然后傳遞給訂閱者。不過在使用的時候有一點需要稍微注意一下,為了直觀說明,先上一個小Demo。

  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.     RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { //1 
  4.         MTModel *model = [[MTModel alloc] init]; // MTModel有一個名為的title的屬性 
  5.         [subscriber sendNext:model]; 
  6.         [subscriber sendCompleted]; 
  7.         return nil; 
  8.     }]; 
  9.     self.flattenMapSignal = [signal flattenMap:^RACStream *(MTModel *model) { //2 
  10.         return RACObserve(model, title); 
  11.     }]; 
  12.     [self.flattenMapSignal subscribeNext:^(id x) { //3 
  13.         NSLog(@"subscribeNext - %@", x); 
  14.     }]; 
  1. 創建一個signal,該signal被訂閱后會發送一個MTModel的實例;
  2. 對第一步創建的signal進行flattenMap操作,并將返回的信號保留(之所以要保留,是因為可能希望在其它地方訂閱,不過這里為了簡單,就直接在第三步進行訂閱);
  3. 對第二步產生的信號(self.flattenMapSignal)進行訂閱。

這段代碼看起來很正常,工作也相當良好,但是當從添加了這段代碼的控制器返回時,控制器并沒有被釋放。這又是為啥呢?看下RACObserve的定義:

  1. #define RACObserve(TARGET, KEYPATH) \ 
  2.     ({ \ 
  3.         _Pragma("clang diagnostic push") \ 
  4.         _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ 
  5.         __weak id target_ = (TARGET); \ 
  6.         [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ 
  7.         _Pragma("clang diagnostic pop") \ 
  8.     }) 

注意這一句:

  1. [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; 

如果將宏簡單展開就變成了下面這樣:

  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.     RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id < RACSubscriber > subscriber) { //1 
  4.         GJModel *model = [[GJModel alloc] init]; 
  5.         [subscriber sendNext:model]; 
  6.         [subscriber sendCompleted]; 
  7.         return nil; 
  8.     }];  
  9.     self.flattenMapSignal = [signal flattenMap:^RACStream *(GJModel *model) {//2 
  10.         __weak GJModel *target_ = model; 
  11.         return [target_ rac_valuesForKeyPath:@keypath(target_, title) observer:self]; 
  12.     }]; 
  13.     [self.flattenMapSignal subscribeNext:^(id x) {//3 
  14.         NSLog(@"subscribeNext - %@", x); 
  15.     }]; 

看到這里,應該發現哪里不對了吧?沒錯,flattenMap操作接收的block里面出現了self,對self進行了持有,而flattenMap操作返回的信號又由self的屬性flattenMapSignal進行了持有,這就造成了循環引用。

注意:2是間接持有,從邏輯上來講,flattenMapSignal會有一個didSubscribeBlock,為了讓傳遞給flattenMap操作的block有意義,didSubscribeBlock會對該block進行持有,從而也就間接持有了self,感興趣的讀者可以去看下相關源碼。

OK,找到了問題所在,解決起來也就簡單了,使用@weakify和@strongify即可:

  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.     RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id < RACSubscriber > subscriber) { 
  4.         GJModel *model = [[GJModel alloc] init]; 
  5.         [subscriber sendNext:model]; 
  6.         [subscriber sendCompleted]; 
  7.         return nil; 
  8.     }]; 
  9.     @weakify(self); // 
  10.     self.signal = [signal flattenMap:^RACStream *(GJModel *model) { 
  11.         @strongify(self); // 
  12.         return RACObserve(model, title); 
  13.     }]; 
  14.     [self.signal subscribeNext:^(id x) { 
  15.         NSLog(@"subscribeNext - %@", x); 
  16.     }]; 

這里之所以容易疏忽,是因為在block里沒有很直觀的看到self,但是RACObserve的定義里面卻用到了self。

其實RACObserve的解釋中已經很明確地說明了這個問題。

  1. /// Creates a signal which observes `KEYPATH` on `TARGET` for changes. 
  2. /// 
  3. /// In either case, the observation continues until `TARGET` _or self_ is 
  4. /// deallocated. If any intermediate object is deallocated instead, it will be 
  5. /// assumed to have been set to nil. 
  6. /// 
  7. /// Make sure to `@strongify(self)` when using this macro within a block! The 
  8. /// macro will _always_ reference `self`, which can silently introduce a retain 
  9. /// cycle within a block. As a result, you should make sure that `self` is a weak 
  10. /// reference (e.g., created by `@weakify` and `@strongify`) before the 
  11. /// expression that uses `RACObserve`. 
  12. /// 
  13. /// Examples 
  14. /// 
  15. ///    // Observes self, and doesn't stop until self is deallocated. 
  16. ///    RACSignal *selfSignal = RACObserve(self, arrayController.items); 
  17. /// 
  18. ///    // Observes the array controller, and stops when self _or_ the array 
  19. ///    // controller is deallocated. 
  20. ///    RACSignal *arrayControllerSignal = RACObserve(self.arrayController, items); 
  21. /// 
  22. ///    // Observes obj.arrayController, and stops when self _or_ the array 
  23. ///    // controller is deallocated. 
  24. ///    RACSignal *signal2 = RACObserve(obj.arrayController, items); 
  25. /// 
  26. ///    @weakify(self); 
  27. ///    RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) { 
  28. ///        // Avoids a retain cycle because of RACObserve implicitly referencing 
  29. ///        // self. 
  30. ///        @strongify(self); 
  31. ///        return RACObserve(arrayController, items); 
  32. ///    }]; 
  33. /// 
  34. /// Returns a signal which sends the current value of the key path on 
  35. /// subscription, then sends the new value every time it changes, and sends 
  36. /// completed if self or observer is deallocated. 
  37. #define RACObserve(TARGET, KEYPATH) \ 
  38.     ({ \ 
  39.         _Pragma("clang diagnostic push") \ 
  40.         _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \ 
  41.         __weak id target_ = (TARGET); \ 
  42.         [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \ 
  43.         _Pragma("clang diagnostic pop") \ 
  44.     }) 

通過這個例子,相信你已經知道了RACObserve的正確使用姿勢,也意識到了閱讀文檔的重要性。

如果說RACObserve潛在的內存泄漏只要稍加留意,使用的時候查看下文檔就能避免;那么下面的情況,就相當隱蔽了,就算是看了文檔也不一定能看出來。

不信?接著往下看。

RACSubject帶來的悲劇

RACSubject是非RAC到RAC的一個橋梁,使用起來也很簡單方便,基本的用法如下:

  1. - (void)viewDidLoad { 
  2.     [super viewDidLoad]; 
  3.     RACSubject *subject = [RACSubject subject]; //1 
  4.     [subject.rac_willDeallocSignal subscribeCompleted:^{ //2 
  5.         NSLog(@"subject dealloc"); 
  6.     }]; 
  7.     [subject subscribeNext:^(id x) { //3 
  8.         NSLog(@"next = %@", x); 
  9.     }]; 
  10.     [subject sendNext:@1]; //4 
  1. 創建一個RACSubject的實例;
  2. 訂閱subject的dealloc信號,在subject被釋放的時候會發送完成信號;
  3. 訂閱subject;
  4. 使用subject發送一個值。

接下來看一下輸出的結果:

  1. 2016-06-13 09:15:25.426 RAC[5366:245360] next = 1 
  2. 2016-06-13 09:15:25.428 RAC[5366:245360] subject dealloc 

工作相當良好,接下來改造下程序,要求對subject發送的所有值進行乘3,這用map很容易就實現了。

  1. - (void)viewDidLoad { 
  2.     [super viewDidLoad]; 
  3.     RACSubject *subject = [RACSubject subject];  
  4.     [subject.rac_willDeallocSignal subscribeCompleted:^{  
  5.         NSLog(@"subject dealloc"); 
  6.     }]; 
  7.  
  8.     [[subject map:^id(NSNumber *value) {  
  9.         return @([value integerValue] * 3); 
  10.     }] subscribeNext:^(id x) {  
  11.         NSLog(@"next = %@", x); 
  12.     }]; 
  13.     [subject sendNext:@1];  

跟之前大體不變,只是對subject進行了map操作然后再訂閱,看下輸出結果:

  1. 2016-06-13 09:21:42.450 RAC[5404:248584] next = 3 

的確是進行了乘3操作,符合預期,但是這里有一個很嚴重的問題,subject dealloc沒有輸出,也就是說subject沒有釋放。

這不科學啊!subject看上去沒有被任何對象持有。

那究竟是什么情況?下面我們將RACSubject換成RACSignal試試:

  1. - (void)viewDidLoad { 
  2.     [super viewDidLoad]; 
  3.     RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 
  4.         [subscriber sendNext:@1]; 
  5.         return nil; 
  6.     }]; 
  7.     [signal.rac_willDeallocSignal subscribeCompleted:^{ 
  8.         NSLog(@"signal dealloc"); 
  9.     }]; 
  10.     [[signal map:^id(NSNumber *value) { 
  11.         return @([value integerValue] * 3); 
  12.     }] subscribeNext:^(id x) { 
  13.         NSLog(@"next = %@", x); 
  14.     }]; 

邏輯跟之前一樣,看一下輸出結果:

  1. 2016-06-12 23:32:31.669 RACDemo[5085:217082] next = 3 
  2. 2016-06-12 23:32:31.674 RACDemo[5085:217082] signal dealloc 

很明顯,signal被釋放了。同樣的邏輯,signal能正常釋放,subject卻不能正常釋放,太神奇了!

細心的讀者看到這里,應該會發現一個問題:上面的幾次試驗,不管是RACSubject還是RACSignal都沒有調用sendCompleted。

難道跟這個有關系?帶著這個疑問,再進行如下試驗,給RACSubject發送一個完成信號:

  1. - (void)viewDidLoad { 
  2.     [super viewDidLoad]; 
  3.     RACSubject *subject = [RACSubject subject];  
  4.     [subject.rac_willDeallocSignal subscribeCompleted:^{  
  5.         NSLog(@"subject dealloc"); 
  6.     }]; 
  7.  
  8.     [[subject map:^id(NSNumber *value) {  
  9.         return @([value integerValue] * 3); 
  10.     }] subscribeNext:^(id x) {  
  11.         NSLog(@"next = %@", x); 
  12.     }]; 
  13.     [subject sendNext:@1];  
  14.     [subject sendCompleted]; 

輸出結果:

  1. 2016-06-12 23:40:19.148 RAC_bindSample[5168:221902] next = 3 
  2. 2016-06-12 23:40:19.153 RAC_bindSample[5168:221902] subject dealloc 

subject被釋放了,確實修正了內存泄漏問題。到這里,我們可以得出結論:

使用RACSubject,如果進行了map操作,那么一定要發送完成信號,不然會內存泄漏。

雖然得出了結論,但是留下的疑問也是不少,如果你希望知道這其中的緣由,請繼續往下看。

簡單來說,留下的疑問有:

為什么對RACSubject的實例進行map操作之后會產生內存泄漏?

為什么RACSignal不管是否有map操作,都不會產生內存泄漏?

針對第一個問題,為什么發送完成可以修復內存泄漏?

帶著疑問,咱們繼續一探究竟。

講道理,RACSignal和RACSubject雖然都是信號,但是它們有一個本質的區別:

RACSubject會持有訂閱者(因為RACSubject是熱信號,為了保證未來有事件發送的時候,訂閱者可以收到信息,所以需要對訂閱者保持狀態,做法就是持有訂閱者),而RACSignal不會持有訂閱者。

關于這一點,更詳細的說明請看《細說ReactiveCocoa的冷信號與熱信號(三):怎么處理冷信號與熱信號》。

那么持不持有訂閱者,跟內存無法釋放又有啥關系呢?不急,先記著有這樣一個特性,咱們看看實現。

從上面提出第一個問題可以發現,關鍵點在于map操作,那么map操作究竟干了什么事情,看下map的實現:

  1. - (instancetype)map:(id (^)(id value))block { 
  2.     NSCParameterAssert(block != nil); 
  3.     Class class = self.class; 
  4.  
  5.     return [[self flattenMap:^(id value) { 
  6.         return [class return:block(value)]; 
  7.     }] setNameWithFormat:@"[%@] -map:", self.name]; 

很簡單,只是調用了一下flattenMap,再看下flattenMap怎么實現的:

  1. - (instancetype)flattenMap:(RACStream * (^)(id value))block { 
  2.    Class class = self.class; 
  3.    return [[self bind:^{ 
  4.        return ^(id value, BOOL *stop) { 
  5.            id stream = block(value) ?: [class empty]; 
  6.            NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream); 
  7.            return stream; 
  8.        }; 
  9.    }] setNameWithFormat:@"[%@] -flattenMap:", self.name]; 

也很簡單,只是調用了一下bind,再看看bind的實現,bind的實現位于RACSignal.m的92行左右。

  1. - (RACSignal *)bind:(RACStreamBindBlock (^)(void))block { 
  2.    NSCParameterAssert(block != NULL); 
  3.    /* 
  4.     * -bind: should: 
  5.     *  
  6.     * 1. Subscribe to the original signal of values
  7.     * 2. Any time the original signal sends a value, transform it using the binding block. 
  8.     * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received. 
  9.     * 4. If the binding block asks the bind to terminate, complete the _original_ signal. 
  10.     * 5. When _all_ signals complete, send completed to the subscriber. 
  11.     *  
  12.     * If any signal sends an error at any point, send that to the subscriber. 
  13.     */ 
  14.    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) { 
  15.        RACStreamBindBlock bindingBlock = block(); 
  16.        NSMutableArray *signals = [NSMutableArray arrayWithObject:self]; 
  17.        // 此處省略了80行代碼 
  18.        // ... 
  19.    }] setNameWithFormat:@"[%@] -bind:", self.name]; 

如果你下載了源代碼(不想下源碼的話,也可以在線查看),并且看到了這里,相信你的感覺一定是一臉懵逼的,不要激動,雖然這個方法很長,看上去也不那么好懂,但是關鍵點就那么幾個地方,掌握了關鍵點就基本能get了。

ReactiveCocoa的作者更是罕見地在實現文件了寫了一大段注釋來說明bind方法的用途,根據作者的注釋再去理解這個方法會輕松很多。

這里貼一個圖,方便大家理解:

OK,了解了bind操作的用途,也是時候回歸主題了——內存是怎么泄露的。

首先我們看到,在didSubscribe的開頭,就創建了一個數組signals,并且持有了self,也就是源信號:

  1. NSMutableArray *signals = [NSMutableArray arrayWithObject:self]; 

(p.s. 如果你不知道didSubscribe是什么,也不了解ReactiveCocoa中信號的訂閱過程,可以先看下《RACSignal的Subscription深入分析》)

接下來會對源信號進行訂閱:

  1. RACDisposable *bindingDisposable = [self subscribeNext:^(id x) { 
  2.     // Manually check disposal to handle synchronous errors. 
  3.     if (compoundDisposable.disposed) return
  4.     BOOL stop = NO
  5.     id signal = bindingBlock(x, &stop); 
  6.     @autoreleasepool { 
  7.         if (signal != nil) addSignal(signal); 
  8.         if (signal == nil || stop) { 
  9.             [selfDisposable dispose]; 
  10.             completeSignal(self, selfDisposable); 
  11.         } 
  12.     } 
  13. } error:^(NSError *error) { 
  14.     //... 
  15. } completed:^{ 
  16.     //... 
  17. }]; 

訂閱者會持有nextBlock、errorBlock、completedBlock三個block,為了簡單,我們只討論nextBlock。

從nextBlock中的completeSignal(self, selfDisposable);這一行代碼可以看出,nextBlock對self,也就是源信號進行了持有,再看到if (signal != nil) addSignal(signal);這一行,nextBlock對addSignal進行了持有,addSignal是在訂閱self之前定義的一個block。

  1. void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { 
  2.     @synchronized (signals) { 
  3.         [signals addObject:signal]; 
  4.     } 
  5.     //... 
  6. }; 

addSignal這個block里面對一開始創建的數組signals進行了持有,用一幅圖來描述下剛才所說的關系:

如果這個signal是一個RACSignal,那么是沒有任何問題的;如果是signal是一個RACSubject,那問題就來了。還記得前面說過的RACSignal和RACSubject的區別嗎?RACSubject會持有訂閱者,而RACSignal不會持有訂閱者,如果signal是一個RACSubject,那么圖應該是這樣的:

很明顯,產生了循環引用!!!到這里,也就解答了前面提出的三個問題的前兩個:

對一個信號進行了map操作,那么最終會調用到bind。

如果源信號是RACSubject,由于RACSubject會持有訂閱者,所以產生了循環引用(內存泄漏);

如果源信號是RACSignal,由于RACSignal不會持有訂閱者,那么也就不存在循環引用。

還剩下最后一個問題:如果源信號是RACSubject,為什么發送完成可以修復內存泄漏?

來看下訂閱者收到完成信號之后干了些什么:

  1. RACDisposable *bindingDisposable = [self subscribeNext:^(id x) { 
  2.     //... 
  3. } error:^(NSError *error) { 
  4.     //... 
  5. } completed:^{ 
  6.     @autoreleasepool { 
  7.         completeSignal(self, selfDisposable); 
  8.     } 
  9. }]; 

很簡單,只是調用了一下completeSignal這個block。再看下這個block內部在干嘛:

  1. void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) { 
  2.     BOOL removeDisposable = NO
  3.     @synchronized (signals) { 
  4.         [signals removeObject:signal]; //1 
  5.         if (signals.count == 0) { 
  6.             [subscriber sendCompleted]; //2 
  7.             [compoundDisposable dispose]; //3 
  8.         } else { 
  9.             removeDisposable = YES; 
  10.         } 
  11.     } 
  12.     if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable]; //4 
  13. }; 

//1這里從signals這個數組中移除傳入的signal,也就斷掉了signals持有subject這條線。

//2、//3、//4其實干的事情差不多,都是拿到對應的disposable調用dispose,這樣資源就得到了回收,subject就不會再持有subscriber,subscriber也會對自己的nextBlock、errorBlock、completedBlock三個block置為nil,就不會存在引用關系,所有的對象都得到了釋放。

有興趣的同學可以去了解下RACDisposable,它也是ReactiveCocoa中的重要一員,對理解源碼有很大的幫助。

map只是一個很典型的操作,其實在ReactiveCocoa的實現中,幾乎所有的操作底層都會調用到bind這樣一個方法,包括但不限于:

  1. map、filter、merge、combineLatest、flattenMap …… 

所以在使用ReactiveCocoa的時候也一定要仔細,對信號操作完成之后,記得發送完成信號,不然可能在不經意間就導致了內存泄漏。

RACSubject就是一個比較典型直接的例子。除此之外,如果在對一個信號進行類似replay這樣的操作之后,也一定要保證源信號發送完成;不然,也是會有內存泄漏的。

  1. RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 
  2.     [subscriber sendNext:@1]; 
  3.     [subscriber sendCompleted]; // 保證源信號發送完成 
  4.     return nil; 
  5. }]; 
  6.  
  7. RACSignal *replaySignal = [signal replay]; // 這里返回的其實是一個RACReplaySubject 
  8.  
  9. [[replaySignal map:^id(NSNumber *value) { 
  10.     return @([value integerValue] * 3); 
  11. }] subscribeNext:^(id x) { 
  12.     NSLog(@"subscribeNext - %@", x); 
  13. }]; 

總之,一句話:使用ReactiveCocoa必須要保證信號發送完成或者發送錯誤。

責任編輯:武曉燕 來源: 美團點評技術團隊
相關推薦

2024-08-05 10:40:58

2021-06-18 10:45:29

Java內存泄漏C++

2010-09-26 15:38:33

JVM內存泄漏

2009-07-09 18:25:31

MyEclipse消耗

2009-06-16 11:01:14

Java內存溢出

2025-09-28 01:45:00

2010-09-26 16:04:48

JVM內存溢出

2024-01-30 10:12:00

Java內存泄漏

2010-08-26 14:00:28

CSSmargin

2010-07-13 16:36:07

SQLServer占內

2010-11-18 18:15:24

2016-09-27 21:14:53

JavaURL

2024-02-22 11:23:16

2010-06-12 12:46:04

Grub Rescue

2015-05-12 16:31:22

Elasticsear開源分布式搜索引擎

2018-12-12 15:50:13

2018-10-12 14:34:13

2018-10-24 19:59:45

Kubernetes混合云云擴展

2019-10-08 16:05:19

Redis數據庫系統

2010-07-29 15:56:04

FlexSocket
點贊
收藏

51CTO技術棧公眾號

亚洲欧美bt| 97久久精品| 国产精品久久久久久久久果冻传媒| 国产精品视频最多的网站| 后入内射无码人妻一区| 中文字幕视频精品一区二区三区| 欧美日韩免费观看中文| 亚洲欧洲国产精品久久| 亚洲国产成人精品一区二区三区| 久久久久久一区二区| 久久激情视频免费观看| 在线免费看黄色片| 精品国产黄a∨片高清在线| 亚洲韩国一区二区三区| 五月天丁香综合久久国产| 精品女同一区二区三区| 久久在线91| 欧美激情一二三| 精品国产aaa| 精品精品国产毛片在线看| 精品视频一区二区三区免费| 欧美乱大交xxxxx潮喷l头像| 午夜小视频在线| 91一区二区在线| 亚洲xxxxx| 天堂av免费在线观看| 亚洲每日在线| 欧美xxxx14xxxxx性爽| 亚洲永久精品ww.7491进入| 日韩一区二区三区精品| 欧美视频在线观看一区二区| 国产极品尤物在线| 亚洲91av| 亚洲免费伊人电影| 日韩激情视频| 天堂在线中文资源| 成人一二三区视频| 亚洲一区二区久久久久久| 无码日韩精品一区二区| 亚洲欧洲一区| 九色精品美女在线| 国产十六处破外女视频| 日韩电影免费在线观看| 亚洲人成电影在线| 人人妻人人澡人人爽人人精品 | 91精品国产九九九久久久亚洲| 国产免费久久久久| 99久久亚洲精品| 丝袜美腿精品国产二区| 日韩欧美黄色网址| 欧美偷拍自拍| 在线免费观看羞羞视频一区二区| 久久久久久久久久久久| 婷婷精品在线观看| 亚洲精品国产拍免费91在线| 国产av一区二区三区传媒| 亚洲欧美日本国产| 欧美成人性战久久| 中文字幕18页| 韩国精品福利一区二区三区| 亚洲福利视频二区| 免费成人蒂法网站| 亚洲人成网站77777在线观看| 亚洲精美色品网站| 人妻体内射精一区二区| 精品视频亚洲| 久久精品国产久精国产思思| 欧洲猛交xxxx乱大交3| 欧美一区不卡| 久久久久五月天| a v视频在线观看| 欧美亚洲自偷自偷| 国产精品久久一区主播| 国产精品天天操| 成人一区二区三区中文字幕| 快播亚洲色图| √新版天堂资源在线资源| 中文字幕视频一区二区三区久| 男插女免费视频| 国产91足控脚交在线观看| 亚洲小说欧美激情另类| 欧美日韩中文在线视频| 全球最大av网站久久| 欧美一区二区三区影视| 国产精品手机在线观看| 国产日产精品_国产精品毛片| 国产亚洲精品久久久久久牛牛| 懂色av蜜臀av粉嫩av永久| 欧美成人午夜| 欧美一区第一页| 97人妻一区二区精品免费视频| 国产成人av网站| 欧美二区三区| 182tv在线播放| 色综合欧美在线视频区| 国产精品久久久久久久av福利| www国产精品| 一本一本久久a久久精品综合小说| 91视频青青草| 国产日韩亚洲欧美精品| 国产日韩在线观看av| 亚洲精品一区二区三区不卡| 国产日韩成人精品| 丰满少妇大力进入| 国产一区影院| 日韩精品中文字幕视频在线| 91传媒免费观看| 亚洲视频www| 亚洲精品免费网站| 国产天堂在线| 午夜国产精品一区| 一二三av在线| 久久91麻豆精品一区| 欧美乱妇高清无乱码| 亚洲成熟少妇视频在线观看| 国产不卡在线视频| 亚洲国产另类久久久精品极度| 17videosex性欧美| 欧美精品欧美精品系列| 极品人妻一区二区三区| 国产精品v日韩精品v欧美精品网站 | 成人在线直播| 欧美三区免费完整视频在线观看| 久久久久国产精品无码免费看| 久久精品青草| 国产精品激情av电影在线观看 | 一区二区三区无毛| 亚洲男人天堂久| 精品无码黑人又粗又大又长| 美女网站一区二区| 日本一区不卡| 东京一区二区| 亚洲激情电影中文字幕| 免费人成视频在线| 韩国毛片一区二区三区| 亚洲精品tv久久久久久久久| 欧美极度另类| 日韩精品高清在线观看| 动漫精品一区一码二码三码四码| 久久综合综合久久综合| 午夜视频久久久| 456亚洲精品成人影院| 精品无人区太爽高潮在线播放| 国产亚洲精品成人| 国产91对白在线观看九色| 国产在线拍揄自揄拍无码| 久久三级毛片| 色噜噜亚洲精品中文字幕| 黄色一区二区视频| 国产精品网曝门| 欧美第一页浮力影院| 不卡一区2区| 国产日韩欧美日韩| 黄av在线播放| 欧美一区二区大片| 成人免费看片98| 成人av电影在线| 黄色av网址在线播放| 亚洲精品进入| 国产精品pans私拍| youjizz在线播放| 欧美日韩视频第一区| 亚洲波多野结衣| 国产在线精品免费| 蜜臀av性久久久久蜜臀av| 亚洲专区**| 韩国美女主播一区| 肉丝一区二区| 欧美亚洲动漫另类| 欧美一级片在线视频| 国产成人免费视频| 2022亚洲天堂| 欧美激情在线精品一区二区三区| 国产精品视频免费在线| av中文字幕在线观看| 亚洲精品一区二区三区影院| 久久久精品福利| 亚洲国产精品激情在线观看| 日本高清一区二区视频| 亚洲香蕉网站| 欧美精品二区三区四区免费看视频 | 亚洲免费观看在线视频| 国产精品一区二区无码对白| 久久aⅴ国产紧身牛仔裤| 亚洲伊人婷婷| 好吊妞视频这里有精品| 国产精品户外野外| 欧洲黄色一区| 亚洲视频在线免费观看| av中文字幕播放| 欧美午夜精品伦理| 三级黄色在线观看| 91麻豆国产福利在线观看| 男女视频在线看| 亚洲国产免费看| 一区二区三区四区在线视频| 久久综合五月婷婷| 成人国产在线激情| 手机在线观看av| 久久久国产视频| 男操女在线观看| 欧美大胆人体bbbb| 伊人网站在线观看| 欧美日韩国产一区中文午夜| 亚洲 欧美 变态 另类 综合| 久久先锋影音av| wwwxxx色| 久久精品国产精品亚洲精品| 夫妻免费无码v看片| 中文字幕日韩欧美精品高清在线| 日本三级中国三级99人妇网站| 日韩中文字幕无砖| 国产精品丝袜白浆摸在线| 日本不卡网站| 欧美激情亚洲自拍| 顶级网黄在线播放| 中文字幕在线视频日韩| 色av男人的天堂免费在线| 精品国精品国产| 国产麻豆91视频| 欧美日韩国产高清一区| 午夜精品一区二| 激情懂色av一区av二区av| 九九热国产在线| 亚洲欧美在线视频观看| 国产又粗又猛又爽又黄av| 99精品视频在线免费观看| 极品人妻一区二区| 国产专区综合网| 久久久精品高清| 奇米综合一区二区三区精品视频| 免费日韩中文字幕| 国产亚洲亚洲| 国产精品久久中文字幕| 国产精品v亚洲精品v日韩精品 | 8x8x8国产精品| 中文字幕乱码无码人妻系列蜜桃| 色先锋资源久久综合| 亚洲精品男人的天堂| 亚洲大片在线观看| 日本免费一二三区| 亚洲永久免费av| 久久中文字幕无码| 亚洲国产精品欧美一二99| 久久久无码精品亚洲国产| 亚洲综合成人在线| 久视频在线观看| 天天亚洲美女在线视频| 日韩精品久久久久久久| 欧美性色xo影院| 精品国产乱子伦| 在线观看国产一区二区| 特级西西444www大胆免费看| 欧美视频在线播放| 国产又粗又猛又色又| 日韩午夜在线观看| 精品人妻无码一区二区| 精品国产乱码久久久久久图片 | 区一区二在线观看| 在线观看日韩av先锋影音电影院| 亚洲综合成人av| 7777精品久久久大香线蕉 | 日韩av在线看| 美丽的姑娘在线观看免费动漫| 一本色道久久综合狠狠躁篇的优点 | 久久午夜国产精品| 人与嘼交av免费| 亚洲人成7777| 国产一区二区99| 欧美三区在线观看| 国产成人麻豆精品午夜在线| 亚洲第一中文字幕| 国产福利在线| 欧美成年人视频网站| sm久久捆绑调教精品一区| 国产成人免费av| 国产精品18| 精品中文字幕一区| 日本欧美国产| 97中文字幕在线| 日本欧美一区二区三区乱码| 国产成人在线综合| av一二三不卡影片| 日韩福利在线视频| 亚洲一区二区三区中文字幕 | 国产精品色网| aaa一级黄色片| 99亚偷拍自图区亚洲| 99久久精品免费视频| 一区二区三区在线观看欧美| 精品国产乱码一区二区| 91麻豆精品91久久久久同性| 五月婷婷深深爱| 久久精品99国产精品酒店日本 | 国产精品日韩欧美综合| 91成人午夜| 亚洲一区在线直播| 国产欧美欧美| 深爱五月综合网| 久久亚洲私人国产精品va媚药| 爱爱视频免费在线观看| 色偷偷88欧美精品久久久| www.香蕉视频| 日日摸夜夜添一区| **欧美日韩在线观看| 岛国一区二区三区高清视频| 日韩大片在线播放| 精品国产成人av在线免| 成人黄色网址在线观看| 国产午夜精品理论片在线| 色美美综合视频| 偷拍自拍在线视频| 欧美裸身视频免费观看| 91欧美精品| 欧美日韩精品久久| 影音先锋亚洲一区| 伊人精品视频在线观看| 国产精品久久久久久户外露出| 在线观看中文字幕视频| 精品少妇一区二区三区日产乱码 | 97精品在线视频| 视频亚洲一区二区| 亚洲自拍三区| 日韩国产精品大片| 精品人妻一区二区三区视频| 亚洲一二三专区| 亚洲AV午夜精品| 欧美精品在线免费| 久久久久久久久成人| 亚洲一区二区三区涩| 日本vs亚洲vs韩国一区三区二区 | 国产成人亚洲精品| 日韩成人一级| 成人网站免费观看入口| 国产.精品.日韩.另类.中文.在线.播放| 极品尤物一区二区| 欧美日韩一区二区三区在线| 好男人免费精品视频| 国产成人一区二| 免费成人网www| 成人在线激情网| 久久这里只有精品首页| 三级视频在线观看| 日韩精品中文字幕视频在线| 亚洲风情在线资源| 欧美精品亚洲| 可以免费看不卡的av网站| 国产aⅴ激情无码久久久无码| 色综合天天狠狠| yw193.com尤物在线| 国产在线高清精品| 亚洲五月综合| 精品无码av一区二区三区不卡| 亚洲影院久久精品| 三级小视频在线观看| 91sa在线看| 国产a久久精品一区二区三区| 欧美激情国产精品日韩| 国产欧美一区二区精品性色 | 久久99久久精品| 婷婷激情四射网| 精品人在线二区三区| 国产乱码午夜在线视频| 免费久久99精品国产自| 老牛国产精品一区的观看方式| 天天操天天干天天操天天干| 51午夜精品国产| 182在线视频观看| 欧美日韩一区综合| 久久精品999| 久久亚洲av午夜福利精品一区| 亚洲国产精品yw在线观看 | 狠狠色丁香久久综合频道| 少妇精品无码一区二区三区| 欧美日韩国产精品一区二区三区四区 | 精品午夜久久福利影院| 亚洲一区 视频| 亚洲色图第三页| 国产成人久久精品一区二区三区| 精品久久久久久无码中文野结衣| 久久久久久免费网| 国产特级黄色片| 欧美在线一级视频| 91成人超碰| 西西444www无码大胆| 日韩一区二区三区四区| 亚洲日本天堂| 老汉色影院首页| 国产亚洲一二三区| 国产福利资源在线| 日韩美女视频免费看| 亚洲视频在线免费| 精品国产无码在线观看| 欧美一级淫片007| 国产v综合v| www污在线观看| 国产精品国产三级国产aⅴ中文| 天天干天天爱天天操| 成人久久久久爱| 久久精品在线|