筆記-KVC的底層實(shí)現(xiàn)原理

KVC

KVC(key-Value coding) 鍵值編碼拯啦,指iOS開(kāi)發(fā)中,可以允許開(kāi)發(fā)者通過(guò)Key名直接訪(fǎng)問(wèn)對(duì)象的屬性萍悴,或者給對(duì)象的屬性賦值夹厌。不需要調(diào)用明確的存取方法,這樣就可以在運(yùn)行時(shí)動(dòng)態(tài)訪(fǎng)問(wèn)和修改對(duì)象的屬性,而不是在編譯時(shí)確定。

KVC的定義都是對(duì)NSObject的擴(kuò)展來(lái)實(shí)現(xiàn)的(Objective-C中有個(gè)顯示的NSKeyValueCoding類(lèi)別名,而Swift沒(méi)有次屠,也不需要)。所以對(duì)于所有繼承NSObject的類(lèi)型雳刺,也就是基本上所有的Objective-C對(duì)象都能使用KVC(一些純Swift類(lèi)和結(jié)構(gòu)體是不支持KVC的)

KVC最重要的四個(gè)方法

- (nullable id)valueForKey:(NSString *)key;                          //直接通過(guò)Key來(lái)取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通過(guò)Key來(lái)設(shè)值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通過(guò)KeyPath來(lái)取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通過(guò)KeyPath來(lái)設(shè)值

NSKeyValueCoding類(lèi)別中的其他方法

+ (BOOL)accessInstanceVariablesDirectly;
//默認(rèn)返回YES劫灶,表示如果沒(méi)有找到Set<Key>方法的話(huà),會(huì)按照_key掖桦,_iskey本昏,key,iskey的順序搜索成員枪汪,設(shè)置成NO就不這樣搜索

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供屬性值正確性驗(yàn)證的API涌穆,它可以用來(lái)檢查set的值是否正確、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因雀久。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//這是集合操作的API宿稀,里面還有一系列這樣的API,如果屬性是一個(gè)NSMutableArray赖捌,那么可以用這個(gè)方法來(lái)返回祝沸。

- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且KVC無(wú)法搜索到任何和Key有關(guān)的字段或者屬性越庇,則會(huì)調(diào)用這個(gè)方法罩锐,默認(rèn)是拋出異常。

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一個(gè)方法一樣卤唉,但這個(gè)方法是設(shè)值涩惑。

- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法時(shí)面給Value傳nil,則會(huì)調(diào)用這個(gè)方法

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//輸入一組key,返回該組key對(duì)應(yīng)的Value桑驱,再轉(zhuǎn)成字典返回竭恬,用于將Model轉(zhuǎn)到字典跛蛋。

設(shè)置

當(dāng)調(diào)用setValue:屬性值 forKey:@"name"代碼時(shí),底層的執(zhí)行機(jī)制:

  • 程序優(yōu)先調(diào)用setKey:屬性值方法痊硕,代碼通過(guò)setter方法完成設(shè)置问芬。注意,這里的key是指成員變量名寿桨,首字母大小寫(xiě)要符合KVC的命名規(guī)范,下同
  • 如果沒(méi)有找到setName:方法强戴,KVC機(jī)制會(huì)檢查+(BOOL)accessInstanceVariablesDirectly方法有沒(méi)有返回YES亭螟,默認(rèn)返回的是YES,如果你重寫(xiě)了該方法讓其返回NO骑歹,那么在這一步KVC會(huì)執(zhí)行setValue: forUndefineKey:方法预烙,不過(guò)一般不會(huì)這么做。所以KVC機(jī)制會(huì)搜索該類(lèi)里面有沒(méi)有名為_key的成員變量道媚,無(wú)論該變量是在.h扁掸,還是在.m文件里定義,也不論用什么樣的訪(fǎng)問(wèn)修飾符最域,只要存在_key命名的變量谴分,KVC都可以對(duì)該成員變量賦值。
  • 如果該類(lèi)既沒(méi)有setKey:方法镀脂,也沒(méi)有_key成員變量牺蹄,KVC機(jī)制會(huì)搜索_isKey的成員變量。
  • 同樣道理薄翅,如果該類(lèi)沒(méi)有setKey:方法沙兰,也沒(méi)有_key_isKey成員變量,KVC還會(huì)繼續(xù)搜索keyisKey的成員變量翘魄,再給他們賦值鼎天。
  • 如果上面列出的方法或者成員變量都不存在,系統(tǒng)將會(huì)執(zhí)行該對(duì)象的setValue:forUndefinedKey:方法暑竟,默認(rèn)是拋出異常斋射。

如果想讓這個(gè)類(lèi)禁用KVC,重寫(xiě)+(BOOL)accessInstanceVariablesDirectly方法但荤,讓其返回NO即可绩鸣,這樣的話(huà),如果KVC沒(méi)有找到setKey:時(shí)纱兑,會(huì)直接調(diào)用setValue:forUndefinedKey:方法呀闻。

取值

當(dāng)調(diào)用valueForKey:@"name"時(shí),KVC對(duì)key的搜索方式不同于setValue:屬性值 forKey:@"name"潜慎,方式如下

  • 首先按getKey捡多,key蓖康,isKey的順序方法查找getter方法。找到的話(huà)就會(huì)直接調(diào)用垒手。
    如果是BOOL或者Int等值類(lèi)型蒜焊,會(huì)將其包裝成一個(gè)NSNumber對(duì)象。
  • 如果上面的getter沒(méi)有找到科贬,KVC則會(huì)查找countOfKey泳梆,objectInKeyAtIndexkeyAtIndexes格式的方法。如果找到countOfKey和另外兩個(gè)方法中的一個(gè)榜掌,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合(他是NSKeyValueArray优妙,是NSArray的子類(lèi)),調(diào)用這個(gè)代理集合的方法或者說(shuō)給這個(gè)代理集合發(fā)送屬于NSArray的方法憎账,就會(huì)以countOfKey套硼,objectInKeyAtIndexkeyAtIndexes這幾個(gè)方法組合的形式調(diào)用,還有一個(gè)可選的getKey:range:方法胞皱。所以如果你想重新定義KVC的一些功能邪意,你可以添加這些方法。注意的是方法名的命名規(guī)則要符合KVC的標(biāo)準(zhǔn)命名方法反砌,包括方法簽名雾鬼。
  • 如果上面的方法沒(méi)有找到,那么會(huì)同時(shí)查找countOfKey宴树,enumeratorOfKey呆贿,memberOfKey格式的方法。如果這三個(gè)方法都找到森渐,那么就返回一個(gè)可以響應(yīng)NSSet所有方法的代理集合做入,和上面一樣,給這個(gè)代理集合發(fā)NSSet的消息同衣,就會(huì)以countOfKey竟块,enumeratorOfKeymemberOfKey組合的形式調(diào)用耐齐。

簡(jiǎn)單的說(shuō)就是如果你在自己的類(lèi)定義了KVC的實(shí)現(xiàn)浪秘,并且實(shí)現(xiàn)了上面的方法,那么你可以將返回的對(duì)象當(dāng)做數(shù)組(NSArray)/集合(NSSet)用了埠况。

  • 如果還沒(méi)有找到耸携,在檢查類(lèi)方法+(BOOL)accessInstanceVariablesDirectly,如果返回YES辕翰,那么和之前的設(shè)值一樣夺衍,會(huì)按_key,_isKey,key,isKey的順序搜索成員變量名,這里不推薦這樣做喜命,因?yàn)檫@樣直接訪(fǎng)問(wèn)實(shí)例變量破壞了封裝性沟沙,使代碼更脆弱河劝。如果重寫(xiě)了+(BOOL)accessInstanceVariablesDirectly返回NO的話(huà),那么直接調(diào)用valueForUndefinedKey:
  • 如果還沒(méi)有找到的話(huà)矛紫,調(diào)用valueForUndefinedKey:

使用keyPath

在開(kāi)發(fā)過(guò)程中赎瞎,一個(gè)類(lèi)的成員變量有可能是自定義類(lèi)或者其它的復(fù)雜數(shù)據(jù)類(lèi)型,可以先用KVC獲取該屬性颊咬,然后再用KVC來(lái)獲取這個(gè)自定義類(lèi)的屬性务甥。但是比較麻煩,可以使用KVC的鍵路徑keyPath

- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通過(guò)KeyPath來(lái)取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通過(guò)KeyPath來(lái)設(shè)值

參考下面的例子:

// Address類(lèi)
@interface Address()
@property (nonatomic, copy) NSString *country;
@end

// People類(lèi)
@interface People()
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) Address *address;
@property (nonatomic, assign) NSInteger age;
@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;
}
//打印結(jié)果 
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

KVC處理集合

簡(jiǎn)單集合運(yùn)算符

@avg喳篇、@count敞临、@max、@min杭隙、@sum5種,直接從代碼種理解其中含義

#import <Foundation/Foundation.h>

@interface Book : NSObject
@property (nonatomic, copy)  NSString *name;
@property (nonatomic, assign)  NSInteger price;
@end

@implementation Book
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Book *book1 = [Book new];
        book1.name = @"The Great Gastby";
        book1.price = 10;
        Book *book2 = [Book new];
        book2.name = @"Time History";
        book2.price = 20;
        Book *book3 = [Book new];
        book3.name = @"Wrong Hole";
        book3.price = 30;
        Book *book4 = [Book new];
        book4.name = @"Wrong Hole";
        book4.price = 40;
        
        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);
        
    }
    return 0;
}

打印結(jié)果:
sum:100.000000
avg:25.000000
count:4.000000
min:10.000000
max:40.000000

對(duì)象運(yùn)算符

想比較集合運(yùn)算符稍微復(fù)雜因妙,能以數(shù)組的方式返回指定的內(nèi)容

  • @distinctUnionOfObjects
  • @unionOfObjects

它們的返回值都是NSArray痰憎,區(qū)別是前者返回的元素都是唯一的,是去重以后的結(jié)果攀涵;后者返回的元素是全集铣耘。

#import <Foundation/Foundation.h>
@interface Book : NSObject
@property (nonatomic, copy)  NSString *name;
@property (nonatomic, assign)  NSInteger price;
@end

@implementation Book
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Book *book1 = [Book new];
        book1.name = @"The Great Gastby";
        book1.price = 40;
        Book *book2 = [Book new];
        book2.name = @"Time History";
        book2.price = 20;
        Book *book3 = [Book new];
        book3.name = @"Wrong Hole";
        book3.price = 30;
        Book *book4 = [Book new];
        book4.name = @"Wrong Hole";
        book4.price = 10;
        Book *book5 = [Book new];
        book5.name = @"Wrong Hole1";
        book5.price = 20;
        
        NSArray* arrBooks = @[book1, book2, book3, book4, book5];
        
        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);
        }
    }
    return 0;
}

打印結(jié)果:
distinctUnionOfObjects
10.000000
20.000000
30.000000
40.000000
unionOfObjects
10.000000
20.000000
30.000000
40.000000
20.000000

KVC處理字典

當(dāng)對(duì)NSDictionary對(duì)象使用KVC時(shí),valueForKey的表現(xiàn)行為和objectForKey一樣以故。所以使用valueForKeyPath用來(lái)訪(fǎng)問(wèn)多層嵌套的字典比較方便蜗细。

KVC里面還有兩個(gè)關(guān)于NSDictionary的方法:

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

dictionaryWithValuesForKeys:是指輸入一組key,返回這組key對(duì)應(yīng)的屬性怒详,在組成一個(gè)字典炉媒。
setValuesForKeysWithDictionary:是用來(lái)修改model種對(duì)應(yīng)key的屬性。
看代碼:

#import <Foundation/Foundation.h>

@interface Address : NSObject

@end

@interface Address()

@property (nonatomic, copy)NSString *country;
@property (nonatomic, copy)NSString *province;
@property (nonatomic, copy)NSString *city;
@property (nonatomic, copy)NSString *district;

@end

@implementation Address

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //模型轉(zhuǎn)字典
        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]; //把對(duì)應(yīng)key所有的屬性全部取出來(lái)
        NSLog(@"%@",dict);
        
        //字典轉(zhuǎn)模型
        NSDictionary *modifyDict = @{@"country" : @"USA", 
                                    @"province" : @"california", 
                                        @"city" : @"Los angle"
                                    };
        [add setValuesForKeysWithDictionary:modifyDict];            //用key Value來(lái)修改Model的屬性
        NSLog(@"country:%@  province:%@ city:%@", add.country, add.province, add.city);
    }
    return 0;
}

打印結(jié)果:
2018-05-05 17:08:48.824653+0800 KVCKVO[35807:6368235] {
city = "Shen Zhen";
country = China;
district = "Nan Shan";
province = "Guang Dong";
}
2018-05-05 17:08:48.825075+0800 KVCKVO[35807:6368235]
country:USA province:california city:Los angle

KVC的使用

動(dòng)態(tài)的取值和設(shè)值

利用KVC動(dòng)態(tài)的取值和設(shè)值是最基本的用途

KVC來(lái)訪(fǎng)問(wèn)和修改私有變量

對(duì)于類(lèi)里的私有屬性昆烁,Objective-C是無(wú)法直接訪(fǎng)問(wèn)的吊骤,但是KVC是可以的。
Model和字典轉(zhuǎn)換

這是KVC強(qiáng)大作用的又一次體現(xiàn)静尼,KVC和Objc的runtime組合可以很容易的實(shí)現(xiàn)Model和字典的轉(zhuǎn)換白粉。

修改一些控件的內(nèi)部屬性

這也是iOS開(kāi)發(fā)中必不可少的小技巧。眾所周知很多UI控件都由很多內(nèi)部UI控件組合而成的鼠渺,但是Apple度沒(méi)有提供這訪(fǎng)問(wèn)這些控件的API鸭巴,這樣我們就無(wú)法正常地訪(fǎng)問(wèn)和修改這些控件的樣式。
而KVC在大多數(shù)情況可下可以解決這個(gè)問(wèn)題拦盹。最常用的就是個(gè)性化UITextField中的placeHolderText了鹃祖。

操作集合

Apple對(duì)KVC的valueForKey:方法作了一些特殊的實(shí)現(xiàn),比如說(shuō)NSArray和NSSet這樣的容器類(lèi)就實(shí)現(xiàn)了這些方法普舆。所以可以用KVC很方便地操作集合惯豆。

用KVC實(shí)現(xiàn)高階消息傳遞

當(dāng)對(duì)容器類(lèi)使用KVC時(shí)池磁,valueForKey:將會(huì)被傳遞給容器中的每一個(gè)對(duì)象,而不是容器本身進(jìn)行操作楷兽。結(jié)果會(huì)被添加進(jìn)返回的容器中地熄,這樣,開(kāi)發(fā)者可以很方便的操作集合來(lái)返回另一個(gè)集合芯杀。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        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);
        }
    }
    return 0;
}

打印結(jié)果:
English
Franch
Chinese
7
6
7

方法capitalizedString被傳遞到NSArray中的每一項(xiàng)端考,這樣,NSArray的每一員都會(huì)執(zhí)行capitalizedString并返回一個(gè)包含結(jié)果的新的NSArray揭厚。
從打印結(jié)果可以看出却特,所有String的首字母都成功以轉(zhuǎn)成了大寫(xiě)。
同樣如果要執(zhí)行多個(gè)方法也可以用valueForKeyPath:方法筛圆。它先會(huì)對(duì)每一個(gè)成員調(diào)用 capitalizedString方法裂明,然后再調(diào)用length,因?yàn)?code>length方法返回是一個(gè)數(shù)字太援,所以返回結(jié)果以NSNumber的形式保存在新數(shù)組里闽晦。

實(shí)現(xiàn)KVO

可參考KVO

參考博客:jackyshan

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市提岔,隨后出現(xiàn)的幾起案子仙蛉,更是在濱河造成了極大的恐慌,老刑警劉巖碱蒙,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荠瘪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赛惩,警方通過(guò)查閱死者的電腦和手機(jī)哀墓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)喷兼,“玉大人麸祷,你說(shuō)我怎么就攤上這事“Γ” “怎么了阶牍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)星瘾。 經(jīng)常有香客問(wèn)我走孽,道長(zhǎng),這世上最難降的妖魔是什么琳状? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任磕瓷,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘困食。我一直安慰自己边翁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布硕盹。 她就那樣靜靜地躺著符匾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘩例。 梳的紋絲不亂的頭發(fā)上啊胶,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音垛贤,去河邊找鬼焰坪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛聘惦,可吹牛的內(nèi)容都是我干的某饰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼善绎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黔漂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起涂邀,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瘟仿,失蹤者是張志新(化名)和其女友劉穎箱锐,沒(méi)想到半個(gè)月后比勉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驹止,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年浩聋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臊恋。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衣洁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抖仅,到底是詐尸還是另有隱情坊夫,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布撤卢,位于F島的核電站环凿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏放吩。R本人自食惡果不足惜智听,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧到推,春花似錦考赛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至悔雹,卻和暖如春复哆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腌零。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工梯找, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人益涧。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓锈锤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闲询。 傳聞我的和親對(duì)象是個(gè)殘疾皇子久免,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容