日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

告诉你KVC的一切-b

發布時間:2025/5/22 c/c++ 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 告诉你KVC的一切-b 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iOS的開發中,可以允許開發者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值。而不需要調用明確的存取方法。這樣就可以在運行時動態在訪問和修改對象的屬性。而不是在編譯時確定,這也是iOS開發中的黑魔法之一。很多高級的iOS開發技巧都是基于KVC實現的。目前網上關于KVC的文章在非常多,有的只是簡單地說了下用法,有的講得深入但是在使用場景和最佳實踐沒有說明,我寫下這遍文章就是給大家詳解一個最完整最詳細的KVC。

KVC在iOS中的定義

無論是Swift還是Objective-C,KVC的定義都是對NSObject的擴展來實現的(Objective-c中有個顯式的NSKeyValueCoding類別名,而Swift沒有,也不需要)所以對于所有繼承了NSObject在類型,都能使用KVC(一些純Swift類和結構體是不支持KVC的),下面是KVC最為重要的四個方法

1 2 3 4 - (nullable id)valueForKey:(NSString *)key;??????????????????????????//直接通過Key來取值 - (void)setValue:(nullable id)value forKey:(NSString *)key;??????????//通過Key來設值 - (nullable id)valueForKeyPath:(NSString *)keyPath;??????????????????//通過KeyPath來取值 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;??//通過KeyPath來設值

當然NSKeyValueCoding類別中還有其他的一些方法,下面列舉一些

1 2 3 4 5 6 7 8 9 10 11 12 13 14 + (BOOL)accessInstanceVariablesDirectly; //默認返回YES,表示如果沒有找到Set方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,設置成NO就不這樣搜索 - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError; //KVC提供屬性值確認的API,它可以用來檢查set的值是否正確、為不正確的值做一個替換值或者拒絕設置新值并返回錯誤原因。 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; //這是集合操作的API,里面還有一系列這樣的API,如果屬性是一個NSMutableArray,那么可以用這個方法來返回 - (nullable id)valueForUndefinedKey:(NSString *)key; //如果Key不存在,且沒有KVC無法搜索到任何和Key有關的字段或者屬性,則會調用這個方法,默認是拋出異常 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; //和上一個方法一樣,只不過是設值。 - (void)setNilValueForKey:(NSString *)key; //如果你在SetValue方法時面給Value傳nil,則會調用這個方法 - (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys; //輸入一組key,返回該組key對應的Value,再轉成字典返回,用于將Model轉到字典。

上面的這些方法在碰到特殊情況或者有特殊需求還是會用到的,所以也是可以了解一下。后面的代碼示例會有講到其中的一些方法。
同時蘋果對一些容器類比如NSArray或者NSSet等,KVC有著特殊的實現。建議有基礎的或者英文好的開發者直接去看蘋果的官方文檔,相信你會對KVC的理解更上一個臺階。

KVC是怎么尋找Key的

KVC是怎么使用的,我相信絕大多數的開發者都很清楚,我在這里就不再寫簡單的使用KVC來設值和取值的代碼了,首頁我們來探討KVC在內部是按什么樣的順序來尋找key的。
當調用setValue:屬性值 forKey:@”name“的代碼時,底層的執行機制如下:

  • 程序優先調用set:屬性值方法,代碼通過setter方法完成設置。注意,這里的是指成員變量名,首字母大清寫要符合KVC的全名規則,下同
  • 如果沒有找到setName:方法,KVC機制會檢查+ (BOOL)accessInstanceVariablesDirectly方法有沒有返回YES,默認該方法會返回YES,如果你重寫了該方法讓其返回NO的話,那么在這一步KVC會執行setValue:forUNdefinedKey:方法,不過一般開發者不會這么做。所以KVC機制會搜索該類里面有沒有名為_的成員變量,無論該變量是在類接口部分定義,還是在類實現部分定義,也無論用了什么樣的訪問修飾符,只在存在以_命名的變量,KVC都可以對該成員變量賦值。
  • 如果該類即沒有set:方法,也沒有_成員變量,KVC機制會搜索_is的成員變量,
  • 和上面一樣,如果該類即沒有set:方法,也沒有_和_is成員變量,KVC機制再會繼續搜索和is的成員變量。再給它們賦值。
  • 如果上面列出的方法或者成員變量都不存在,系統將會執行該對象的setValue:forUNdefinedKey:方法,默認是拋出異常。

如果開發者想讓這個類禁用KVC里,那么重寫+ (BOOL)accessInstanceVariablesDirectly方法讓其返回NO即可,這樣的話如果KVC沒有找到set:屬性名時,會直接用setValue:forUNdefinedKey:方法。

下面我們來讓代碼來測試一下上面的KVC機制

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @interface Dog : NSObject @end @implementation Dog { ???? NSString* toSetName; ????NSString* isName; ????//NSString* name; ????NSString* _name; ????NSString* _isName; } // -(void)setName:(NSString*)name{ //???? toSetName = name; // } //-(NSString*)getName{ //????return toSetName; //} +(BOOL)accessInstanceVariablesDirectly{ ????return NO; } -(id)valueForUndefinedKey:(NSString *)key{ ????NSLog(@"出現異常,該key不存在%@",key); ????return nil; } -(void)setValue:(id)value forUndefinedKey:(NSString *)key{ ???? NSLog(@"出現異常,該key不存在%@",key); } @end int main(int argc, const char * argv[]) { ????@autoreleasepool { ????????// insert code here... ????????Dog* dog = [Dog new]; ????????[dog setValue:@"newName" forKey:@"name"]; ????????NSString* name = [dog valueForKey:@"toSetName"]; ????????NSLog(@"%@",name); ????} ????return 0; }

首先我們先重寫accessInstanceVariablesDirectly方法讓其返回NO,再運行代碼(注意上面注釋的部分),XCode直接打印出

1 2 3 2016-04-15 15:52:12.039 DemoKVC[9681:287627] 出現異常,該key不存在name 2016-04-15 15:52:12.040 DemoKVC[9681:287627] 出現異常,該key不存在toSetName 2016-04-15 15:52:12.040 DemoKVC[9681:287627] (null)

這說明了重寫+(BOOL)accessInstanceVariablesDirectly方法讓其返回NO后,KVC找不到SetName:方法后,不再去找name系列成員變量,而是直接調用forUndefinedKey方法
所以開發者如果不想讓自己的類實現KVC,就可以這么做。
下面那兩個setter和gettr的注釋取消掉,再把

1 NSString* name = [dog valueForKey:@"toSetName"]; 換成 NSString* name = [dog valueForKey:@"name"];

XCode就可以正確地打印出正確的值了

1 2016-04-15 15:56:22.130 DemoKVC[9726:289258] newName

下面再注釋到accessInstanceVariablesDirectly方法,就能測試其他的key查找順序了,為了節省篇幅,剩下的的KVC對于key尋找機制就不在這里展示了,有興趣的讀者可以寫代碼去驗證。

當調用ValueforKey:@”name“的代碼時,KVC對key的搜索方式不同于setValue:屬性值 forKey:@”name“,其搜索方式如下

  • 首先按get,,is的順序方法查找getter方法,找到的話會直接調用。如果是BOOL或者int等值類型, 會做NSNumber轉換
  • 如果上面的getter沒有找到,KVC則會查找countOf,objectInAtIndex,AtIndex格式的方法。如果countOf和另外兩個方法中的要個被找到,那么就會返回一個可以響應NSArray所的方法的代理集合(它是NSKeyValueArray,是NSArray的子類),調用這個代理集合的方法,或者說給這個代理集合發送NSArray的方法,就會以countOf,objectInAtIndex,AtIndex這幾個方法組合的形式調用。還有一個可選的get:range:方法。所以你想重新定義KVC的一些功能,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標準命名方法,包括方法簽名。
  • 如果上面的方法沒有找到,那么會查找countOf,enumeratorOf,memberOf格式的方法。如果這三個方法都找到,那么就返回一個可以響應NSSet所的方法的代理集合,以送給這個代理集合消息方法,就會以countOf,enumeratorOf,memberOf組合的形式調用。
  • 如果還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認行為),那么和先前的設值一樣,會按_,_is,,is的順序搜索成員變量名,這里不推薦這么做,因為這樣直接訪問實例變量破壞了封裝性,使代碼更脆弱。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話,那么會直接調用valueForUndefinedKey:
  • 還沒有找到的話,調用valueForUndefinedKey:

下面再上代碼測試

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 @interface TwoTimesArray : NSObject -(void)incrementCount; -(NSUInteger)countOfNumbers; -(id)objectInNumbersAtIndex:(NSUInteger)index; @end @interface TwoTimesArray() @property (nonatomic,readwrite,assign) NSUInteger count; @property (nonatomic,copy) NSString* arrName; @end @implementation TwoTimesArray -(void)incrementCount{ ????self.count ++; } -(NSUInteger)countOfNumbers{ ????return self.count; -(id)objectInNumbersAtIndex:(NSUInteger)index{???? //當key使用numbers時,KVC會找到這兩個方法。 ????return @(index * 2); } -(NSInteger)getNum{???????????????? //第一個,自己一個一個注釋試 ????return 10; } -(NSInteger)num{?????????????????????? //第二個 ????return 11; } -(NSInteger)isNum{????????????????????//第三個 ????return 12; } @end int main(int argc, const char * argv[]) { ????@autoreleasepool { ????????TwoTimesArray* arr = [TwoTimesArray new]; ????????NSNumber* num =?? [arr valueForKey:@"num"]; ????????NSLog(@"%@",num); ????????id ar = [arr valueForKey:@"numbers"]; ????????NSLog(@"%@",NSStringFromClass([ar class])); ???????? NSLog(@"0:%@???? 1:%@???? 2:%@???? 3:%@",ar[0],ar[1],ar[2],ar[3]); ????????[arr incrementCount];????????????????????????????????????????????????????????????????????????????//count加1 ????????NSLog(@"%lu",(unsigned long)[ar count]);???????????????????????????????????????????????????????? //打印出1 ????????[arr incrementCount];????????????????????????????????????????????????????????????????????????????//count再加1 ????????NSLog(@"%lu",(unsigned long)[ar count]);???????????????????????????????????????????????????????? //打印出2 ????????[arr setValue:@"newName" forKey:@"arrName"]; ????????NSString* name = [arr valueForKey:@"arrName"]; ????????NSLog(@"%@",name); ????} ????return 0; } //打印結果 2016-04-17 15:39:42.214 KVCDemo[1088:74481] 10 2016-04-17 15:39:42.215 KVCDemo[1088:74481] NSKeyValueArray 2016-04-17 15:41:24.713 KVCDemo[1102:75424] 0:0???? 1:2???? 2:4???? 3:6???????????????? //太明顯了,直接調用-(id)objectInNumbersAtIndex:(NSUInteger)index;方法 2016-04-17 15:39:42.215 KVCDemo[1088:74481] 1 2016-04-17 15:39:42.215 KVCDemo[1088:74481] 2 2016-04-17 15:39:42.215 KVCDemo[1088:74481] newName

很明顯,上面的代碼充分說明了說明了KVC在調用ValueforKey:@”name“時搜索key的機制。不過還有些功能沒有全部列出,有興趣的讀者可以寫代碼去驗證。

在KVC中使用KeyPath

然而在開發過程中,一個類的成員變量有可能是其他的自定義類,你可以先用KVC獲取出來再該屬性,然后再次用KVC來獲取這個自定義類的屬性,但這樣是比較繁瑣的,對此,KVC提供了一個解決方案,那就是鍵路徑KeyPath。

1 2 - (nullable id)valueForKeyPath:(NSString *)keyPath;??????????????????//通過KeyPath來取值 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;??//通過KeyPath來設值

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @interface Address : NSObject @end @interface Address() @property (nonatomic,copy)NSString* country; @end @implementation Address @end @interface People : NSObject @end @interface People() @property (nonatomic,copy) NSString* name; @property (nonatomic,strong) Address* address; @property (nonatomic,assign) NSInteger age; @end @implementation People @end int main(int argc, const char * argv[]) { ????@autoreleasepool { ????????People* people1 = [People new]; ????????Address* add = [Address new]; ????????add.country = @"China"; ????????people1.address = add; ????????NSString* country1 = people1.address.country; ????????NSString * country2 = [people1 valueForKeyPath:@"address.country"]; ????????NSLog(@"country1:%@?? country2:%@",country1,country2); ????????[people1 setValue:@"USA" forKeyPath:@"address.country"]; ???????? country1 = people1.address.country; ????????country2 = [people1 valueForKeyPath:@"address.country"]; ????????NSLog(@"country1:%@?? country2:%@",country1,country2); ????} ????return 0; } //打印結果 2016-04-17 15:55:22.487 KVCDemo[1190:82636] country1:China?? country2:China 2016-04-17 15:55:22.489 KVCDemo[1190:82636] country1:USA?? country2:USA

上面的代碼簡單在展示了KeyPath是怎么用的。如果你不小心錯誤的使用了key而非KeyPath的話,KVC會直接查找address.country這個屬性,很明顯,這個屬性并不存在,所以會再調用UndefinedKey相關方法。而KVC對于KeyPath是搜索機制第一步就是分離key,用小數點.來分割key,然后再像普通key一樣按照先前介紹的順序搜索下去。

KVC如何處理異常

KVC中最常見的異常就是不小心使用了錯誤的Key,或者在設值中不小心傳遞了nil的值,KVC中有專門的方法來處理這些異常。
通常在用KVC操作Model時,拋出異常的那兩個方法是需要重寫的。雖然一般很小出現傳遞了錯誤的Key值這種情況,但是如果不小心出現了,直接拋出異常讓APP崩潰顯然是不合理的。
一般在這里直接讓這個Key打印出來即可,或者有些特殊情況需要特殊處理。
通常情況下,KVC不允許你要在調用setValue:屬性值 forKey:@”name“(或者keyPath)時對非對象傳遞一個nil的值。很簡單,因為值類型是不能為nil的。如果你不小心傳了,KVC會調用setNilValueForKey:方法。這個方法默認是拋出異常,所以一般而言最好還是重寫這個方法。

1 2 ??[people1 setValue:nil forKey:@"age"] ?? *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[ setNilValueForKey]: could not set nil as the value for the key age.' // 調用setNilValueForKey拋出異常

如果重寫setNilValueForKey:就沒問題了

1 2 3 4 5 6 7 8 9 @implementation People -(void)setNilValueForKey:(NSString *)key{ ????NSLog(@"不能將%@設成nil",key); } @end //打印出 2016-04-17 16:19:55.298 KVCDemo[1304:92472] 不能將age設成nil

?

KVC處理非對象和自定義對象

不是每一個方法都返回對象,但是valueForKey:總是返回一個id對象,如果原本的變量類型是值類型或者結構體,返回值會封裝成NSNumber或者NSValue對象。這兩個類會處理從數字,布爾值到指針和結構體任何類型。然后開以者需要手動轉換成原來的類型。盡管valueForKey:會自動將值類型封裝成對象,但是setValue:forKey:卻不行。你必須手動將值類型轉換成NSNumber或者NSValue類型,才能傳遞過去。

對于自定義對象,KVC也會正確以設值和取值。因為傳遞進去和取出來的都是id類型,所以需要開發者自己擔保類型的正確性,運行時Objective-C在發送消息的會檢查類型,如果錯誤會直接拋出異常。

1 2 3 4 5 6 7 8 Address* add2 = [Address new]; add2.country = @"England"; [people1 setValue:add2 forKey:@"address"]; NSString* country1 = people1.address.country; NSString * country2 = [people1 valueForKeyPath:@"address.country"]; NSLog(@"country1:%@?? country2:%@",country1,country2); //打印結果 2016-04-17 16:29:36.349 KVCDemo[1346:95910] country1:England?? country2:England

?

KVC與容器類

對象的屬性可以是一對一的,也可以是一對多的。一對多的屬性要么是有序的(數組),要么是無序的(數組)
不可變的有序容器屬性(NSArray)和無序容器屬性(NSSet)一般可以使用valueForKey:來獲取。比如有一個叫items的NSArray屬性,你可以用valurForKey:@"items"來獲取這個屬性。前面valueForKey:的key搜索模式中,我們發現其實KVC使用了一種更靈活的方式來管理容器類。蘋果的官方文檔也推薦我們實現這些這些特殊的訪問器。

而當對象的屬性是可變的容器時,對于有序的容器,可以用下面的方法:

1 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

該方法返回一個可變有序數組,如果調用該方法,KVC的搜索順序如下

  • 搜索insertObject:inAtIndex:?,?removeObjectFromAtIndex:?或者?insertAdIndexes?,?removeAtIndexes?格式的方法
    如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應NSMutableArray所有方法代理集合(類名是NSKeyValueFastMutableArray2),那么給這個代理集合發送NSMutableArray的方法,以insertObject:inAtIndex:?,?removeObjectFromAtIndex:?或者?insertAdIndexes?,?removeAtIndexes組合的形式調用。還有兩個可選實現的接口:replaceOnjectAtIndex:withObject: , replaceAtIndexes:with: 。
  • 如果上步的方法沒有找到,則搜索set:?格式的方法,如果找到,那么發送給代理集合的NSMutableArray最終都會調用set:方法。 也就是說,mutableArrayValueForKey:取出的代理集合修改后,用·set:· 重新賦值回去去。這樣做效率會低很多。所以推薦實現上面的方法。
  • 如果上一步的方法還還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認行為),會按_,,的順序搜索成員變量名,如果找到,那么發送的NSMutableArray消息方法直接交給這個成員變量處理。
  • 如果還是找不到,調用valueForUndefinedKey:
    關于mutableArrayValueForKey:的適用場景,我在網上找了很多,發現其一般是用在對NSMutableArray添加Observer上。
    如果對象屬性是個NSMutableAArray、NSMutableSet、NSMutableDictionary等集合類型時,你給它添加KVO時,你會發現當你添加或者移除元素時并不能接收到變化。因為KVO的本質是系統監測到某個屬性的內存地址或常量改變時,會添加上- (void)willChangeValueForKey:(NSString *)key
    和- (void)didChangeValueForKey:(NSString *)key方法來發送通知,所以一種解決方法是手動調用者兩個方法,但是并不推薦,你永遠無法像系統一樣真正知道這個元素什么時候被改變。另一種便是利用使用mutableArrayValueForKey:了。

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @interface demo : NSObject @property (nonatomic,strong) NSMutableArray* arr; @end @implementation demo -(id)init{ ????if (self == [super init]){ ????????_arr = [NSMutableArray new]; ????????[self addObserver:self forKeyPath:@"arr" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; ????} ????return self; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ ????NSLog(@"%@",change); } -(void)dealloc{ ????[self removeObserver:self forKeyPath:@"arr"]; //一定要在dealloc里面移除觀察 } -(void)addItem{ ????[_arr addObject:@"1"]; } -(void)addItemObserver{ ????[[self mutableArrayValueForKey:@"arr"] addObject:@"1"]; } -(void)removeItemObserver{ ????[[self mutableArrayValueForKey:@"arr"] removeLastObject]; } @end 然后再: demo* d = [demo new]; [d addItem]; [d addItemObserver]; [d removeItemObserver]; 打印結果 2016-04-18 17:48:22.675 KVCDemo[32647:505864] { ????indexes = "[number of indexes: 1 (in 1 ranges), indexes: (1)]"; ????kind = 2; ????new =???? ( ????????1 ????); } 2016-04-18 17:48:22.677 KVCDemo[32647:505864] { ????indexes = "[number of indexes: 1 (in 1 ranges), indexes: (1)]"; ????kind = 3; ????old =???? ( ????????1 ????); }

從上面的代碼可以看出,當只是普通地調用[_arr addObject:@"1"]時,Observer并不會回調,只有[[self mutableArrayValueForKey:@"arr"] addObject:@"1"];這樣寫時才能正確地觸發KVO。打印出來的數據中,可以看出這次操作的詳情,kind可能是指操作方法(我還不是很確認),old和new并不是成對出現的,當加添新數據時是new,刪除數據時是old

而對于無序的容器,可以用下面的方法:

1 - (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

該方法返回一個可變的無序數組如果調用該方法,KVC的搜索順序如下

  • 搜索addObjectObject:?,?removeObject:?或者?add?,?remove?格式的方法
    如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應NSMutableSet所有方法代理集合(類名是NSKeyValueFastMutableSet2),那么給這個代理集合發送NSMutableSet的方法,以addObjectObject:?,?removeObject:?或者?add?,?remove組合的形式調用。還有兩個可選實現的接口:intersect , set:?。
  • 如果reciever是ManagedObject,那么就不會繼續搜索。
  • 如果上步的方法沒有找到,則搜索set: 格式的方法,如果找到,那么發送給代理集合的NSMutableSet最終都會調用set:方法。 也就是說,mutableSetValueForKey取出的代理集合修改后,用set:?重新賦值回去去。這樣做效率會低很多。所以推薦實現上面的方法。
  • 如果上一步的方法還還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認行為),會按_,,的順序搜索成員變量名,如果找到,那么發送的NSMutableSet消息方法直接交給這個成員變量處理。
  • 如果還是找不到,調用valueForUndefinedKey:
    可見,除了檢查reciever是ManagedObject以外,其搜索順序和mutableArrayValueForKey基本一至,

同樣,它們也有對應的keyPath版本

1 2 - (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath; - (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

iOS5和OSX10.7以后還有個mutableOrdered版本

1 - (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key

這兩種KVC的用法我還不是清楚,目前只能找到用于KVO的例子。如果有讀者能在項目中用到,希望可以告訴我。

KVC和字典

當對NSDictionary對象使用KVC時,valueForKey:的表現行為和objectForKey:一樣。所以使用valueForKeyPath:用來訪問多層嵌套的字典是比較方便的。

KVC里面還有兩個關于NSDictionary的方法

1 2 - (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys; - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;

dictionaryWithValuesForKeys:是指輸入一組key,返回這組key對應的屬性,再組成一個字典。
setValuesForKeysWithDictionary是用來修改Model中對應key的屬性。下面直接用代碼會更直觀一點

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Address* add = [Address new]; add.country = @"China"; add.province = @"Guang Dong"; add.city = @"Shen Zhen"; add.district = @"Nan Shan"; NSArray* arr = @[@"country",@"province",@"city",@"district"]; NSDictionary* dict = [add dictionaryWithValuesForKeys:arr]; //把對應key所有的屬性全部取出來 NSLog(@"%@",dict); NSDictionary* modifyDict = @{@"country":@"USA",@"province":@"california",@"city":@"Los angle"}; [add setValuesForKeysWithDictionary:modifyDict];????????????//用key Value來修改Model的屬性 NSLog(@"country:%@??province:%@ city:%@",add.country,add.province,add.city); //打印結果 2016-04-19 11:54:30.846 KVCDemo[6607:198900] { ????city = "Shen Zhen"; ????country = China; ????district = "Nan Shan"; ????province = "Guang Dong"; } 2016-04-19 11:54:30.847 KVCDemo[6607:198900] country:USA??province:california city:Los angle

打印出來的結果完全符合預期。

KVC的內部實現機制

前面我們對析了KVC是怎么搜索key的。所以如果明白了key的搜索順序,是可以自己寫代碼實現KVC的。在考慮到集合和keyPath的情況下,KVC的實現會比較復雜,我們只寫代碼實現最普通的取值和設值即可。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @interface NSObject(MYKVC) -(void)setMyValue:(id)value forKey:(NSString*)key; -(id)myValueforKey:(NSString*)key; @end @implementation NSObject(MYKVC) -(void)setMyValue:(id)value forKey:(NSString *)key{ ????if (key == nil || key.length == 0) { ????????return; ????} ????if ([value isKindOfClass:[NSNull class]]) { ????????[self setNilValueForKey:key]; //如果需要完全自定義,那么這里需要寫一個setMyNilValueForKey,但是必要性不是很大,就省略了 ????????return; ????} ????if (![value isKindOfClass:[NSObject class]]) { ????????@throw @"must be s NSobject type"; ????????return; ????} ????NSString* funcName = [NSString stringWithFormat:@"set%@:",key.capitalizedString]; ????if ([self respondsToSelector:NSSelectorFromString(funcName)]) { ????????[self performSelector:NSSelectorFromString(funcName) withObject:value]; ????????return; ????} ????unsigned int count; ????BOOL flag = false; ????Ivar* vars = class_copyIvarList([self class], &count); ????for (NSInteger i = 0; i

上面就是自己寫代碼實現KVC的部分功能。其中我省略了自定義KVC錯誤方法,省略了部分KVC搜索key的步驟,但是邏輯是很清晰明了的,后面的測試也符合預期。當然這只是我自己實現KVC的思路,Apple也許并不是這么做的。

KVC的正確性驗證

KVC提供了屬性值,用來驗證key對應的Value是否可用的方法

1 - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

這個方法的默認實現是去探索類里面是否有一個這樣的方法:-(BOOL)validate:error:如果有這個方法,就調用這個方法來返回,沒有的話就直接返回YES

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @implementation Address -(BOOL)validateCountry:(id *)value error:(out NSError * _Nullable __autoreleasing *)outError{??//在implementation里面加這個方法,它會驗證是否設了非法的value ????NSString* country = *value; ????country = country.capitalizedString; ????if ([country isEqualToString:@"Japan"]) { ????????return NO;???????????????????????????????????????????????????????????????????????????? //如果國家是日本,就返回NO,這里省略了錯誤提示, ????} ????return YES; } @end NSError* error; id value = @"japan"; NSString* key = @"country"; BOOL result = [add validateValue:&value forKey:key error:&error]; //如果沒有重寫-(BOOL)-validate:error:,默認返回Yes if (result) { ????NSLog(@"鍵值匹配"); ????[add setValue:value forKey:key]; } else{ ????NSLog(@"鍵值不匹配"); //不能設為日本,基他國家都行 } NSString* country = [add valueForKey:@"country"]; NSLog(@"country:%@",country); //打印結果 2016-04-20 14:55:12.055 KVCDemo[867:58871] 鍵值不匹配 2016-04-20 14:55:12.056 KVCDemo[867:58871] country:China

如上面的代碼,當開發者需要驗證能不能用KVC設定某個值時,可以調用validateValue: forKey:這個方法來驗證,如果這個類的開發者實現了-(BOOL)validate:error:這個方法,那么KVC就會直接調用這個方法來返回,如果沒有,就直接返回YES,注意,KVC在設值時不會主動去做驗證,需要開發者手動去驗證。所以即使你在類里面寫了驗證方法,但是KVC因為不會去主動驗證,所以還是能夠設值成功。

KVC的使用

KVC在iOS開發中是絕不可少的利器,這種基于運行時的編程方式極大地提高了靈活性,簡化了代碼,甚至實現很多難以想像的功能,KVC也是許多iOS開發黑魔法的基礎。下面我來列舉iOS開發中KVC的使用場景

動態地取值和設值

利用KVC動態的取值和設值是最基本的用途了。相信每一個iOS開發者都能熟練掌握,

用KVC來訪問和修改私有變量

對于類里的私有屬性,Objective-C是無法直接訪問的,但是KVC是可以的,請參考本文前面的Dog類的例子。

Model和字典轉換

這是KVC強大作用的又一次體現,請參考我寫的iOS開發技巧系列—打造強大的BaseMod系列文章,里面
充分地運用了KVC和Objc的runtime組合的技巧,只用了短短數行代碼就是完成了很多功能。

修改一些控件的內部屬性

這也是iOS開發中必不可少的小技巧。眾所周知很多UI控件都由很多內部UI控件組合而成的,但是Apple度沒有提供這訪問這些空間的API,這樣我們就無法正常地訪問和修改這些控件的樣式。而KVC在大多數情況可下可以解決這個問題。最常用的就是個性化UITextField中的placeHolderText了。
下面演示如果修改placeHolder的文字樣式。這里的關鍵點是如果獲取你要修改的樣式的屬性名,也就是key或者keyPath名。

修改placeHolder的樣式

一般情況下可以運用runtime來獲取Apple不想開放的屬性名

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let count:UnsafeMutablePointer =??UnsafeMutablePointer() var properties = class_copyIvarList(UITextField.self, count) while properties.memory.debugDescription !=??"0x0000000000000000"{ ????let t = ivar_getName(properties.memory) ????let n = NSString(CString: t, encoding: NSUTF8StringEncoding) ????print(n)???????????????????????????????????????????????????????? //打印出所有屬性,這里我用了Swift語言 ????properties = properties.successor() } //上面省略了部分屬性 Optional(_disabledBackgroundView) Optional(_systemBackgroundView) Optional(_floatingContentView) Optional(_contentBackdropView) Optional(_fieldEditorBackgroundView) Optional(_fieldEditorEffectView) Optional(_displayLabel) Optional(_placeholderLabel)???????????????????????????????????????? //這個正是我想要修改的屬性。 Optional(_dictationLabel) Optional(_suffixLabel) Optional(_prefixLabel) Optional(_iconView) //下面省略了部分屬性

可以從里面看到其他還有很多東西可以修改,運用KVC設值可以獲得自己想要的效果。

操作集合

Apple對KVC的valueForKey:方法作了一些特殊的實現,比如說NSArray和NSSet這樣的容器類就實現了這些方法。所以可以用KVC很方便地操作集合

用KVC實現高階消息傳遞

當對容器類使用KVC時,valueForKey:將會被傳遞給容器中的每一個對象,而不是容器本身進行操作。結果會被添加進返回的容器中,這樣,開發者可以很方便的操作集合來返回另一個集合。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 NSArray* arrStr = @[@"english",@"franch",@"chinese"]; NSArray* arrCapStr = [arrStr valueForKey:@"capitalizedString"]; for (NSString* str??in arrCapStr) { ????NSLog(@"%@",str); } NSArray* arrCapStrLength = [arrStr valueForKeyPath:@"capitalizedString.length"]; for (NSNumber* length??in arrCapStrLength) { ????NSLog(@"%ld",(long)length.integerValue); } 打印結果 2016-04-20 16:29:14.239 KVCDemo[1356:118667] English 2016-04-20 16:29:14.240 KVCDemo[1356:118667] Franch 2016-04-20 16:29:14.240 KVCDemo[1356:118667] Chinese 2016-04-20 16:29:14.240 KVCDemo[1356:118667] 7 2016-04-20 16:29:14.241 KVCDemo[1356:118667] 6 2016-04-20 16:29:14.241 KVCDemo[1356:118667] 7

方法capitalizedString被傳遞到NSArray中的每一項,這樣,NSArray的每一員都會執行capitalizedString并返回一個包含結果的新的NSArray。從打印結果可以看出,所有String都成功以轉成了大寫。
同樣如果要執行多個方法也可以用valueForKeyPath:方法。它先會對每一個成員調用?capitalizedString方法,然后再調用length,因為lenth方法返回是一個數字,所以返回結果以NSNumber的形式保存在新數組里。

用KVC中的函數操作集合

KVC同時還提供了很復雜的函數,主要有下面這些
①簡單集合運算符
簡單集合運算符共有@avg, @count ,?@max?,?@min?,@sum5種,都表示啥不用我說了吧, 目前還不支持自定義。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @interface Book : NSObject @property (nonatomic,copy)??NSString* name; @property (nonatomic,assign)??CGFloat price; @end @implementation Book @end Book *book1 = [Book new]; book1.name = @"The Great Gastby"; book1.price = 22; Book *book2 = [Book new]; book2.name = @"Time History"; book2.price = 12; Book *book3 = [Book new]; book3.name = @"Wrong Hole"; book3.price = 111; Book *book4 = [Book new]; book4.name = @"Wrong Hole"; book4.price = 111; NSArray* arrBooks = @[book1,book2,book3,book4]; NSNumber* sum = [arrBooks valueForKeyPath:@"@sum.price"]; NSLog(@"sum:%f",sum.floatValue); NSNumber* avg = [arrBooks valueForKeyPath:@"@avg.price"]; NSLog(@"avg:%f",avg.floatValue); NSNumber* count = [arrBooks valueForKeyPath:@"@count"]; NSLog(@"count:%f",count.floatValue); NSNumber* min = [arrBooks valueForKeyPath:@"@min.price"]; NSLog(@"min:%f",min.floatValue); NSNumber* max = [arrBooks valueForKeyPath:@"@max.price"]; NSLog(@"max:%f",max.floatValue); 打印結果 2016-04-20 16:45:54.696 KVCDemo[1484:127089] sum:256.000000 2016-04-20 16:45:54.697 KVCDemo[1484:127089] avg:64.000000 2016-04-20 16:45:54.697 KVCDemo[1484:127089] count:4.000000 2016-04-20 16:45:54.697 KVCDemo[1484:127089] min:12.000000 2016-04-20 16:45:54.697 KVCDemo[1484:127089] max:111.000000

②對象運算符
比集合運算符稍微復雜,能以數組的方式返回指定的內容,一共有兩種:
@distinctUnionOfObjects
@unionOfObjects
它們的返回值都是NSArray,區別是前者返回的元素都是唯一的,是去重以后的結果;后者返回的元素是全集。
用法如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 NSLog(@"distinctUnionOfObjects"); NSArray* arrDistinct = [arrBooks valueForKeyPath:@"@distinctUnionOfObjects.price"]; for (NSNumber *price in arrDistinct) { ????NSLog(@"%f",price.floatValue); } NSLog(@"unionOfObjects"); NSArray* arrUnion = [arrBooks valueForKeyPath:@"@unionOfObjects.price"]; for (NSNumber *price in arrUnion) { ????NSLog(@"%f",price.floatValue); } 2016-04-20 16:47:34.490 KVCDemo[1522:128840] distinctUnionOfObjects 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 111.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 12.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 22.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] unionOfObjects 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 22.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 12.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 111.000000 2016-04-20 16:47:34.490 KVCDemo[1522:128840] 111.000000

前者會將重復的價格去除后返回所有價格,后者直接返回所有的圖書價格。(因為只返回價格,沒有返回圖書,感覺用處不大。)
③Array和Set操作符
這種情況更復雜了,說的是集合中包含集合的情況,我們執行了如下的一段代碼:
@distinctUnionOfArrays
@unionOfArrays
@distinctUnionOfSets
@distinctUnionOfArrays:該操作會返回一個數組,這個數組包含不同的對象,不同的對象是在從關鍵路徑到操作器右邊的被指定的屬性里
@unionOfArrays?該操作會返回一個數組,這個數組包含的對象是在從關鍵路徑到操作器右邊的被指定的屬性里和@distinctUnionOfArrays不一樣,重復的對象不會被移除
@distinctUnionOfSets?和@distinctUnionOfArrays類似。因為Set本身就不支持重復。

KVO

你沒看錯,KVO是基于KVC實現的。那么是怎么用KVC實現KVO的呢,請期待下章。

總結

本文全方位介紹了KVC的原理和各種用法。相信讀者看完后對會KVC會有更完全的理解,也會在項目里更好的運用KVC。其實這里面所有的東西在官方文檔里都有詳細的講解說明。只不過全是英文的,我也看過幾遍,但是英語不好會看得很吃力,比如官方在介紹@distinctUnionOfArrays時的那句話我想了很好久也不是很明白,而且官方的示例代碼也做得不夠好,所以很難找出某些功能的適用場景。但我還是推薦各位開發者能夠學好英語去看官方文檔。再結合StackOverFlow和Google。真的可以解決絕大多數開發中碰到的難題了。這篇文章就到這里,下篇我向大家介紹KVO。

?

感謝大神分享

總結

以上是生活随笔為你收集整理的告诉你KVC的一切-b的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

欧美日韩超碰 | 色综合久久天天 | 人人爽人人插 | www视频在线播放 | 国产男女免费完整视频 | 亚洲第一区精品 | 在线看黄色的网站 | 精品99999 | av中文字幕在线播放 | 99在线热播精品免费 | 日韩资源在线播放 | 欧美精品在线观看免费 | 美女视频一区 | 亚洲五月婷婷 | 91大神精品视频 | 亚洲作爱视频 | 国产精品不卡在线播放 | 成人在线小视频 | 久久精品99精品国产香蕉 | 精品成人在线 | 久久一区二区三区日韩 | 日韩av中文 | 成人精品福利 | 66av99精品福利视频在线 | 最近日本韩国中文字幕 | 丝袜美腿亚洲 | 国际精品久久久久 | 亚洲精品88欧美一区二区 | 高清视频一区二区三区 | 久久草在线视频国产 | 中文字幕在线观看网址 | 久久一区精品 | 国产成人精品久 | 国产欧美综合视频 | 婷色| 高清中文字幕av | 日韩婷婷 | 久青草视频在线观看 | 天天综合网天天 | 色黄久久久久久 | 黄色大片日本 | 天天干国产 | 日日干 天天干 | 91av官网 | 日韩黄色免费在线观看 | 精品久久久久_ | 亚洲精品66 | 国产精品午夜久久久久久99热 | 干干干操操操 | 亚洲免费成人 | 久久综合五月 | 午夜久久久久久久 | 开心色婷婷| 日韩免费视频在线观看 | 国内少妇自拍视频一区 | 最新中文在线视频 | 国产高清视频在线观看 | 人人干人人草 | 免费性网站 | 六月婷操 | 日韩精选在线 | 久久99久久99免费视频 | 婷婷av色综合 | 亚洲aaa毛片 | 亚洲成av人片在线观看香蕉 | 一级黄色免费网站 | 国产精品精品久久久 | av不卡中文字幕 | 麻豆视频在线免费看 | 五月婷婷综合色拍 | 成年人免费在线 | 久久亚洲人 | 人人插人人射 | 国产精品久久婷婷六月丁香 | 激情狠狠干 | 中文在线√天堂 | 国产96在线视频 | 不卡av在线 | 国产福利不卡视频 | 久久九九久久九九 | 色综合中文字幕 | 四季av综合网站 | 国产一区在线不卡 | 日韩免费在线 | 91精品一区二区三区久久久久久 | 中文在线 | 国产尤物一区二区三区 | www.com久久| 婷婷色综合| 九九在线视频免费观看 | japanese黑人亚洲人4k | 欧美成人精品欧美一级乱黄 | 免费成人在线电影 | 黄色成人在线观看 | 久久99国产综合精品免费 | 三级av黄色| 中文字幕日韩一区二区三区不卡 | 亚洲精品久久在线 | 成人av一区二区兰花在线播放 | 午夜的福利 | av在线中文 | 国产成人精品一区二区三区免费 | 国产在线91精品 | 日韩精品视频免费看 | 国产成人61精品免费看片 | 久久精选视频 | 97电影手机 | 中文字幕亚洲不卡 | 久久国产精品99国产精 | 黄色亚洲片 | 中文字幕精品www乱入免费视频 | av一级片 | 色狠狠一区二区 | 一区二区三区四区在线 | 色综合天天色 | 99在线视频免费观看 | 97超碰国产精品 | 高清免费av在线 | 日韩欧美国产成人 | 亚洲黄色免费 | 欧美最猛性xxxx | 手机看国产毛片 | 久草在线观看视频免费 | 精品久久一区二区 | 久草视频免费在线观看 | 久久视频免费在线 | 久久久18| 狠狠的操狠狠的干 | 国产一级特黄毛片在线毛片 | 黄色软件网站在线观看 | 美女网站视频免费黄 | 色网站在线免费观看 | av电影中文字幕在线观看 | 一区二区三区中文字幕在线 | 狠狠躁夜夜av | av在线免费在线观看 | 亚洲精品在线观看的 | 最近日本字幕mv免费观看在线 | 狠狠色丁香婷婷综合久小说久 | 久久久国产精品电影 | avav片| 天天综合视频在线观看 | 日韩在线免费小视频 | 国产原厂视频在线观看 | 欧美一级片在线观看视频 | 天天干天天干天天射 | 久久综合九色综合久久久精品综合 | 国产精品一区二区三区四区在线观看 | 久久蜜桃av| 亚洲三级影院 | 黄色亚洲大片免费在线观看 | 精品久久久久久综合日本 | 丰满少妇久久久 | 天天艹天天 | 深爱婷婷| 91精品国产92久久久久 | 97成人免费 | 国产精品久久久久9999吃药 | 日日夜夜精品免费视频 | 欧美小视频在线观看 | 久久久www | 波多野结衣视频一区二区 | 亚洲成人免费在线 | 亚洲精品综合久久 | 色干综合 | 波多野结衣在线中文字幕 | 午夜性生活 | 狠狠色丁香婷婷综合基地 | 久久视频中文字幕 | 日韩高清一 | 久久精品视频2 | 91亚洲在线观看 | 在线视频一区二区 | 97视频总站 | 亚洲一区动漫 | 亚洲免费黄色 | 久久精品国产亚洲精品 | 伊人国产在线观看 | 成人黄色短片 | 91在线免费观看网站 | 最新国产中文字幕 | 精品国产乱子伦一区二区 | 狠狠色丁香九九婷婷综合五月 | 久草精品视频 | 天天射天天操天天色 | 一区二区伦理 | 久久久99国产精品免费 | 日韩激情网 | 狠狠干免费 | 精品字幕在线 | 国产精品黑丝在线观看 | 欧美日韩国内在线 | 亚洲国产精品视频在线观看 | 欧美少妇xx | 99精品国产99久久久久久97 | 久久九九影视 | 久久久久久中文字幕 | 高清不卡一区二区在线 | 97福利社 | 久99久中文字幕在线 | 亚洲精品日韩一区二区电影 | 午夜精品久久久久99热app | 超碰97中文 | 欧美在线91 | 在线观看电影av | 日韩精品一区二区三区不卡 | 99视频这里只有 | av片子在线观看 | 成人18视频 | 四虎永久免费在线观看 | 久久精品一 | 狠狠色噜噜狠狠狠狠2021天天 | 成人毛片在线观看 | 精品视频123区在线观看 | 伊人网综合在线观看 | 国产流白浆高潮在线观看 | 在线亚洲欧美日韩 | 日本黄区免费视频观看 | 香蕉影视app | 国产精品美女免费 | 人人澡人人模 | 欧美日本日韩aⅴ在线视频 插插插色综合 | av韩国在线 | 欧美色图88| 中文字幕免费成人 | 人人舔人人射 | 99热网站| 波多野结衣在线视频一区 | 97国产一区二区 | 亚洲一级黄色av | 视频一区二区国产 | 人人草网站 | 亚洲国产成人在线播放 | 久久影视一区 | 亚洲伊人第一页 | 国产美女精彩久久 | 少妇视频在线播放 | 在线草 | 久久歪歪 | 韩国av在线| 欧美国产日韩激情 | 国产精品乱码久久久久 | 黄色av三级在线 | 黄色小说网站在线 | 蜜桃av久久久亚洲精品 | 免费91麻豆精品国产自产在线观看 | 婷婷激情欧美 | 深夜免费网站 | 免费久久精品视频 | 精品久久久免费 | 亚洲综合视频在线播放 | 超碰最新网址 | 日韩在线观看免费 | 日韩欧美高清一区二区 | 国产大片免费久久 | aav在线| 丰满少妇在线观看 | 久久视频一区 | 亚洲天堂视频在线 | 中文字幕婷婷 | www五月天婷婷 | 在线免费黄色毛片 | 国产成人免费精品 | 一本一道久久a久久精品蜜桃 | 天天草综合| 欧洲不卡av| 久久这里只有精品1 | 人人干天天射 | 婷婷色在线播放 | 久久精品一 | 午夜国产福利在线 | 国产精品婷婷午夜在线观看 | 欧美亚洲国产日韩 | 91精品专区 | 久久一区二区三区超碰国产精品 | 欧美另类v | 在线电影日韩 | 午夜久久久久久久久久久 | 亚洲视频1| a黄在线观看 | 天堂va欧美va亚洲va老司机 | 精品成人a区在线观看 | 免费日韩av电影 | 日韩欧美电影在线 | 一区国产精品 | 91九色网站 | 人人澡人人添人人爽一区二区 | 91在线视频观看 | 麻豆视频国产精品 | 日韩成人免费观看 | 欧美日韩a视频 | 香蕉视频在线免费 | 午夜av免费 | 国产精品久久久久一区二区三区 | 中文字幕欧美日韩va免费视频 | 中日韩在线 | 99久久久久国产精品免费 | 久久久99国产精品免费 | 久久九九九九 | 国产精品久久久久三级 | 成人av播放 | 91在线免费观看网站 | 98久9在线 | 免费 | 91完整版| 在线有码中文 | 国产精品久久久久久久久搜平片 | 国内精品久久久久久久久久清纯 | 色亚洲激情 | 日日夜夜天天人人 | 日本精品va在线观看 | 亚洲精品mv在线观看 | 在线国产一区二区三区 | 免费成人在线观看视频 | 99精品视频播放 | 九九免费在线视频 | 亚洲视频国产 | 91大神在线观看视频 | 97在线观看免费观看高清 | 婷婷在线精品视频 | 六月激情丁香 | 久99久在线 | 91在线看黄| 九九有精品 | 欧美日韩一区二区三区视频 | 国产成人久久 | 久久成人高清视频 | 天天看天天干 | 国产小视频在线 | 欧美大香线蕉线伊人久久 | 91精品在线视频 | 久久综合中文色婷婷 | 免费a级黄色毛片 | 国产精品久久在线观看 | 日韩午夜av| 成人黄色片在线播放 | 成人久久18免费网站 | 中文字幕在线观看免费高清完整版 | 麻豆视屏 | 国产一区二区三区高清播放 | 91精品亚洲影视在线观看 | 国产精品孕妇 | 91精品国产高清自在线观看 | 久草视频免费播放 | 欧美日韩精品影院 | 少妇视频在线播放 | 最新av网址在线观看 | 久久激情小视频 | 久久精品中文视频 | 天天操福利视频 | 丁香五月缴情综合网 | 激情视频久久 | 国产一级免费观看视频 | 2020天天干夜夜爽 | 国产精品一级在线 | 免费成人在线观看视频 | 天天干天天干天天干 | 在线午夜 | 午夜视频亚洲 | 女人魂免费观看 | 在线观看免费高清视频大全追剧 | 香蕉视频网站在线观看 | 在线 精品 国产 | 日韩视频a | 亚洲国产精品一区二区久久hs | 狠狠色丁香久久婷婷综 | 国产精品久久久久久婷婷天堂 | 亚洲经典视频 | 国产午夜免费视频 | 麻豆91精品91久久久 | a色视频| 国产精品毛片一区二区在线看 | 国产v欧美 | 亚洲免费在线视频 | 国产一区二区精品久久91 | 91少妇精拍在线播放 | www.久久久精品 | 精品国产免费一区二区三区五区 | 国产精品免费不卡 | 精品福利网 | 国产精品免费在线播放 | 国产成人精品一区二区三区在线 | 欧美一区二区精品在线 | 国产精品女人网站 | 日本性久久| 伊人网av | 99久久综合精品五月天 | 免费av一级电影 | 二区中文字幕 | 黄污视频网站大全 | 狠狠的操| www.成人sex| 日韩精品中文字幕av | 69中文字幕 | 国产日产高清dvd碟片 | 国产一级精品绿帽视频 | 亚洲精品在线视频 | 欧美日韩在线免费观看 | 婷婷色在线资源 | a黄色片| 国产成人精品一区二区三区在线观看 | 夜色资源站wwwcom | 日韩av网址在线 | av看片在线观看 | 免费看片成年人 | 国产黄在线| 亚洲视频 在线观看 | 91伊人久久大香线蕉蜜芽人口 | 免费在线观看国产精品 | 亚洲综合在线五月天 | a√天堂资源 | 成人av在线网 | 婷婷九九| 久久伊人91| 五月婷婷亚洲 | 久久精品91视频 | 欧美日韩中文字幕综合视频 | 一级a毛片高清视频 | 国产精品v a免费视频 | 国产一级在线播放 | 福利一区二区在线 | 色在线高清 | 久久综合福利 | 六月丁香伊人 | 久久99视频精品 | 美女黄久久 | 国产黑丝袜在线 | av三区在线 | 亚洲一区网 | 日韩欧美视频免费观看 | 五月天丁香综合 | 456成人精品影院 | 国产在线1区 | 99在线观看视频 | 国产精品 国内视频 | 欧美二区视频 | 国内精品中文字幕 | 国内久久精品 | 99久久久成人国产精品 | 深爱综合网| 黄色小说在线免费观看 | av日韩国产 | 亚洲国产欧美在线人成大黄瓜 | 欧美孕妇与黑人孕交 | 日日插日日干 | 欧美看片| 成人一级免费电影 | 91人人澡人人爽人人精品 | 免费a网| 91av视频在线播放 | 日韩av影视 | 久久久久久片 | 欧美一区二区精品在线 | 99久久精品网 | 国内精品视频在线 | 99热99| 亚洲精品国偷自产在线91正片 | 国产99久久九九精品免费 | 91九色在线播放 | 久久久久日本精品一区二区三区 | 中文在线最新版天堂 | 美女视频黄免费的 | 久艹视频在线免费观看 | 午夜国产福利在线 | 黄色毛片在线 | 粉嫩av一区二区三区免费 | 日韩在线高清免费视频 | 亚洲开心激情 | 国产剧情一区 | 爱射综合 | www.av在线播放 | 五月开心六月婷婷 | 国产一级久久久 | 中文字幕一区二区三区在线视频 | 九九九在线观看视频 | 久久久久久国产一区二区三区 | 午夜 久久 tv | 中文在线字幕免费观 | 欧美精品中文字幕亚洲专区 | 日韩欧美视频在线免费观看 | 波多野结衣精品在线 | 亚洲伊人av | 久久99精品久久久久婷婷 | 在线观看免费高清视频大全追剧 | 国产精品久久久久一区二区国产 | 日本激情视频中文字幕 | 天天操天天操天天操 | 亚洲综合狠狠干 | 国产精品99久久免费黑人 | 久久久久久国产精品999 | 97色婷婷成人综合在线观看 | 欧美性超爽| 久久午夜视频 | 久久不射网站 | 亚洲视屏一区 | 免费看片日韩 | 四虎www com| 白丝av免费观看 | 在线免费观看黄色小说 | 激情综合网五月婷婷 | a视频免费看 | 在线免费观看黄色小说 | 久草在线一免费新视频 | 国内精品久久久 | 国产精品久久久久久久妇 | 欧美大荫蒂xxx| 日韩r级电影在线观看 | 国产一区二区高清 | 丁香久久五月 | 国产免费视频在线 | 99热都是精品 | 91视频-88av | 日韩在线国产精品 | 中文字幕超清在线免费 | 日韩精品免费在线播放 | 久草免费在线视频观看 | 国产人成一区二区三区影院 | 国产精品美女久久久久aⅴ 干干夜夜 | av免费看在线 | 国产一区欧美在线 | 国精产品永久999 | 91日韩免费| 夜夜摸夜夜爽 | 天天操人人干 | 国产专区视频在线 | 人人爽人人做 | 国产免费三级在线观看 | 国产精品永久免费在线 | 97精品免费视频 | 国产精品丝袜 | 日日夜夜网 | av免费观看高清 | 国产一级黄大片 | 精品91在线 | 激情五月婷婷激情 | 精品国产91亚洲一区二区三区www | 99国产精品 | 国产精品二区在线观看 | 亚洲男女精品 | 美女一级毛片视频 | 午夜精品一区二区三区在线观看 | 91精品视频免费看 | 日韩av视屏在线观看 | 九色视频网 | 丁香国产视频 | 国产日韩欧美网站 | 免费在线黄网 | 最近中文字幕在线中文高清版 | 热久久视久久精品18亚洲精品 | 亚洲一本视频 | 国产一区二区高清不卡 | 国产精品女同一区二区三区久久夜 | .精品久久久麻豆国产精品 亚洲va欧美 | 久久精品屋 | 天天综合久久综合 | 久操视频在线免费看 | 久久久91精品国产 | 亚洲精品天天 | 中文字幕在线精品 | 久久综合丁香 | 91午夜精品| 日韩色在线 | 国产福利一区二区三区视频 | 亚洲高清免费在线 | 在线免费观看黄色 | 亚洲电影在线看 | 日日夜夜操操操操 | 日本久久久久久久久 | 久久五月天综合 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 亚洲做受高潮欧美裸体 | 在线观看视频97 | 麻豆久久久 | 亚洲成av人片在线观看香蕉 | 91尤物国产尤物福利在线播放 | 日韩在线观看视频中文字幕 | 中文字幕高清在线 | 日韩在线免费电影 | 成人在线免费观看网站 | 九色视频自拍 | 中文字幕精品一区二区精品 | 久久久久久久国产精品影院 | 在线视频欧美日韩 | 黄色毛片观看 | 香蕉视频在线免费 | 精品亚洲午夜久久久久91 | 九九九九精品九九九九 | 久久a视频 | 97国产情侣爱久久免费观看 | 在线视频日韩欧美 | 成人精品一区二区三区中文字幕 | 狠狠狠色丁香婷婷综合久久88 | 欧美日韩午夜爽爽 | 六月丁香综合 | 午夜免费电影院 | 亚洲免费视频观看 | 国产v在线 | 麻豆国产精品va在线观看不卡 | 国产视频日韩视频欧美视频 | 中文字幕在 | 国产女教师精品久久av | 天堂v中文 | 9999亚洲 | 成人在线播放免费观看 | 国产人成一区二区三区影院 | 国产精品久久久av久久久 | 精品女同一区二区三区在线观看 | 久久99久久99精品 | 久久久久久久久久久久久国产精品 | 日韩一区二区三区观看 | 丁香网五月天 | 成年人视频免费在线播放 | 在线免费观看麻豆视频 | a在线观看视频 | 国产精品久久久久久久久岛 | 69久久久久久久 | 国产流白浆高潮在线观看 | 九九九国产 | 久久夜视频| 888av| 91豆花在线观看 | 九九九九色 | 亚洲精品18日本一区app | 精品久久久久久久 | 亚洲高清视频在线观看 | 国产裸体无遮挡 | 中文字幕亚洲欧美日韩 | 久久成人人人人精品欧 | 国产视频精品久久 | 国产理论免费 | 日韩一区视频在线 | 国产精品永久久久久久久久久 | 国产丝袜一区二区三区 | 久久人人精品 | 欧美精品久久久久久久亚洲调教 | 久久综合九色综合欧美就去吻 | 狠狠干在线 | 亚洲欧美激情精品一区二区 | 亚洲精品玖玖玖av在线看 | 91av亚洲 | 国产亚洲精品免费 | 激情婷婷综合 | 在线观看av大片 | 精品视频资源站 | 国产精品综合久久久久 | 成人免费观看网站 | 丰满少妇对白在线偷拍 | 激情狠狠干 | 久久久精选 | 91夜夜夜 | 日韩三级成人 | 91九色自拍 | 日韩av福利在线 | 91精品久久久久久综合五月天 | 伊人狠狠 | 中文字幕av专区 | 天天操夜夜摸 | 日本最大色倩网站www | 国产黄色精品网站 | 干综合网| 亚州精品天堂中文字幕 | 91在线看视频 | 911亚洲精品第一 | 四虎在线视频 | 欧美三级高清 | 手机色在线 | 色在线视频 | 日韩欧美高清在线 | 国产精品免费麻豆入口 | 天天射日 | 欧美另类tv | 久久狠狠亚洲综合 | 亚洲精品男人天堂 | 国产精品女人网站 | 久久久久久蜜av免费网站 | 久久高清毛片 | 久久久久免费精品视频 | 欧美一级在线 | 免费视频久久久久 | 精品国产一区二区三区久久久 | 国内精品久久天天躁人人爽 | 国产福利午夜 | 精品国精品自拍自在线 | 精品网站999www| 综合中文字幕 | 粉嫩av一区二区三区免费 | 久久久久综合网 | 日av免费 | 精品久久久久久一区二区里番 | 国产麻豆精品传媒av国产下载 | 黄色成人av | 香蕉91视频 | 999久久国产 | 亚洲另类视频在线 | 看av免费网站| 国产精品入口传媒 | 97操操操 | 国产成人一区二区三区免费看 | 在线免费亚洲 | 成人av一区二区在线观看 | 黄色免费在线看 | 最近2019中文免费高清视频观看www99 | 夜夜夜夜猛噜噜噜噜噜初音未来 | www..com黄色片| 免费色视频网址 | 国产精品久久久 | 国产一在线精品一区在线观看 | 亚洲精品在线视频观看 | 9999亚洲| www.99av| 超碰人在线 | 欧美久久久久久久久中文字幕 | 日韩欧美在线观看一区二区 | 日韩免费不卡av | 成人黄色在线看 | 亚洲男女精品 | 亚洲精品女 | 成人宗合网 | 国产91小视频 | av天天澡天天爽天天av | 国产五月婷| 国产成人精品日本亚洲999 | 久久艹在线| 五月婷婷六月丁香激情 | 99久久精品国产欧美主题曲 | 日韩欧美在线综合网 | 日韩欧美高清在线观看 | 午夜视频在线观看一区二区三区 | 亚洲天堂精品视频在线观看 | 狠狠网站| 黄色三级免费片 | 日韩欧美一级二级 | 免费三级黄 | 久久不射电影院 | 日韩啪啪小视频 | 国产色婷婷精品综合在线手机播放 | 婷婷成人综合 | 成人av免费电影 | 玖草影院 | 久久久黄色免费网站 | 成人免费一区二区三区在线观看 | 成人国产电影在线观看 | 欧美日本啪啪无遮挡网站 | 成人av中文字幕 | 二区三区av | 午夜精品久久久久久99热明星 | 国产精品一二 | 日韩色在线 | 超碰在线色 | 久久久久综合视频 | 午夜色性片 | 天天操天天操天天操 | 国产一区二区三区视频在线 | 国产一区久久 | 综合久久五月天 | 一区二区电影在线观看 | 亚洲资源在线 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 伊人成人激情 | 国产高清av免费在线观看 | 天天插狠狠干 | 99热最新在线 | 国产精品成人aaaaa网站 | 国产高清视频在线播放 | 99热这里只有精品国产首页 | 91精品视频在线免费观看 | www.五月婷婷 | 国产精品涩涩屋www在线观看 | 日韩在线电影一区 | 中文字幕二区在线观看 | 成人小视频在线观看免费 | 国产成人精品免费在线观看 | 免费色av | 蜜臀一区二区三区精品免费视频 | 字幕网在线观看 | 国产精品videoxxxx| 九九99 | 中文视频一区二区 | 九九免费精品视频 | 五月网婷婷 | 精品久久久久久亚洲综合网站 | 99久久久久久 | 天天干,天天射,天天操,天天摸 | 最近最新中文字幕 | 91中文在线视频 | 国产破处在线视频 | 中文字幕一区在线观看视频 | 亚洲专区在线播放 | 在线观看国产中文字幕 | 婷色在线 | 九草视频在线观看 | 国产日韩av在线 | 成人h动漫精品一区二 | 2022久久国产露脸精品国产 | 亚洲mv大片欧洲mv大片免费 | 少妇搡bbbb搡bbb搡忠贞 | 亚洲一级理论片 | 日韩综合视频在线观看 | 亚洲精品美女久久久 | 中文字幕国语官网在线视频 | 三级av免费 | 日韩素人在线观看 | 国产精品中文字幕在线观看 | 国产精品第二页 | 国产日韩欧美自拍 | 国内精品视频在线播放 | 91视频3p| 国产尤物一区二区三区 | 久久夜视频 | 日韩在线第一区 | 日韩欧美在线综合网 | 久久九九免费视频 | 波多野结衣在线观看视频 | 午夜视频黄 | 天天看天天干天天操 | 在线超碰av | 精品久久久免费视频 | 中文字幕在线观看1 | 国产成人黄色在线 | 99视频一区二区 | av片在线观看 | 一区二区视频免费在线观看 | 丁香五月网久久综合 | 日韩欧美在线一区 | 成人国产亚洲 | 日日夜夜精品网站 | 成年人在线电影 | 日韩欧美一区二区三区在线观看 | 国产精品爽爽久久久久久蜜臀 | 日日夜日日干 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 免费看片日韩 | 九九免费精品视频在线观看 | 在线观看免费国产小视频 | 美女视频黄频 | 免费黄a| 欧美在线一 | 欧美一区二区三区在线看 | 国产午夜精品一区二区三区在线观看 | 国产精品毛片一区视频播不卡 | 亚洲激情综合 | 色在线免费观看 | 久久久久女人精品毛片九一 | 日韩videos| 成在人线av | 日韩黄色一级电影 | 国产va在线 | 男女免费视频观看 | 粉嫩高清一区二区三区 | 亚洲一区二区三区四区精品 | 久久精品资源 | 视频国产在线观看18 | 久久综合九色综合网站 | 久久综合久久综合这里只有精品 | 国产中文字幕久久 | 在线观看网站av | 中文字幕亚洲欧美日韩 | 伊人五月天| 青草草在线视频 | 国产精品一区二区三区电影 | 精品久久91 | 91福利社区在线观看 | 国产视频 亚洲精品 | 日韩一区二区三区高清免费看看 | 亚洲精品中文字幕视频 | 黄色av免费看 | 五月婷婷操 | 西西444www大胆高清视频 | 国产精品亚洲人在线观看 | 亚洲情感电影大片 | 91亚洲国产成人久久精品网站 | 91看片在线 | 91精品国产成人 | 日韩高清不卡在线 | 999久久a精品合区久久久 | 日韩免费观看一区二区 | 久99久精品 | 免费看短| 精品国产三级a∨在线欧美 免费一级片在线观看 | 日韩欧美国产激情在线播放 | 日本久热| 97av精品 | 波多野结衣电影一区二区三区 | 午夜国产一区二区 | 久久综合网色—综合色88 | 亚洲成人999 | 欧美在线视频一区二区 | 日日爽夜夜操 | 亚洲成av人片在线观看 | 免费日韩电影 | 另类五月激情 | 欧美精品黑人性xxxx | 日韩欧美在线综合网 | 久久久久久久久久久久电影 | 久久一级电影 | 人人爱人人做人人爽 | 69av视频在线观看 | 日韩精品播放 | 日韩一区在线免费观看 | 香蕉一区 | av电影中文字幕 | 中文字幕高清av | 一区精品久久 | 美女黄视频免费看 | 亚洲精品视频在线免费播放 | 日韩av电影网站在线观看 | 久草免费资源 | 国产色婷婷在线 | 欧美一级特黄aaaaaa大片在线观看 | 亚洲第一区精品 | 欧美日本国产在线观看 | 中文字幕久久精品亚洲乱码 | 久草免费在线视频 | 国产91精品久久久久 | 精品国产精品一区二区夜夜嗨 | 亚洲成色| 中文字幕在线免费看线人 | 最新日韩在线观看视频 | 九九天堂 | 99精品一级欧美片免费播放 | www.久久色.com | av国产在线观看 | 四虎国产 | 中文字幕日本在线 | 久久成人综合 | 97免费| 在线免费观看视频一区二区三区 | 久久国产一区 | 亚洲无线视频 | 日韩欧美精品在线观看视频 | 韩国一区二区av | av免费电影网站 | 久久久久久久久久伊人 | 国产精品中文字幕在线 | 又爽又黄又无遮挡网站动态图 | 亚洲一区二区三区在线看 | 免费日韩精品 | 欧美精品小视频 | 91桃色免费视频 | 亚洲乱亚洲乱亚洲 | 成人久久亚洲 | 日本公妇色中文字幕 | 黄a在线看| 日韩最新理论电影 | 国产精品九九视频 | v片在线看 | 中文字幕资源站 | 人人澡澡人人 | 欧美日本中文字幕 | 中文字幕av在线 | 国产日韩三级 | 久久亚洲视频 | 91在线91拍拍在线91 | 欧美日韩一区二区在线 | 久久国产精品99国产精 | 成+人+色综合 | 日韩激情视频 | 成人在线视频一区 | 成人久久免费视频 | 在线黄av | 99热在线国产精品 | 尤物九九久久国产精品的分类 | 久久er99热精品一区二区三区 | 日韩网站在线播放 | 日韩黄色中文字幕 | 久久久久久久亚洲精品 | 中文字幕一区二区三区四区久久 | 欧美成年黄网站色视频 | 中文字幕精品一区二区精品 | 久久久亚洲影院 | 国产精品99精品久久免费 | 久久天堂影院 | 日韩精品你懂的 | 久久久国产日韩 | 激情视频在线高清看 | 国模一二三区 | 精品主播网红福利资源观看 | 91av视频在线观看 | 色综合天天狠狠 | 成人h在线观看 | 精品视频免费播放 | 久久精品亚洲精品国产欧美 | 天天综合网国产 | 亚洲高清视频在线观看 | 国产精品中文字幕在线 | 综合色伊人 | 亚洲国产片 | 国产日产精品一区二区三区四区 | 五月天婷婷免费视频 | 久草免费在线观看视频 | aaawww| 国产精品2019 | 日韩黄色影院 | 久久久久久久99精品免费观看 | 午夜精品久久久久久久99 | 韩国在线一区二区 | 中文字幕在线免费播放 | 色成人亚洲网 | 亚洲精品乱码久久久久久蜜桃欧美 |