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

首頁 > 系統 > iOS > 正文

IOS觀察者設計模式

2020-07-26 03:31:12
字體:
來源:轉載
供稿:網友

什么是觀察者模式?我們先打個比方,這就像你訂報紙。比如你想知道美國最近放生了些新聞,你可能會訂閱一份美國周刊,然后一旦美國有了新的故事,美國周刊就發一刊,并郵寄給你,當你收到這份報刊,然后你就能夠了解美國最新的動態。其實這就是觀察者模式,A對B的變化感興趣,就注冊為B的觀察者,當B發生變化時通知A,告知B發生了變化。這是一種非常典型的觀察者的用法,我把這種使用方法叫做經典觀察者模式。當然與之相對的還有另外一種觀察者模式――廣義觀察者模式。

從經典的角度看,觀察者模式是一種通知變化的模式,一般認為只在對象發生變化感興趣的場合有用。主題對象知道有觀察者存在,設置會維護觀察者的一個隊列;而從廣義的角度看,觀察者模式是中傳遞變化數據的模式,需要查看對象屬性時就會使用的一種模式,主題對象不知道觀察者的存在,更像是圍觀者。需要知道主題對象的狀態,所以即使在主題對象沒有發生改變的時候,觀察者也可能會去訪問主題對象。換句話說廣義觀察者模式,是在不同的對象之間傳遞數據的一種模式。

觀察者模式應當是在面向對象編程中被大規模使用的設計模式之一。從方法論的角度出發,傳統的認知論認為,世界是由對象組成的,我們通過不停的觀察和了解就能夠了解對象的本質。整個人類的認知模型就是建立在“觀察”這種行為之上的。我們通過不停與世界中的其他對象交互,并觀察之來了解這個世界。同樣,在程序的世界中,我們構建的每一個實例,也是通過不不停的與其他對象交互(查看其他對象的狀態,或者改變其他對象的狀態),并通過觀察其他實例的變化并作出響應,以來完成功能。這也就是,為什么會把觀察模式單獨提出來,做一個專門的剖析的原因――在我看來他是很多其他設計模式的基礎模式,并且是編程中極其重要的一種設計模式。

經典觀察者模式

經典觀察者模式被認為是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。經典觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己或者做出相應的一些動作。在文章一開始舉的例子就是典型觀察者模式的應用。

而在IOS開發中我們可能會接觸到的經典觀察者模式的實現方式,有這么幾種:NSNotificationCenter、KVO、Delegate等

感知通知方式

在經典觀察者模式中,因為觀察者感知到主題對象變化方式的不同,又分為推模型和拉模型兩種方式。

推模型

主題對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或者部分數據。推模型實現了觀察者和主題對象的解耦,兩者之間沒有過度的依賴關系。但是推模型每次都會以廣播的方式,向所有觀察者發送通知。所有觀察者被動的接受通知。當通知的內容過多時,多個觀察者同時接收,可能會對網絡、內存(有些時候還會涉及IO)有較大影響。

在IOS中典型的推模型實現方式為NSNotificationCenter和KVO。

NSNotificationCenter

NSnotificationCenter是一種典型的有調度中心的觀察者模式實現方式。以NSNotificationCenter為中心,觀察者往Center中注冊對某個主題對象的變化感興趣,主題對象通過NSNotificationCenter進行變化廣播。這種模型就是文章開始發布訂閱報紙在OC中的一種類似實現。所有的觀察和監聽行為都向同一個中心注冊,所有對象的變化也都通過同一個中心向外廣播。

SNotificationCenter就像一個樞紐一樣,處在整個觀察者模式的核心位置,調度著消息在觀察者和監聽者之間傳遞。

一次完整的觀察過程如上圖所示。整個過程中,關鍵的類有這么幾個(介紹順序按照完成順序):

觀察者Observer,一般繼承自NSObject,通過NSNotificationCenter的addObserver:selector:name:object接口來注冊對某一類型通知感興趣.在注冊時候一定要注意,NSNotificationCenter不會對觀察者進行引用計數+1的操作,我們在程序中釋放觀察者的時候,一定要去報從center中將其注銷了。

- (void) handleMessage:(NSNotification*)nc{//解析消息內容NSDictionary* userInfo = [nc userInfo];}- (void) commonInit{//注冊觀察者[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMessage:) name:kDZTestNotificatonMessage object:nil];}

通知中心NSNotificationCenter,通知的樞紐。
主題對象,被觀察的對象,通過postNotificationName:object:userInfo:發送某一類型通知,廣播改變。

- (void) postMessage{[[NSNotificationCenter defaultCenter] postNotificationName:kDZTestNotificatonMessage object:Nil userInfo:@{}];}

通知對象NSNotification,當有通知來的時候,Center會調用觀察者注冊的接口來廣播通知,同時傳遞存儲著更改內容的NSNotification對象。

apple版實現的NotificationCenter讓我用起來不太爽的幾個小問題

在使用NSNotificationCenter的時候,從編程的角度來講我們往往不止是希望能夠做到功能實現,還能希望編碼效率和整個工程的可維護性良好。而Apple提供的以NSNotificationCenter為中心的觀察者模式實現,在可維護性和效率上存在以下缺點:

每個注冊的地方需要同時注冊一個函數,這將會帶來大量的編碼工作。仔細分析能夠發現,其實我們每個觀察者每次注冊的函數幾乎都是雷同的。這就是種變相的CtrlCV,是典型的丑陋和難維護的代碼。
每個觀察者的回調函數,都需要對主題對象發送來的消息進行解包的操作。從UserInfo中通過KeyValue的方式,將消息解析出來,而后進行操作。試想一下,工程中有100個地方,同時對前面中在響應變化的函數中進行了解包的操作。而后期需求變化需要多傳一個內容的時候,將會是一場維護上的災難。

當大規模使用觀察者模式的時候,我們往往在dealloc處加上一句:

[[NSNotificationCenter defaultCenter] removeObserver:self]

而在實際使用過程中,會發現該函數的性能是比較低下的。在整個啟動過程中,進行了10000次RemoveObserver操作,

@implementation DZMessage- (void) dealloc{[[NSNotificationCenter defaultCenter] removeObserver:self];}....for (int i = ; i < ; i++) {DZMessage* message = [DZMessage new];}

通過下圖可以看出這一過程消耗了23.4%的CPU,說明這一函數的效率還是很低的。

這還是只有一種消息類型的存在下有這樣的結果,如果整個NotificationCenter中混雜著多種消息類型,那么恐怕對于性能來說將會是災難性的。

for (int i = 0 ; i < 10000; i++) {DZMessage* message = [DZMessage new];[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle) name:[@(i) stringValue] object:nil];}

增加了多種消息類型之后,RemoveObserver占用了啟動過程中63.9%的CPU消耗。

而由于Apple沒有提供Center的源碼,所以修改這個Center幾乎不可能了。

改進版的有中心觀察者模式(DZNotificationCenter)

GitHub地址 在設計的時候考慮到以上用起來不爽的地方,進行了優化:

將解包到執行函數的操作進行了封裝,只需要提供某消息類型的解包block和消息類型對應的protocol,當有消息到達的時候,消息中心會進行統一解包,并直接調用觀察者相應的函數。
對觀察者的維護機制進行優化(還未做完),提升查找和刪除觀察者的效率。
DZNotificationCenter的用法和NSNotificationCenter在注冊和注銷觀察者的地方是一樣的,不一樣的地方在于,你在使用的時候需要提供解析消息的block。你可以通過兩種方式來提供。

直接注冊的方式

[DZDefaultNotificationCenter addDecodeNotificationBlock:^SEL(NSDictionary *userInfo, NSMutableArray *__autoreleasing *params) {NSString* key = userInfo[@"key"];if (params != NULL) {*params = [NSMutableArray new];}[*params addObject:key];return @selector(handleTestMessageWithKey:);} forMessage:kDZMessageTest];

實現DZNotificationInitDelegaete協議,當整個工程中大規模使用觀察者的時候,建議使用該方式。這樣有利于統一管理所有的解析方式。

- (DZDecodeNotificationBlock) decodeNotification:(NSString *)message forCenter:(DZNotificationCenter *)center{if (message == kDZMessageTest) {return ^(NSDictionary* userInfo, NSMutableArray* __autoreleasing* params){NSString* key = userInfo[@"key"];if (params != NULL) {*params = [NSMutableArray new];}[*params addObject:key];return @selector(handlePortMessage:);};}return nil;}

在使用的過程中為了,能夠保證在觀察者處能夠回調相同的函數,可以實現針對某一消息類型的protocol

@protocol DZTestMessageInterface <NSObject>- (void) handleTestMessageWithKey:(NSString*)key;@end

這樣就能夠保證,在使用觀察者的地方不用反復的拼函數名和解析消息內容了。

@interface DZViewController () <DZTestMessageInterface>@end@implementation DZViewController....- (void) handleTestMessageWithKey:(NSString *)key{self.showLabel.text = [NSString stringWithFormat:@"get message with %@", key];}....

KVO

KVO的全稱是Key-Value Observer,即鍵值觀察。是一種沒有中心樞紐的觀察者模式的實現方式。一個主題對象管理所有依賴于它的觀察者對象,并且在自身狀態發生改變的時候主動通知觀察者對象。 讓我們先看一個完整的示例:

static NSString* const kKVOPathKey = @"key";@implementation DZKVOTest- (void) setMessage:(DZMessage *)message{if (message != _message) {if (_message) {[_message removeObserver:self forKeyPath:kKVOPathKey];}if (message) {[message addObserver:self forKeyPath:kKVOPathKey options:NSKeyValueObservingOptionNew context:Nil];}_message = message;}}- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{if ([keyPath isEqual:kKVOPathKey] && object == _message) {NSLog(@"get %@",change);}}- (void) postMessage{_message.key = @"asdfasd";}@end

完成一次完整的改變通知過程,經過以下幾次過程:

注冊觀察者[message addObserver:self forKeyPath:kKVOPathKey options:NSKeyValueObservingOptionNew context:Nil];
更改主題對象屬性的值,即觸發發送更改的通知 _message.key = @"asdfasd";
在制定的回調函數中,處理收到的更改通知

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{if ([keyPath isEqual:kKVOPathKey] && object == _message) {NSLog(@"get %@",change);}}

注銷觀察者 [_message removeObserver:self forKeyPath:kKVOPathKey];

KVO實現原理

一般情況下對于使用Property的屬性,objc會為其自動添加鍵值觀察功能,你只需要寫一句@property (noatomic, assign) float age 就能夠獲得age的鍵值觀察功能。而為了更深入的探討一下,KVO的實現原理我們先手動實現一下KVO:

@implementation DZKVOManual- (void) setAge:(int)age{[self willChangeValueForKey:kKVOPathAge];if (age !=_age) {_age = age;}[self didChangeValueForKey:kKVOPathAge];}//經驗證 會先去調用automaticallyNotifiesObserversForKey:當該函數沒有時才會調用automaticallyNotifiesObserversOfAge。這個函數應該是編譯器,自動增加的一個函數,使用xcode能夠自動提示出來。的確很強大。//+(BOOL) automaticallyNotifiesObserversOfAge//{// return NO;//}+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{if (key ==kKVOPathAge) {return NO;}return [super automaticallyNotifiesObserversForKey:key];}@end

首先,需要手動實現屬性的 setter 方法,并在設置操作的前后分別調用 willChangeValueForKey: 和 didChangeValueForKey方法,這兩個方法用于通知系統該 key 的屬性值即將和已經變更了;

其次,要實現類方法 automaticallyNotifiesObserversForKey,并在其中設置對該 key 不自動發送通知(返回 NO 即可)。這里要注意,對其它非手動實現的 key,要轉交給 super 來處理。

在這里的手動實現,主要是手動實現了主題對象變更向外廣播的過程。后續如何廣播到觀察者和觀察者如何響應我們沒有實現,其實這兩個過程apple已經封裝的很好了,猜測一下的話,應該是主題對象會維護一個觀察者的隊列,當本身屬性發生變動,接受到通知的時候,找到相關屬性的觀察者隊列,依次調用observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context來廣播更改。 還有一個疑問,就是在自動實現KVO的時候,系統是否和我們手動實現做了同樣的事情呢?

自動實現KVO及其原理

我們仔細來觀察一下在使用KVO的過程中類DZMessage的一個實例發生了什么變化: 在使用KVO之前:

當調用Setter方法,并打了斷點的時候:

神奇的發現類的isa指針發生了變化,我們原本的類叫做DZMessage,而使用KVO后類名變成了NSKVONotifying_DZMessage。這說明objc在運行時對我們的類做了些什么。

我們從Apple的文檔Key-Value Observing Implementation Details找到了一些線索。

Automatic key-value observing is implemented using a technique called isa-swizzling. The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table.This dispatch table essentially contains pointers to the methods the class implements, among other data. When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance. You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.

當某一個類的實例第一次使用KVO的時候,系統就會在運行期間動態的創建該類的一個派生類,該類的命名規則一般是以NSKVONotifying為前綴,以原本的類名為后綴。并且將原型的對象的isa指針指向該派生類。同時在派生類中重載了使用KVO的屬性的setter方法,在重載的setter方法中實現真正的通知機制,正如前面我們手動實現KVO一樣。這么做是基于設置屬性會調用 setter 方法,而通過重寫就獲得了 KVO 需要的通知機制。當然前提是要通過遵循 KVO 的屬性設置方式來變更屬性值,如果僅是直接修改屬性對應的成員變量,是無法實現 KVO 的。

同時派生類還重寫了 class 方法以“欺騙”外部調用者它就是起初的那個類。因此這個對象就成為該派生類的對象了,因而在該對象上對 setter 的調用就會調用重寫的 setter,從而激活鍵值通知機制。此外,派生類還重寫了 dealloc 方法來釋放資源。

拉模型

拉模型是指主題對象在通知觀察者的時候,只傳遞少量信息或者只是通知變化。如果觀察者需求要更具體的信息,由觀察者主動從主題對象中拉取數據。相比推模型來說,拉模型更加自由,觀察者只要知道有情況發生就好了,至于什么時候獲取、獲取那些內容、甚至是否獲取都可以自主決定。但是,卻存在兩個問題:

如果某個觀察者響應過慢,可能會漏掉之前通知的內容
觀察者必須保存一個對目標對象的引用,而且還需要了解主題對象的結構,這就使觀察者產生了對主題對象的依賴。
可能每種設計模式都會存在或多或少的一些弊端,但是他們的確能夠解決問題,也有更多有用的地方。在使用的時候,就需要我們權衡利弊,做出一個合適的選擇。而工程師的價值就體現在,能夠在紛繁復雜的工具世界中找到最有效的那個。而如果核桃沒被砸開,不是你手力氣不大的問題,而是你選錯了工具,誰讓你非得用門縫夾,不用錘子呢!

當然,上面那段屬于題外話。言歸正傳,在OBJC編程中,典型的一種拉模型的實現是delegate??赡芎芏嗳藭煌馕业挠^點,說delegate應當是委托模式。好吧,我不否認,delegate的確是委托模式的一種極度典型的實現方式。但是這并不妨礙,他也是一種觀察者模式。其實本來各種設計模式之間就不是涇渭分明的。在使用和解釋的時候,只要你能夠說得通,而且能夠解決問題就好了,沒必要糾纏他們的名字。而在通知變化這個事情上delegate的確是能夠解決問題的。

我們來看一個使用delegate實現拉模型的觀察者的例子:

先實現一個delegate方便注冊觀察者,和回調函數

@class DZClient;@protocol DZClientChangedDelegate <NSObject>- (void) client:(DZClient*)client didChangedContent:(NSString*)key;@end@interface DZClient : NSObject@property (nonatomic, weak) id<DZClientChangedDelegate> delegate;@property (nonatomic, strong) NSString* key;@end

注冊觀察者

//DZAppDelegateDZClient* client = [DZClient new];client.delegate = self;client.key = @"aa";

當主題對象的屬性發生改變的時候,發送內容有變化的通知

@implementation DZClient- (void) setKey:(NSString *)key{if (_key != key) {_key = key;if ([_delegate respondsToSelector:@selector(client:didChangedContent:)]) {[_delegate client:self didChangedContent:@"key"];}}}

觀察者收到主題對象有變化的通知后,主動去拉取變化的內容。

//DZAppDelegate- (void) client:(DZClient *)client didChangedContent:(NSString *)key{if ([key isEqual: @"key"]) {NSLog(@"get changed key %@",client.key);}}

廣義觀察者模式

在上面介紹了,觀察者被動的接受主題改變的經典意義上的觀察者模式之后,我們再來看一下廣義觀察者模式。當然上面所講的經典觀察者模式,也是一種一種傳遞數據的方式。廣義觀察者涵蓋了經典觀察者模式。

往往我們會有需要在“觀察者”和“主題對象”之間傳遞變化的數據。而這種情況下,主題對象可能不會像經典觀察者模式中的主題對象那樣勤勞,在發生改變的時候不停的廣播。在廣義觀察者模式中,主題對象可能是懶惰的,而是由觀察者通過不停的查詢主題對象的狀態,來獲知改變的內容。

我們熟悉的服務器CS架構,始終比較典型的冠以觀察者模式,服務器是伺服的,等待著客戶端的訪問,客戶端通過訪問服務器來獲取最新的內容,而不是服務器主動的推送。

之所以,要提出廣義觀察者模式這樣一個概念。是為了探討一下觀察者模式的本質。方便我們能夠更深刻的理解觀察者模式,并且合理的使用它。而且我們平時更多的將注意力放在了通知變化上面,而觀察者根本的目的是在于,在觀察者和主題對象之間,傳遞變化的數據。這些數據可能是變化這個事件本身,也可能是變化的內容,甚至可能是一些其他的內容。

從變化數據傳遞的角度來思考的話,能夠實現這個的模式和策略實在是數不勝數,比如傳統的網絡CS模型,比如KVC等等。在這里就先不詳細展開討論了。

以上所述是本文給大家介紹的ios觀察者設計模式,希望大家喜歡。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲男人天堂网站| 色久欧美在线视频观看| 国产精品入口尤物| 91精品国产综合久久香蕉| 欧美一级电影免费在线观看| 在线观看国产成人av片| 青青久久aⅴ北条麻妃| 青青青国产精品一区二区| 91精品国产综合久久久久久久久| 亚洲视频一区二区三区| 日韩欧美在线视频日韩欧美在线视频| 美女国内精品自产拍在线播放| 精品毛片三在线观看| 亚洲一区www| 欧美激情亚洲自拍| 91久久国产综合久久91精品网站| 最新国产成人av网站网址麻豆| 国产成人久久久精品一区| 久久人人97超碰精品888| 日韩av在线网| 51久久精品夜色国产麻豆| 欧美性高潮床叫视频| 精品呦交小u女在线| www.99久久热国产日韩欧美.com| 久久精品福利视频| 久久久精品网站| 亚洲深夜福利在线| 亚洲中国色老太| 一区二区三区在线播放欧美| 91免费欧美精品| 亚洲精品videossex少妇| 久久成人综合视频| 国产精品麻豆va在线播放| 国产精品久久久一区| 在线观看欧美日韩| 成人av在线亚洲| 高清欧美一区二区三区| 国内精久久久久久久久久人| 免费成人高清视频| 精品成人69xx.xyz| 成人网欧美在线视频| 色视频www在线播放国产成人| 亚洲四色影视在线观看| 色综合亚洲精品激情狠狠| 日韩中文字幕精品| 欧美洲成人男女午夜视频| 青草热久免费精品视频| 久久成人精品一区二区三区| 色琪琪综合男人的天堂aⅴ视频| 丝袜美腿精品国产二区| 国模精品系列视频| 日韩国产精品视频| 日韩精品久久久久| 欧美中文在线字幕| 亚洲天堂免费观看| 中文亚洲视频在线| 亚洲一区二区三| 91精品国产自产在线老师啪| 亚洲日本欧美日韩高观看| 黄色成人在线播放| 777午夜精品福利在线观看| 4444欧美成人kkkk| 一区二区三区四区精品| 97在线观看视频| 在线日韩精品视频| 国产美女精品视频免费观看| 日本在线观看天堂男亚洲| 欧美日韩免费一区| 欧美一区深夜视频| 尤物99国产成人精品视频| 中日韩美女免费视频网址在线观看| 国产精品毛片a∨一区二区三区|国| 色爱av美腿丝袜综合粉嫩av| 国产精品自拍网| 国产精品欧美日韩久久| 国产精品444| 日本精品视频网站| 久久久久久久国产| 在线视频国产日韩| 亚洲综合在线中文字幕| 亚洲的天堂在线中文字幕| 爽爽爽爽爽爽爽成人免费观看| 欧美精品videossex性护士| 亚洲毛片在线观看.| 国产精品大陆在线观看| 欧美高清在线视频观看不卡| 91最新在线免费观看| 亚洲综合在线小说| 国产精品看片资源| 国内伊人久久久久久网站视频| 精品久久久久久久久中文字幕| 日韩精品高清视频| 亚洲一区二区中文| 亚洲国产精品福利| 国产欧美日韩免费看aⅴ视频| 欧美精品一区三区| 国产精品99久久久久久久久久久久| 国产精品久久久久高潮| 日韩视频一区在线| 日本精品久久中文字幕佐佐木| 57pao国产精品一区| 色婷婷成人综合| 亚洲欧美激情四射在线日| 亚洲欧美一区二区三区在线| 国语自产精品视频在线看| 亚洲最大福利视频网| 成人网欧美在线视频| 米奇精品一区二区三区在线观看| 国产一区二区色| 日韩av在线免费观看| 97久久精品人搡人人玩| 韩国福利视频一区| 亚洲国产精品久久久久秋霞不卡| 日韩免费电影在线观看| 国产欧美日韩中文字幕| 亚洲美女自拍视频| 日韩成人激情影院| 国产成人精品午夜| 88国产精品欧美一区二区三区| 国产主播在线一区| 欧美做受高潮电影o| 日韩毛片在线观看| 亚洲国产中文字幕在线观看| 久久久久久午夜| 成人免费在线视频网站| 亚洲国产成人精品久久| 亚洲自拍在线观看| 亚洲欧美在线一区| 亚洲最大在线视频| 亚洲精品成人免费| 日韩大陆欧美高清视频区| 91在线观看免费高清| 色综合久久天天综线观看| 国产偷国产偷亚洲清高网站| 欧美日韩亚洲精品内裤| 日韩中文有码在线视频| 日韩精品在线看| 午夜精品www| 久久精品91久久久久久再现| 2021久久精品国产99国产精品| 欧美国产日韩一区二区| 国产成人精品免费久久久久| 精品欧美国产一区二区三区| 欧美日韩激情视频| 亚洲午夜色婷婷在线| 姬川优奈aav一区二区| 欧美日韩一二三四五区| 91超碰中文字幕久久精品| www亚洲精品| 国产精品久久久久久久电影| 91精品国产高清久久久久久91| 高跟丝袜一区二区三区| 最新69国产成人精品视频免费| 欧美国产日韩视频| 亚洲一品av免费观看| 国产中文字幕亚洲| 欧美一级片一区| 亚洲一区亚洲二区| 在线播放日韩专区| 亚洲成人久久一区| 77777少妇光屁股久久一区| 欧美性xxxx极品hd欧美风情| 亚洲乱码一区二区| 久久精品国产亚洲|