iOS 可变拷贝VS不可变拷贝

iOS 可变拷贝VS不可变拷贝

概念

我们先来了解两个概念

深拷贝deep copy: 直接拷贝整个对象内存到另一块内存中;

浅拷贝shallow copy: 并不拷贝对象本身,仅仅是拷贝指向对象的指针;

详细介绍看下面这段Apple Documents描述;

The normal copy is a shallow copy that produces a new collection that shares ownership of the objects with the original. Deep copies create new objects from the originals and add those to the new collection.

字符串

我们分成2种情况测试:

  1. 初始值不可变
    NSString *originStr = @"hello";
    NSString *copyStr = [originStr copy];
    NSString *mulCopyStr = [originStr mutableCopy];
    NSLog(@"originStr address: %p", originStr);
    NSLog(@"copyStr address: %p", copyStr);
    NSLog(@"mulCopyStr address: %p", mulCopyStr);
2017-09-21 13:36:08.859 iOSLockDemo[35388:2491304] originStr address: 0x1021e6078
2017-09-21 13:36:08.859 iOSLockDemo[35388:2491304] copyStr address: 0x1021e6078
2017-09-21 13:36:08.860 iOSLockDemo[35388:2491304] mulCopyStr address: 0x608000078000

通过log可以发现,此时copy是浅拷贝,mutableCopy是深拷贝;

  1. 初始值可变
    NSString *originStr = [@"hello" mutableCopy];
    NSString *copyStr = [originStr copy];
    NSString *mulCopyStr = [originStr mutableCopy];
    NSLog(@"originStr address: %p", originStr);
    NSLog(@"copyStr address: %p", copyStr);
    NSLog(@"mulCopyStr address: %p", mulCopyStr);
2017-09-21 13:39:33.036 iOSLockDemo[35469:2498312] originStr address: 0x60800007bd40
2017-09-21 13:39:33.037 iOSLockDemo[35469:2498312] copyStr address: 0xa00006f6c6c65685
2017-09-21 13:39:33.037 iOSLockDemo[35469:2498312] mulCopyStr address: 0x60800007c000

通过log可以发现,此时copymutableCopy都是深拷贝;

集合

  1. 初始值为不可变集合
    NSArray *originArr = @[@"item1"];
    NSLog(@"originArr address: %p", originArr);
    NSLog(@"originArr item address: %p", originArr[0]);
    
    NSArray *copyArr = [originArr copy];
    NSLog(@"copyArr address: %p", copyArr);
    NSLog(@"copyArr item address: %p", copyArr[0]);
    
    NSArray *mulCopyArr = [originArr mutableCopy];
    NSLog(@"mulCopyArr address: %p", mulCopyArr);
    NSLog(@"mulCopyArr item address: %p", mulCopyArr[0]);

2017-09-21 13:48:16.515 iOSLockDemo[35683:2513469] originArr address: 0x600000006ca0
2017-09-21 13:48:16.516 iOSLockDemo[35683:2513469] originArr item address: 0x10d338080
2017-09-21 13:48:16.516 iOSLockDemo[35683:2513469] copyArr address: 0x600000006ca0
2017-09-21 13:48:16.517 iOSLockDemo[35683:2513469] copyArr item address: 0x10d338080
2017-09-21 13:48:16.517 iOSLockDemo[35683:2513469] mulCopyArr address: 0x608000244800
2017-09-21 13:48:16.518 iOSLockDemo[35683:2513469] mulCopyArr item address: 0x10d338080

通过Log,我们可以发现,当初始值不可变时

copy操作,无论集合本身,还是集合里的objects都是指针拷贝,这时我们可以称这个过程是浅拷贝。

mutableCopy操作,集合指针发生了变化,说明生成了新的集合,但是集合里的objects指针还是和原来一样。那么这时我们还能称之为深拷贝吗?

下面我们引入一个概念单层深拷贝one-level-deep copy;什么意思呢?下面我们看一段代码就明白了;

Apple Documents提供了一个进行one-level-deep copy的方法。

[[NSArray alloc] initWithArray:<#(nonnull NSArray *)#> copyItems:<#(BOOL)#>]

You can use the collection’s equivalent of initWithArray:copyItems: with YES as the second parameter. If you create a deep copy of a collection in this way, each object in the collection is sent a copyWithZone: message. If the objects in the collection have adopted the NSCopying protocol, the objects are deeply copied to the new collection, which is then the sole owner of the copied objects. If the objects do not adopt the NSCopying protocol, attempting to copy them in such a way results in a runtime error.

用这种方法深复制,集合里的每个对象都会收到copyWithZone:消息。如果集合里的对象遵循NSCopying协议,那么对象就会被深拷贝(deep copy)到新的集合,并且这个新的集合是被拷贝对象的唯一所有者,如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。

看到这里,是不是觉得生活很美好,这方法使用很简单?too young too naive;下面我们看几个例子;

  1. 集合不可变,集合内元素为非集合对象,并且集合内元素为不可变对象。
    NSArray *originArr = @[@"11", @"22"];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
   
2017-09-21 15:58:53.073 iOSLockDemo[38249:2650494] originArr address: 0x608000029180
2017-09-21 15:58:53.073 iOSLockDemo[38249:2650494] originArr subArr item 0 address: 0x10f012080
2017-09-21 15:58:53.073 iOSLockDemo[38249:2650494] originArr subArr item 1 address: 0x10f0120a0
2017-09-21 15:58:53.073 iOSLockDemo[38249:2650494] copyArr address: 0x600000028600
2017-09-21 15:58:53.073 iOSLockDemo[38249:2650494] copyArr subArr item 0 address: 0x10f012080
2017-09-21 15:58:53.074 iOSLockDemo[38249:2650494] copyArr subArr item 1 address: 0x10f0120a0
2017-09-21 15:58:53.074 iOSLockDemo[38249:2650494] mutCopyArr address: 0x60000005c5f0
2017-09-21 15:58:53.074 iOSLockDemo[38249:2650494] mutCopyArr subArr item 0 address: 0x10f012080
2017-09-21 15:58:53.074 iOSLockDemo[38249:2650494] mutCopyArr subArr item 1 address: 0x10f0120a0

通过log发现了什么?无论可变拷贝,还是非可变拷贝,进行深拷贝的仍然只是集合本身,而集合内的元素还是指针拷贝,这下懵了,说好的集合内对象进行深拷贝呢?

再看下面一个例子

  1. 集合不可变,集合内元素为非集合对象,并且集合内元素为可变对象。
    NSArray *originArr = @[[@"11" mutableCopy], [@"22"  mutableCopy]];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 16:06:16.961 iOSLockDemo[38399:2658835] originArr address: 0x60000003a900
2017-09-21 16:06:16.964 iOSLockDemo[38399:2658835] originArr subArr item 0 address: 0x60000007c9c0
2017-09-21 16:06:16.964 iOSLockDemo[38399:2658835] originArr subArr item 1 address: 0x60000007cc80
2017-09-21 16:06:16.964 iOSLockDemo[38399:2658835] copyArr address: 0x60000003a920
2017-09-21 16:06:16.965 iOSLockDemo[38399:2658835] copyArr subArr item 0 address: 0xa000000000031312
2017-09-21 16:06:16.965 iOSLockDemo[38399:2658835] copyArr subArr item 1 address: 0xa000000000032322
2017-09-21 16:06:16.965 iOSLockDemo[38399:2658835] mutCopyArr address: 0x60800005e390
2017-09-21 16:06:16.966 iOSLockDemo[38399:2658835] mutCopyArr subArr item 0 address: 0xa000000000031312
2017-09-21 16:06:16.966 iOSLockDemo[38399:2658835] mutCopyArr subArr item 1 address: 0xa000000000032322

通过log发现,无论可变拷贝,还是非可变拷贝,集合本身和集合内对象都进行了深拷贝,但是两次拷贝的结果中,集合内对象的指针地址居然相同(ps: 多次测试,仍然如此,有兴趣的同学,可以多加几组拷贝试试),这下更懵了。

  1. 集合可变,集合内元素为非集合对象,并且集合内元素为不可变对象
    NSArray *originArr = [@[@"11", @"2"] mutableCopy];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 16:15:27.249 iOSLockDemo[38637:2669678] originArr address: 0x60000005a880
2017-09-21 16:15:27.249 iOSLockDemo[38637:2669678] originArr subArr item 0 address: 0x10baf8080
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] originArr subArr item 1 address: 0x10baf80a0
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] copyArr address: 0x608000035580
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] copyArr subArr item 0 address: 0x10baf8080
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] copyArr subArr item 1 address: 0x10baf80a0
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] mutCopyArr address: 0x60800005e5a0
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] mutCopyArr subArr item 0 address: 0x10baf8080
2017-09-21 16:15:27.250 iOSLockDemo[38637:2669678] mutCopyArr subArr item 1 address: 0x10baf80a0

通过log,这组结果和第一组是一样的。好,我们再看一组实验。

  1. 集合可变,集合内元素为非集合对象,并且集合内元素为可变对象
    NSArray *originArr = [@[[@"11" mutableCopy], [@"22"  mutableCopy]] mutableCopy];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 16:17:48.706 iOSLockDemo[38703:2673443] originArr address: 0x6000000482b0
2017-09-21 16:17:48.706 iOSLockDemo[38703:2673443] originArr subArr item 0 address: 0x600000267600
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] originArr subArr item 1 address: 0x6000002678c0
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] copyArr address: 0x60800003d120
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] copyArr subArr item 0 address: 0xa000000000031312
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] copyArr subArr item 1 address: 0xa000000000032322
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] mutCopyArr address: 0x608000046510
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] mutCopyArr subArr item 0 address: 0xa000000000031312
2017-09-21 16:17:48.707 iOSLockDemo[38703:2673443] mutCopyArr subArr item 1 address: 0xa000000000032322

这组实验结果和第二组一样。是不是彻底懵了?难道是官方文档写错了?好,那我们自定义一个对象,并实现NSCopying协议,然后在集合中使用我们自定义的对象。

#import <Foundation/Foundation.h>

@interface Person : NSObject<NSCopying>

@end

#import "Person.h"

@implementation Person

- (id)copyWithZone:(nullable NSZone *)zone {
    return [Person allocWithZone:zone];
}

@end
    
    NSArray *originArr = @[[Person new], [Person new]];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 16:33:46.501 iOSLockDemo[39061:2691963] originArr address: 0x60000003cc60
2017-09-21 16:33:46.501 iOSLockDemo[39061:2691963] originArr subArr item 0 address: 0x60000001d790
2017-09-21 16:33:46.502 iOSLockDemo[39061:2691963] originArr subArr item 1 address: 0x60000001d7a0
2017-09-21 16:33:46.502 iOSLockDemo[39061:2691963] copyArr address: 0x60800003d2e0
2017-09-21 16:33:46.502 iOSLockDemo[39061:2691963] copyArr subArr item 0 address: 0x60800001d2d0
2017-09-21 16:33:46.502 iOSLockDemo[39061:2691963] copyArr subArr item 1 address: 0x60800001d2a0
2017-09-21 16:33:46.502 iOSLockDemo[39061:2691963] mutCopyArr address: 0x60800005fda0
2017-09-21 16:33:46.503 iOSLockDemo[39061:2691963] mutCopyArr subArr item 0 address: 0x60800001d2b0
2017-09-21 16:33:46.503 iOSLockDemo[39061:2691963] mutCopyArr subArr item 1 address: 0x60800001d2c0

好神奇,这次结果和苹果官方文档写的一致了。这是为什么呢?

[[NSArray alloc] initWithArray: copyItems:得到的最终结果其实是和copyWithZone:的实现方式息息相关的,之所以集合中是字符串时会出现和官方文档相左的结果;

以下是个人猜测,望了解的同学可以指点一二。


因为NSString在实现copyWithZone:根据不同的情况作了优化。如果当前是可变对象,则返回一个不可变的副本,如果当前对象是不可变对象,则返回当前对象。**

**至于第二次实验中两次拷贝结果中的容器中的对象地址相同的情况,是NSArray在实现[[NSArray alloc] initWithArray: copyItems:为了节约内存开销,进行了优化。当第一次调用时,会给集合内对象发送copyWithZone:生成新的不可变副本,以后调用时,不再向原对象发送消息,而是使用第一次生成的不可变副本。


下面我们测试一下多层集合嵌套的情况。

    NSArray *originArr = @[@[[Person new], [Person new]]];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray *copyArr = [[NSArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray *mutCopyArr = [[NSMutableArray alloc] initWithArray:originArr copyItems:YES];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 17:16:34.672 iOSLockDemo[39913:2737590] originArr address: 0x600000005670
2017-09-21 17:16:34.672 iOSLockDemo[39913:2737590] originArr subArr address: 0x60000002d820
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] originArr subArr item 0 address: 0x600000005650
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] originArr subArr item 1 address: 0x600000005660
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] copyArr address: 0x600000005610
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] copyArr subArr address: 0x60000002d820
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] copyArr subArr item 0 address: 0x600000005650
2017-09-21 17:16:34.673 iOSLockDemo[39913:2737590] copyArr subArr item 1 address: 0x600000005660
2017-09-21 17:16:34.674 iOSLockDemo[39913:2737590] mutCopyArr address: 0x600000057970
2017-09-21 17:16:34.674 iOSLockDemo[39913:2737590] mutCopyArr subArr address: 0x60000002d820
2017-09-21 17:16:34.674 iOSLockDemo[39913:2737590] mutCopyArr subArr item 0 address: 0x600000005650
2017-09-21 17:16:34.674 iOSLockDemo[39913:2737590] mutCopyArr subArr item 1 address: 0x600000005660

通过log,可以发现,只有最外层的集合地址发生了变化,其他的不变。

由此我们得出结论:

one-level-deep copy所谓的单层深拷贝的意思是,只有非嵌套集合,且集合内对象在实现copyWithZone:时返回新的对象时,才会产生真正意义上的深拷贝,也就是完全拷贝(real-deep copy)

那么怎样才能实现真正意义上的深拷贝呢?看下面实现。

完全拷贝

    NSArray *originArr = @[@[[@"11" mutableCopy], [@"22"  mutableCopy]]];
    NSLog(@"originArr address: %p", originArr);
    [self print:originArr arrName:@"originArr"];
    
    NSArray* copyArr = [NSKeyedUnarchiver unarchiveObjectWithData:
                                  [NSKeyedArchiver archivedDataWithRootObject:originArr]];
    NSLog(@"copyArr address: %p", copyArr);
    [self print:copyArr arrName:@"copyArr"];
    
    NSArray* mutCopyArr = [NSKeyedUnarchiver unarchiveObjectWithData:
                        [NSKeyedArchiver archivedDataWithRootObject:originArr]];
    NSLog(@"mutCopyArr address: %p", mutCopyArr);
    [self print:mutCopyArr arrName:@"mutCopyArr"];
2017-09-21 17:27:49.138 iOSLockDemo[40148:2754251] originArr address: 0x6080000025d0
2017-09-21 17:27:49.138 iOSLockDemo[40148:2754251] originArr subArr address: 0x608000026ce0
2017-09-21 17:27:49.139 iOSLockDemo[40148:2754251] originArr subArr item 0 address: 0x608000072980
2017-09-21 17:27:49.139 iOSLockDemo[40148:2754251] originArr subArr item 1 address: 0x6080000729c0
2017-09-21 17:27:49.139 iOSLockDemo[40148:2754251] copyArr address: 0x600000002550
2017-09-21 17:27:49.140 iOSLockDemo[40148:2754251] copyArr subArr address: 0x600000027780
2017-09-21 17:27:49.140 iOSLockDemo[40148:2754251] copyArr subArr item 0 address: 0x600000077600
2017-09-21 17:27:49.140 iOSLockDemo[40148:2754251] copyArr subArr item 1 address: 0x600000077740
2017-09-21 17:27:49.140 iOSLockDemo[40148:2754251] mutCopyArr address: 0x600000002570
2017-09-21 17:27:49.141 iOSLockDemo[40148:2754251] mutCopyArr subArr address: 0x600000027620
2017-09-21 17:27:49.141 iOSLockDemo[40148:2754251] mutCopyArr subArr item 0 address: 0x600000077540
2017-09-21 17:27:49.141 iOSLockDemo[40148:2754251] mutCopyArr subArr item 1 address: 0x600000077680

通过log,可以发现,这次是完全拷贝

结语

对其他集合类型NSSet,NSDictionary也具有以上特点。有兴趣的同学可以试试。

参考

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html#//apple_ref/doc/uid/TP40010162-SW3

https://www.zybuluo.com/MicroCai/note/50592

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容