iOS - 內(nèi)存管理(二)之Copy

1.前言

閱讀本文前請(qǐng)先閱讀第一篇《iOS - 內(nèi)存管理(一)之MRR》突琳,因?yàn)椴糠謨?nèi)容有涉及之前的知識(shí)點(diǎn)。
本來copy這個(gè)東西不完全屬于是內(nèi)存管理的內(nèi)容碗殷,不過由于內(nèi)存管理的規(guī)則涉及到copy這個(gè)方法葱色,同時(shí)copy的實(shí)現(xiàn)也有著許多內(nèi)存管理的注意點(diǎn),所以有必要專門在這里將其介紹清楚歪泳。本文的內(nèi)容都基于MRR模式


2.copy的目的

copy的目的就是為了不同的數(shù)據(jù)使用方使用或修改數(shù)據(jù)的時(shí)候不會(huì)影響到另一方正在使用的數(shù)據(jù)。
copy方法就是為了復(fù)制出來一個(gè)對(duì)象的副本露筒,副本內(nèi)的數(shù)據(jù)跟原對(duì)象一樣呐伞,這樣不管之后修改原對(duì)象或者副本對(duì)象的內(nèi)容,都不會(huì)影響到另外一方的數(shù)據(jù)慎式。
就好比我有一本書伶氢,我要借給某人,我又不想這個(gè)人在我書上亂畫瘪吏,我就去復(fù)印了一本癣防,給他了復(fù)印本。這樣不管以后我怎么畫掌眠,或者他怎么畫蕾盯,都不會(huì)影響到我們彼此手中的書里的內(nèi)容。
這個(gè)目的非常簡(jiǎn)單蓝丙,也非常重要级遭,這個(gè)目的要是沒有想清楚,之后有些點(diǎn)的東西在理解上會(huì)有困擾渺尘。


3.不同的copy

copy一個(gè)對(duì)象正常來說結(jié)果都是創(chuàng)建了一個(gè)新的對(duì)象挫鸽,新對(duì)象和舊對(duì)象數(shù)據(jù)是一樣的。但是有的時(shí)候并沒有創(chuàng)建新對(duì)象鸥跟,而直接返回了就對(duì)象的引用丢郊。針對(duì)這兩種情況,就有了不同的名稱锌雀,分別是 深拷貝淺拷貝蚂夕。

3.1淺拷貝

如果細(xì)心的人可能會(huì)問了,你上面不是剛說了copy的目的就是要?jiǎng)?chuàng)建一個(gè)新的副本腋逆,這樣兩邊使用自己的對(duì)象就不會(huì)影響到別人了么?為什么還會(huì)有淺拷貝的情況出現(xiàn)侈贷?如果舊的和新的都是一個(gè)對(duì)象那還怎么能達(dá)到copy的目的惩歉?
這個(gè)問題其實(shí)很好等脂,首先正常來說copy都是深拷貝,只有當(dāng)某個(gè)類是不可變類型的時(shí)候撑蚌,才會(huì)出現(xiàn)淺拷貝的情況上遥。
什么叫不可變的類型?即這個(gè)類型的對(duì)象里面放的數(shù)據(jù)争涌,一旦初始化了粉楚,之后就不能改變了。比如我們都熟悉的NSString亮垫,NSArray或者NSDictionary模软。

NSString *str = @"123";
//copyStr 和 str其實(shí)是指向內(nèi)存里同一個(gè)字符串對(duì)象,這個(gè)copy就是一個(gè)淺拷貝
NSString *copyStr = [str copy];

上面的代碼中饮潦,雖然這個(gè)copy一個(gè)淺拷貝燃异,但是它并沒有違背我們copy的目的。因?yàn)镹SString本身就是不可變的继蜡,也就是說不管是拿著舊的引用str還是副本引用copyStr回俐,你都沒法改變它內(nèi)部的數(shù)據(jù),也就是這個(gè)字符串永遠(yuǎn)都是123稀并。所以為了節(jié)省資源仅颇,這個(gè)copy也就沒有必要返回一個(gè)新的對(duì)象了。這便是淺拷貝的意義所在碘举。
有人可能想說灵莲,我就是想對(duì)一個(gè)不可變的類型對(duì)象,拷貝一個(gè)真正的新的對(duì)象出來殴俱,行不行政冻?行!還有另一個(gè)拷貝方法线欲,mutableCopy明场,

NSString *str = @"123";
//因?yàn)槭鞘褂昧薽utableCopy,明確說明我是要可變的拷貝李丰,所以拷貝出來的對(duì)象是一個(gè)新的對(duì)象
//并且這個(gè)對(duì)象的類型也變了苦锨,變成了它的可變類型NSMutableString
//這就是一個(gè)深拷貝
NSMutableString *copyStr = [str mutableCopy];


3.2深拷貝

深拷貝就很簡(jiǎn)單了,copy之后的對(duì)象是一個(gè)新的對(duì)象趴泌,比如上面的例子對(duì)一個(gè)不可變的字符串調(diào)用了mutableCopy這就是一個(gè)深拷貝舟舒。
假如對(duì)一個(gè)可變的類型進(jìn)行copy會(huì)怎樣?很簡(jiǎn)單嗜憔,對(duì)一個(gè)可變的類型進(jìn)行不管copy還是mutableCopy秃励,結(jié)果都是深拷貝。想想上面最開始說的copy的目的就能馬上明白吉捶,本身就是可變類型夺鲜,表示可以改變對(duì)象保存的數(shù)據(jù)內(nèi)容皆尔,如果還直接返回一個(gè)舊對(duì)象,那就沒法避免互相影響了币励。


3.3內(nèi)存管理的角度分析

下面是一個(gè)小總結(jié)慷蠕,如果理解了copy的目的和淺拷貝深拷貝,就不用背這種東西都能自己寫出來食呻。

不可變類型 -> copy -> 不可變類型(淺拷貝流炕,新舊指針指向同一個(gè)對(duì)象,該對(duì)象引用計(jì)數(shù)+1)
不可變類型 -> mutableCopy -> 可變類型 (深拷貝仅胞,舊對(duì)象引用計(jì)數(shù)不變每辟,新對(duì)象引用計(jì)數(shù)=1)
可變類型 -> copy -> 可變類型 (深拷貝,舊對(duì)象引用計(jì)數(shù)不變饼问,新對(duì)象引用計(jì)數(shù)=1)
可變類型 -> mutableCopy -> 可變類型 (深拷貝影兽,舊對(duì)象引用計(jì)數(shù)不變,新對(duì)象引用計(jì)數(shù)=1)

只有當(dāng)一個(gè)拷貝是淺拷貝的時(shí)候莱革,新舊指針指向同一個(gè)對(duì)象峻堰,該對(duì)象引用計(jì)數(shù)+1。如果一個(gè)拷貝是深拷貝的時(shí)候舊對(duì)象引用計(jì)數(shù)不變盅视,新對(duì)象引用計(jì)數(shù)=1捐名。
如果再細(xì)心的人在這里就能明白為什么我在上一篇《iOS - 內(nèi)存管理(一)之MRR》中說“用copy和mutableCopy獲得的新對(duì)象retainCount等于1”這一句話不嚴(yán)謹(jǐn)了。包括蘋果官方文檔的那個(gè)配圖闹击,也不嚴(yán)謹(jǐn)了镶蹋。

蘋果官方文檔示例圖

因?yàn)檫@種不嚴(yán)謹(jǐn)?shù)恼f法都是深拷貝的情況,淺拷貝的時(shí)候retainCount就不是如上所說了赏半。

但是贺归!但是!這個(gè)但是很重要断箫,雖然說不嚴(yán)謹(jǐn)拂酣,但是并不是不正確,并且在某種層面上來說是很正確的仲义。如果站在底層真實(shí)retainCount的角度來說不嚴(yán)謹(jǐn)婶熬。但是如果站在使用層面,或者說只要遵守了MRR的內(nèi)存管理原則埃撵,你怎么去理解這個(gè)copy都不會(huì)造成內(nèi)存泄漏問題赵颅,你把它全部都當(dāng)成深拷貝來管理都沒關(guān)系。下面用代碼舉例說明

//從真實(shí)retainCount角度來看
{
  //新建的一個(gè)不可變的數(shù)組暂刘,數(shù)組的retainCount等于1
  NSArray *arr = [[NSArray alloc] initWithObjects:obj, nil];
  //這是一個(gè)淺拷貝饺谬,這個(gè)數(shù)組的retainCount = 2
  NSArray * arr2 = [arr copy];
  
  //因?yàn)閍rr和arr2都是指向同一個(gè)內(nèi)存對(duì)象,所以我調(diào)用arr release和arr2 release效果一樣的
  //release兩次就對(duì)象銷毀了
  [arr release];
  [arr release];//或者[arr2 release]效果相同
}

可以看到這種純考慮retainCount的想法不是太可取鸳惯,而且也不應(yīng)該對(duì)一個(gè)變量調(diào)用兩次release商蕴,站在使用方的層面也不應(yīng)該去考慮這個(gè)考慮是淺的還是深的叠萍。所以我們換一個(gè)思維來管理芝发,就是上一篇說的只要是alloc/new/copy/mutableCopy返回的绪商,都當(dāng)成一個(gè)新的對(duì)象,注意看下面代碼的注釋:

//使用方角度來看
{
  //新建的一個(gè)不可變的數(shù)組辅鲸,因?yàn)槭怯胊lloc創(chuàng)建的格郁,所以持有他,有責(zé)任釋放
  NSArray *arr = [[NSArray alloc] initWithObjects:obj, nil];
  //copy了一個(gè)新數(shù)組出來独悴,不要管是不是淺拷貝例书,因?yàn)槭莄opy的,所以持有他刻炒,有責(zé)任釋放
  NSArray * arr2 = [arr1 copy];
  
  //arr和arr2都是持有的决采,所以都有責(zé)任釋放
  [arr release];
  [arr2 release];
}

可以看到兩份代碼一樣的,所以內(nèi)存管理的結(jié)果也是一樣的坟奥,但是由于思想不同树瞭,在寫這兩個(gè)代碼時(shí)候管理內(nèi)存的思路是不同的,也可以看出就將copy看成是獲得了一個(gè)獨(dú)立的副本會(huì)更加有利于去內(nèi)存管理爱谁。

理解一個(gè)東西很重要的一點(diǎn)是要基于這個(gè)東西所在的層的原則去理解晒喷,有時(shí)候你要用下一層實(shí)現(xiàn)的思維去使用上一層的方法往往會(huì)給理解造成困擾。


4.NSCopying和NSMutableCopying

NSCopying是一個(gè)protocol協(xié)議访敌,我們平時(shí)自定義的類默認(rèn)是沒有實(shí)現(xiàn)這個(gè)協(xié)議的凉敲,也就是說我們自定義的類時(shí)沒有copy方法可以調(diào)用的,如果想要有copy方法寺旺,就要讓你的自定義類遵守NSCopying這個(gè)協(xié)議爷抓,并實(shí)現(xiàn)協(xié)議規(guī)定的方法。

4.1 copyWithZone

NSCopying協(xié)議只規(guī)定了一個(gè)方法要實(shí)現(xiàn)就是:

- (id)copyWithZone:(NSZone *)zone

在這個(gè)方法里阻塑,返回一個(gè)新的副本對(duì)象的引用蓝撇。
對(duì)于前面說的不可變類型淺拷貝的情況,就是在里面直接返回當(dāng)前對(duì)象的引用:

- (id)copyWithZone:(NSZone *)zone
{
    return [self retain];
}


4.2 實(shí)例變量的copy方式

對(duì)于我們的自定義的類叮姑,通常都不會(huì)是不可變的類型唉地,也就是說我們自定義的類創(chuàng)建的對(duì)象里面保存的數(shù)據(jù)通常都是可以被不斷重設(shè)的,這種情況下copyWithZone方法的實(shí)現(xiàn)就不是簡(jiǎn)單的返回self retain了传透。

比如我有如下的一個(gè)自定義類耘沼,它想要實(shí)現(xiàn)copy方法:

@interface Book : NSObject <NSCopying>
{
    NSString *_bookName;
    Person *_author;
}
@end

這個(gè)類可能有很多個(gè)實(shí)例變量,當(dāng)我創(chuàng)建了一個(gè)新的副本對(duì)象朱盐,這些原有實(shí)例變量?jī)?nèi)容需要都被賦值過去群嗤,那么這些實(shí)例變量它具體是要被深拷貝還是淺拷貝到新的對(duì)象里,這就要看這個(gè)實(shí)例變量的setter方法是怎么寫的兵琳。

(1)如果setter方法里是copy了再賦值給實(shí)例變量,那么就要深拷貝它

意思就是统阿,比如_author的setter方法如下

- (void)setAuthor:(Person *)author
{
  [_author autorelease];
  // 這里對(duì)新設(shè)置進(jìn)來的author進(jìn)行了copy
  // 說明這里本意就是對(duì)設(shè)置的author持有它的副本米间,從而外接改變了原有author的內(nèi)容也不影響當(dāng)前實(shí)例變量的_author值
  // 更直接的說就是,是希望不同的Book對(duì)象持有不同的author對(duì)象
  _author = [author copy];
}

所以根據(jù)注釋說的破衔,每設(shè)置一個(gè)新的值進(jìn)來的時(shí)候,setter都會(huì)copy一個(gè)副本出來再傳給實(shí)例變量钱烟。所以再copy整個(gè)Book類的時(shí)候晰筛,也要深拷貝這個(gè)實(shí)例變量。


(2)如果setter方法里是retain新值再賦值拴袭,那么就要淺拷貝它

加入author的setter方法如下读第,則在copy Book的時(shí)候只需要淺拷貝這個(gè)變量:

- (void)setAuthor:(Person *)author
{
  [_author autorelease];
  // 這里對(duì)新設(shè)置進(jìn)來的author進(jìn)行了retain
  // 說明這里本意就是持有這個(gè)新值,而不是新值的副本
  _author = [author retain];
}

也很容易理解拥刻,就是它只想持有原始對(duì)象怜瞒,沒有想持有一個(gè)不一樣的副本。


(3)如果setter方法里是直接賦值的般哼,那么也是淺拷貝它

- (void)setAuthor:(Person *)author
{
  // 這里對(duì)新設(shè)置進(jìn)來的author直接賦值
  // 說明這里本意就是使用這個(gè)新值吴汪,而不是持有,允許它雖然被銷毀
  // 更不是要新值的副本
  _author = author;
}

這種情況也是淺拷貝即可逝她,它只想要原始對(duì)象的使用權(quán)浇坐,沒有想持有一個(gè)不一樣的副本。


4.3 父類沒有實(shí)現(xiàn)copy方法

當(dāng)一個(gè)類的父類沒有實(shí)現(xiàn)copy方法的時(shí)候黔宛,在這個(gè)類的copyWithZone里近刘,最好是使用alloc,initXXX方法來創(chuàng)建新的副本對(duì)象。因?yàn)槟切├^承的實(shí)例變量的細(xì)節(jié)通常都被封裝在里面了臀晃。

- (void)copyWithZone:(NSZone *)zone
{
   Book *copy = [[Book alloc] initWithName:[self bookName] author:[self author]];
   return copy;
}

只要父類沒有實(shí)現(xiàn)copy觉渴,子類就有義務(wù)將父類中定義的變量和子類中定義的變量都拷貝給副本對(duì)象里。


4.3 父類實(shí)現(xiàn)了copy方法

當(dāng)父類實(shí)現(xiàn)了copy方法徽惋,我們可以直接super copyWithZone來獲得副本對(duì)象案淋,然后再將子類自己定義的實(shí)例變量復(fù)制給新的副本對(duì)象。

//假設(shè)父類實(shí)現(xiàn)了copy
- (void)copyWithZone:(NSZone *)zone
{
   Book *copy = [super copyWithZone:zone];
   [copy setAuthor:[self author]];
   return copy;
}

假如父類的copyWithZone的實(shí)現(xiàn)里面可能或者用了NSCopyObject()這個(gè)方法來創(chuàng)建副本险绘,那么在子類里調(diào)用完了父類的copyWithZone之后踢京,還要將父類里定義的retain或者copy的實(shí)例變量,重新賦值一次宦棺。

注意:因?yàn)镹SCopyObject()只會(huì)淺拷貝所有實(shí)例變量瓣距,也就是說通過NSCopyObject()拷貝返回出來的對(duì)象是一個(gè)新的對(duì)象,但是對(duì)象里面的所有實(shí)例變量都是指向原本對(duì)象的實(shí)例變量代咸,并且這些實(shí)力變量指向內(nèi)容的retainCount是沒有變化的蹈丸。并且賦值的時(shí)候一定要注意先將實(shí)例變量設(shè)為nil,否則會(huì)實(shí)例變量被提前銷毀。上代碼解釋更清楚:

//假設(shè)我的TestObj類有兩個(gè)實(shí)例變量
//隨便取的類型的名字逻杖,不要太糾結(jié)
@interface TestObj : NSObject
{
  NSMutableString *_name;
  NSError *_error;
}

//_name的set方法里奋岁,舊值release,copy 了新值然后保存
- (void)setName:(NSMutableString *)name
{
  [_name release];
  _name = [name mutableCopy];
}

//error的set方法里荸百,舊值release闻伶,retain了新值然后保存
- (void)setError:(NSError *)error
{
  [_error release];
  _error = [error retain];
}

//TestObj類的copy方法
- (id)copyWithZone:(NSZone *)zone
{
  /*
    使用NSCopyObject()方法copy出來的新的副本對(duì)象copy
  */
  TestObj *copy = NSCopyObject(self, 0, zone);
  /*
    走完上面這句以后self和copy是兩個(gè)TestObj對(duì)象,它們的實(shí)例變量里的值是一樣的
    由于NSCopyObject()方法拷貝出來對(duì)象里的實(shí)例變量值都是淺拷貝
    也就是說copy里面的_name和_error指向的對(duì)象值就是self的_name和_error里的管搪,并且retainCount都沒變
     這就到之后最后銷毀的時(shí)候很可能會(huì)被多release一次虾攻,導(dǎo)致程序異痴÷颍或崩潰
  */

  /*
   所以我們必須在NSCopyObject之后更鲁,重新設(shè)值一次原對(duì)象“持有”的那些實(shí)例變量。
   至于為什么要先設(shè)置為nil奇钞,可以這樣理解:
   假設(shè)當(dāng)前self的_name的retainCount=1澡为,copy的_name因?yàn)槭菧\拷貝,一樣的值retainCount也是1
   但是set方法里會(huì)先autorelease舊的值景埃,所以還沒等賦值媒至,_name的retainCount就歸零了
   所以要先將這個(gè)淺拷貝的“弱指針”指向nil。
  */ 
  copy->_name = nil;
  [copy setName:self.name];
  copy->_error = nil;
  [copy setError:self.error];
  
  return copy;
}



5.NSMutableCopying

以上講的都是NSCopying協(xié)議谷徙,NSMutableCopying協(xié)議同理拒啰,只有一個(gè)方法要實(shí)現(xiàn):

-(id)mutableCopyWithZone:(nullable NSZone *)zone;

但是通常來說,我們自己的自定義的類要實(shí)現(xiàn)copy方法完慧,只需要遵守NSCopying協(xié)議即可谋旦,因?yàn)橐话愕淖远x類都是可變的。除非有特殊情況屈尼,你要實(shí)現(xiàn)想NSString和NSMutableString這種配對(duì)的可變和不可變的類册着,那么你才需要遵守這個(gè)協(xié)議。然后針對(duì)可變不可變來實(shí)現(xiàn)不同的內(nèi)容脾歧。


6. 結(jié)語

至此copy相關(guān)的內(nèi)容就介紹完了甲捏,主要還是為了填補(bǔ)上一篇中遺留的一些內(nèi)容,可以繼續(xù)閱讀下一篇《iOS - 內(nèi)存管理(三)之ARC》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鞭执,一起剝皮案震驚了整個(gè)濱河市司顿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兄纺,老刑警劉巖大溜,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異囤热,居然都是意外死亡猎提,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锨苏,“玉大人疙教,你說我怎么就攤上這事∩∽猓” “怎么了贞谓?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)葵诈。 經(jīng)常有香客問我裸弦,道長(zhǎng),這世上最難降的妖魔是什么作喘? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任理疙,我火速辦了婚禮,結(jié)果婚禮上泞坦,老公的妹妹穿的比我還像新娘窖贤。我一直安慰自己,他們只是感情好贰锁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布赃梧。 她就那樣靜靜地躺著,像睡著了一般豌熄。 火紅的嫁衣襯著肌膚如雪授嘀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天锣险,我揣著相機(jī)與錄音蹄皱,去河邊找鬼。 笑死囱持,一個(gè)胖子當(dāng)著我的面吹牛夯接,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纷妆,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盔几,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了掩幢?” 一聲冷哼從身側(cè)響起逊拍,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎际邻,沒想到半個(gè)月后芯丧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡世曾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年缨恒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骗露,死狀恐怖岭佳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萧锉,我是刑警寧澤珊随,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站柿隙,受9級(jí)特大地震影響叶洞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禀崖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一衩辟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帆焕,春花似錦惭婿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽换吧。三九已至折晦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沾瓦,已是汗流浹背满着。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贯莺,地道東北人风喇。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缕探,于是被迫代替她去往敵國(guó)和親魂莫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354