Block 循環(huán)引用茫打,__weak居触、__block、__strong使用說明

循環(huán)引用的理解

首先說一下循環(huán)引用老赤,為什么沒用 __weak 修飾就直接用 self. 屬性轮洋,有時(shí)候不會(huì)造成循環(huán)引用,有時(shí)候會(huì)造成循環(huán)引用呢抬旺。
循環(huán)引用是指兩個(gè)或者多個(gè)對象循環(huán)持有造成的無法釋放(即引用計(jì)數(shù)減不到0)弊予。
例如:類 Person 有個(gè)屬性 block, 在 block 實(shí)現(xiàn)后, 此時(shí) self 持有 block开财,如果在 block 中汉柒,直接使用 selfblock 將持有 self床未,造成循環(huán)引用, 如果 block 本身不是 self 的屬性竭翠,則 self 不持有 block,即使在 block 中直接使用 self 也不會(huì)造成循環(huán)引用薇搁,但是為了避免多個(gè)對象的循環(huán)引用斋扰,所以 block 中最好還是用 __weak,防止這種情況出現(xiàn)啃洋。代理用 weak 與此同理传货。

coverImage.jpg

__weak、__block宏娄、__strong的作用

  • __weak:弱引用變量修飾詞问裕,引用計(jì)數(shù)不會(huì) +1。本身可以避免循環(huán)引用的問題的孵坚,但是其會(huì)導(dǎo)致外部對象釋放了之后粮宛,Block 內(nèi)部也訪問不到這個(gè)對象的問題窥淆,我們可以通過在 Block 內(nèi)部聲明一個(gè) __strong 的變量來指向 weakObj,使外部對象既能在 Block 內(nèi)部保持住巍杈,又能避免循環(huán)引用的問題忧饭。
  • __block:Block內(nèi)部修改外部變量修飾詞,使外部變量可以在 Block 內(nèi)部進(jìn)行修改筷畦。本身無法避免循環(huán)引用的問題词裤,但是我們可以通過在 Block 內(nèi)部手動(dòng)把 blockObj 賦值為 nil 的方式來避免循環(huán)引用的問題。另外一點(diǎn)就是 __block 修飾的變量在 Block 內(nèi)外都是唯一的鳖宾,要注意這個(gè)特性可能帶來的隱患吼砂。
    但是 __block 有一點(diǎn):這只是限制在ARC環(huán)境下。在非ARC下鼎文,__block 是可以避免引用循環(huán)的渔肩。
  • __strong:強(qiáng)引用變量修飾詞,引用計(jì)數(shù)會(huì)+1拇惋。常用于 Block 內(nèi)部對 blockObj的引用修飾赖瞒,如上面??__weak的說明。

代碼示例

示例1(__weak的使用)

- (void)methond_1
{
    NSString *string = @"1";
    __weak NSString *weakStr = string;
    void (^ block)() = ^ {
        // 此處 weakStr 不能被修改蚤假,會(huì)報(bào)紅
        //weakStr = @"2";
    };
    
    block();
    NSLog(@"string   = %@   pointer = %p   pointer_content = %p", string, &string, string);
    NSLog(@"weakStr  = %@   pointer = %p   pointer_content = %p", weakStr, &weakStr, weakStr);
    // &string 得到的是變量 string 本身的存儲(chǔ)地址,而 number 得到的是存儲(chǔ)的內(nèi)容 @"1" 的地址吧兔。
    // log:
    // string   = 1   pointer = 0x7fff587269f8   pointer_content = 0x1075d6ee0
    // weakStr  = 1   pointer = 0x7fff587269f0   pointer_content = 0x1075d6ee0
}

示例2(__block的使用)

- (void)methond_2
{
    // __block:使外部變量可以在 Block 內(nèi)部進(jìn)行修改.
    NSNumber *number = @1;
    __block NSNumber *blockNum = number;
    void (^ block)() = ^ {
        blockNum = @2;
    };
    
    block();
    NSLog(@"number   = %@   pointer = %p   pointer_content = %p", number, &number, number);
    NSLog(@"blockNum = %@   pointer = %p   pointer_content = %p", blockNum, &blockNum, blockNum);
    // log:
    // number   = 1   pointer = 0x7fff5e35dad0   pointer_content = 0xb000000000000012
    // blockNum = 2   pointer = 0x618000051008   pointer_content = 0xb000000000000022
    
    // 可見 Block 會(huì)拷貝原來對象, __block 修飾的對象可被 Block 內(nèi)外同時(shí)修改.
}

示例3(在堆區(qū)的變量與在棧區(qū)的變量對比)

- (void)methond_3
{
    // model 變量是在堆區(qū)
    BaseModel *model = [[BaseModel alloc] init];
    __weak BaseModel *weakModel = model;
    __weak __block TestVC *blockSelf = self;
    self.blockModel = ^ {
        // 如果 blockSelf 不用 __block 修飾磷仰,則在此處不能修改 testString 值,如果不用 __weak 修飾境蔼,則會(huì)引起循環(huán)引
        blockSelf.testString = @"此時(shí) model = nil灶平,model 已被釋放,所以 weakModel = nil";
    };
    
    model = nil;
    self.blockModel();
    NSLog(@"model       = %@   pointer = %p   pointer_content = %p", model, &model, model);
    NSLog(@"weakMoedl   = %@   pointer = %p   pointer_content = %p", weakModel, &weakModel, weakModel);
    // log:
    // model       = (null)   pointer = 0x7fff595a9ad0   pointer_content = 0x0
    // weakMoedl   = (null)   pointer = 0x7fff595a9ac8   pointer_content = 0x0
    
    // number 變量是在棧區(qū)箍土, 值@1是在常量區(qū)
    NSNumber *number = @1;
    __weak NSNumber *blockNum = number;
    
    number = nil;
    NSLog(@"number   = %@   pointer = %p   pointer_content = %p", number, &number, number);
    NSLog(@"blockNum = %@   pointer = %p   pointer_content = %p", blockNum, &blockNum, blockNum);
    // log:
    // number   = (null)   pointer = 0x7fff5a31cad0   pointer_content = 0x0
    // blockNum = 1        pointer = 0x7fff5a31cac8   pointer_content = 0xb000000000000012
    
    // string 變量是在棧區(qū)逢享,值@"string"是在常量區(qū)
    NSString *string = @"string";
    __weak NSString *weakString = string;

    string = nil;
    NSLog(@"string     = %@   pointer = %p   pointer_content = %p", string, &string, string);
    NSLog(@"weakString = %@   pointer = %p   pointer_content = %p", weakString, &weakString, weakString);
    // log:
    // string     = (null)   pointer = 0x7fff5f627ad0   pointer_content = 0x0
    // weakString = string   pointer = 0x7fff5f627ac8   pointer_content = 0x1006d4e00
    
    // 字符串常量是存在常量區(qū)的,棧內(nèi)存并不會(huì)動(dòng)態(tài)釋放吴藻,而是當(dāng)當(dāng)前線程執(zhí)行完畢后瞒爬,釋放當(dāng)前線程的棧內(nèi)存。所有的常量都存在常量區(qū)沟堡,
    // 所以上面的例子中即使使用__ weak 修飾, 但是 @1 和 @"string" 這2個(gè)常量并沒有被釋放, 所以 weak 的地址指向依然存在值.
}

示例4(__weak與__block作用的對比)

- (void)methond_4
{
    BaseModel *model = [[BaseModel alloc] init];
    __weak BaseModel *weakModel = model;
    void (^ block)() = ^ {
        // weakModel 弱引用侧但, 此時(shí) model = nil ,所以 strongModel = weakModel = nil
        __strong BaseModel *strongModel = weakModel;
        NSLog(@"strongModel  = %@   pointer = %p   pointer_content = %p", strongModel, &strongModel, strongModel);
    };
    
    model = nil;
    block();
    NSLog(@"model        = %@   pointer = %p   pointer_content = %p", model, &model, model);
    NSLog(@"weakMoedl    = %@   pointer = %p   pointer_content = %p", weakModel, &weakModel, weakModel);
    // 在 model 置為 nil 之前, block 的 __ strong 并沒有執(zhí)行, 所以當(dāng)時(shí) model 對象被當(dāng)前的區(qū)塊持有, 當(dāng) model 置為 nil 時(shí), 該對象已經(jīng)被釋放, 所以 __strong 的時(shí)候, weakModel 地址的內(nèi)存已經(jīng)被釋放, strongModel 指向 nil, 所以 model 對象引用計(jì)數(shù)并沒有加 1.
    // log:
    // strongModel  = (null)   pointer = 0x7fff5e6669a8   pointer_content = 0x0
    // model        = (null)   pointer = 0x7fff5e666ad0   pointer_content = 0x0
    // weakMoedl    = (null)   pointer = 0x7fff5e666ac8   pointer_content = 0x0
    
    BaseModel *model_2 = [[BaseModel alloc] init];
    __block BaseModel *blockModel = model_2;
    void (^ blockModel_2)() = ^ {
        // weakModel 只是被 __block 修飾航罗,并不是弱引用禀横,所以 model = nil 并不影響 weakModel 的值,所以 strongModel = weakModel != nil
        __strong BaseModel *strongModel = blockModel;
        NSLog(@"strongModel  = %@   pointer = %p   pointer_content = %p", strongModel, &strongModel, strongModel);
    };
    
    model = nil;
    blockModel_2();
    NSLog(@"model        = %@   pointer = %p   pointer_content = %p", model_2, &model_2, model_2);
    NSLog(@"blockModel   = %@   pointer = %p   pointer_content = %p", blockModel, &blockModel, blockModel);
    // log:
    // strongModel  = <BaseModel: 0x60800001b590>   pointer = 0x7fff558af9e8   pointer_content = 0x60800001b590
    // model        = (null)                        pointer = 0x7fff558afad0   pointer_content = 0x0
    // weakMoedl    = <BaseModel: 0x60800001b590>   pointer = 0x7fff558afac8   pointer_content = 0x60800001b590
}

示例5(__weak和__strong的使用)

- (void)methond_5
{
    BaseModel *model = [[BaseModel alloc] init];
    __weak BaseModel *weakModel = model;
    void (^ block)() = ^ {
        __strong BaseModel *strongModel = weakModel;
        NSLog(@"雖然此時(shí) model = nil, weakModel 也被 __weak 修飾粥血,但是在下面??線程中 weakModel 被 threadStrong 強(qiáng)引用,weakModel 的引用計(jì)數(shù) +1 糠亩,當(dāng) model = nil 時(shí)森渐,weakModel 也不會(huì)被釋放,所以此時(shí) strongModel = weakModel != nil");
        NSLog(@"strongModel  = %@   pointer = %p   pointer_content = %p", strongModel, &strongModel, strongModel);
    };
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        __strong BaseModel *threadStrong = weakModel;
        NSLog(@"weakModel 在線程中被強(qiáng)引用缭嫡,引用計(jì)數(shù)+1");
        sleep(5);
        NSLog(@"threadStrong = %@   pointer = %p   pointer_content = %p", threadStrong, &threadStrong, threadStrong);
    });
    
    sleep(1);
    // 此時(shí) weakModel 在線程中已被強(qiáng)引用,引用計(jì)數(shù) +1茫舶,model = nil 并不能使得 weakModel 也等于 nil
    model = nil;
    
    block();
    NSLog(@"model        = %@   pointer = %p   pointer_content = %p", model, &model, model);
    NSLog(@"weakMoedl    = %@   pointer = %p   pointer_content = %p", weakModel, &weakModel, weakModel);
    // weakModel 在線程中被強(qiáng)引用械巡,引用計(jì)數(shù)+1
    // 雖然此時(shí) model = nil, weakModel 也被 __weak 修飾,但是在下面??線程中 weakModel 被 threadStrong 強(qiáng)引用饶氏,weakModel 的引用計(jì)數(shù) +1 讥耗,當(dāng) model = nil 時(shí),weakModel 也不會(huì)被釋放疹启,所以此時(shí) strongModel = weakModel != nil
    // log:
    // strongModel  = <BaseModel: 0x60000000ad90>   pointer = 0x7fff50507958   pointer_content = 0x60000000ad90
    // model        = (null)                        pointer = 0x7fff50507ad0   pointer_content = 0x0
    // weakMoedl    = <BaseModel: 0x60000000ad90>   pointer = 0x7fff50507ac8   pointer_content = 0x60000000ad90
    // threadStrong = <BaseModel: 0x60000000ad90>   pointer = 0x7000041c4d88   pointer_content = 0x60000000ad90
}

示例6(__weak和__strong的使用 -> block 內(nèi)修改全局變量)

@interface TestVC ()
{
    NSString *testVar;
}
@property (nonatomic, strong) NSString *testString;
@property (nonatomic, copy) void (^ blockModel)();

@end

@implementation TestVC

- (void)methond_6
{
    // 正確使用
    __weak __block TestVC *weakSelf = self;
    self.blockModel = ^{
        __strong TestVC *strongSelf = weakSelf;
        strongSelf.testString = @"testString";
        strongSelf -> testVar = @"可以修改全局變量古程,并且不會(huì)導(dǎo)致 block 無法釋放";
        //當(dāng)然也可以直接把這個(gè)全局變量改為屬性聲明,直接用 weakSelf. 或 strongSelf. 就行
    };
    //blockModel 被 self 持有喊崖,所以在 block 內(nèi)部必須使用 __weak 修飾的 weakSelf挣磨,又因?yàn)橐薷娜肿兞?testVar 使用 "->", 所以又使用 __strong 修飾的 strongSelf
    
    // 編譯不通過
    self.blockModel = ^{
        weakSelf.testString = @"testString";
        //被 __weak 修飾過的 weakSelf 不能使用 "->"
        //weakSelf -> testVar = @"這樣寫編譯不通過荤懂,直接報(bào)紅";
    };
    //報(bào)紅:"dereferencing a __weak pointer is not allowed die to possible null value caused by a race condition, assign it to strong variable first"
    
    // 無法修改全局變量 testVar(原理同示例3 ??)
    __weak __block NSString *weakVar = testVar;
    self.blockModel = ^{
        weakSelf.testString = @"testString";
        weakVar = @"無法修改全局變量 testVar";
    };
    
    self.blockModel = ^{
        weakSelf.testString = @"testString";
        self -> testVar = @"可以修改全局變量 testVar茁裙,但會(huì)引起 block 無法被釋放,導(dǎo)致內(nèi)存泄漏";
    };
}

@end

總結(jié)

  1. 當(dāng)在 block 內(nèi)部修改外部局部變量時(shí)节仿,需要用 __block 修飾晤锥;
    e.g.:
    NSNumber *number = @1;
    __block NSNumber *blockNum = number;
    void (^ block)() = ^ {
        blockNum = @2;
    };
  1. 當(dāng) block 被 self 持有,并且不對 self 做修改廊宪,如 self = nil;(對 self 的屬性修改不算是對 self 的修改)矾瘾,或者是不對 self 的全局變量做修改(因?yàn)闀?huì)用到 "->"),只需要用 __weak 修飾即可箭启;
    e.g.:
    __weak TestVC *weakSelf = self;
    self.blockModel = ^{
        weakSelf.testString = @"testString";
        [weakSelf testMethod];
    };
  1. 當(dāng) block 被 self 持有壕翩,并且對 self 做修改,如 self = nil;傅寡,則需要用 __weak__block 修飾放妈;
    e.g.:
    __weak __block TestVC *weakSelf = self;
    self.blockModel = ^{
        weakSelf.testString = @"testString";
        [weakSelf testMethod];
        weakSelf = nil;
    };
  1. 當(dāng) block 和 self 相互持有時(shí),或者 block 內(nèi)需要修改 self 的全局變量時(shí)赏僧,則 block 外部需要用 __weak 修飾大猛,block 內(nèi)部需要使用 __strong 修飾的變量(為了安全起見,block 內(nèi)部最好還是使用 __strong 修飾的變量吧淀零,不明白的請看上面??示例5(__weak和__strong的使用))挽绩;
    e.g.:
    __weak TestVC *weakSelf = self;
    self.blockModel = ^{
        __strong TestVC *strongSelf = weakSelf;
        strongSelf.testString = @"testString";
        strongSelf -> testVar = @"可以修改全局變量,并且不會(huì)導(dǎo)致 block 無法釋放";
    };
  1. 當(dāng) block 和 self 相互持有時(shí)驾中,并且有修改 self 唉堪,則外部需要用 __weak__block 修飾模聋,block 內(nèi)部需要使用 __strong 修飾的變量;
    e.g.:
    __weak __block  TestVC *weakSelf = self;
    self.blockModel = ^{
        __strong TestVC *strongSelf = weakSelf;
        strongSelf.testString = @"testString";
        strongSelf -> testVar = @"可以修改全局變量唠亚,并且不會(huì)導(dǎo)致 block 無法釋放";
        strongSelf = nil;
    };

以上5種情況基本說明了各個(gè)修飾詞的使用場景链方,如果把握不來的,或者不理解的灶搜,為了安全起見直接按地種情況去寫祟蚀,老鐵,沒毛病割卖。反正記住 以下幾點(diǎn):

  • __weak 是防止循環(huán)引用的前酿;
  • __block 是在 block 內(nèi)部可以修改外部變量的 (在非ARC環(huán)境下也可以防止循環(huán)引用);
  • __strong 是在內(nèi)部防止外部的 weak 變量被提前釋放鹏溯,在內(nèi)部無法獲取 weak 變量罢维;

以上示例代碼基本可以說明__weak__block丙挽、__strong的使用規(guī)則了肺孵,如果還有哪些不清楚的,沒有在示例代碼中展現(xiàn)出來颜阐,建議自己動(dòng)手寫寫看平窘。如有對內(nèi)存分配不太理解的小伙伴可以看看這篇文章《iOS程序中的內(nèi)存分配(棧區(qū)和堆區(qū)的對比)》

補(bǔ)充(weak的實(shí)現(xiàn)原理)

weak 變量在引用計(jì)數(shù)為0時(shí)凳怨,會(huì)被自動(dòng)設(shè)置成 nil初婆,這個(gè)特性是如何實(shí)現(xiàn)的?

很少有人知道weak表其實(shí)是一個(gè)hash(哈希)表猿棉,Key是所指對象的地址,Value是 weak 指針的地址數(shù)組屑咳。更多人的人只是知道 weak 是弱引用萨赁,所引用對象的計(jì)數(shù)器不會(huì)加一,并在引用對象被釋放的時(shí)候自動(dòng)被設(shè)置為nil兆龙。通常用于解決循環(huán)引用問題杖爽。但現(xiàn)在單知道這些已經(jīng)不足以應(yīng)對面試了,好多公司會(huì)問 weak 的原理紫皇。weak 的原理是什么呢慰安?具體細(xì)節(jié)分析請看《iOS 底層解析weak的實(shí)現(xiàn)原理(包含weak對象的初始化,引用聪铺,釋放的分析)》化焕。

weak 實(shí)現(xiàn)原理的概括
Runtime 維護(hù)了一個(gè) weak 表,用于存儲(chǔ)指向某個(gè)對象的所有 weak 指針铃剔。weak表其實(shí)是一個(gè)hash(哈希)表撒桨,Key是所指對象的地址查刻,Value是weak 指針的地址(這個(gè)地址的值是所指對象的地址)數(shù)組。

weak 的實(shí)現(xiàn)原理可以概括一下三步:
1凤类、初始化時(shí):runtime 會(huì)調(diào)用 objc_initWeak 函數(shù)穗泵,初始化一個(gè)新的 weak 指針指向?qū)ο蟮牡刂贰?br> 2、添加引用時(shí):objc_initWeak 函數(shù)會(huì)調(diào)用 objc_storeWeak() 函數(shù)谜疤, objc_storeWeak() 的作用是更新指針指向佃延,創(chuàng)建對應(yīng)的弱引用表。
3夷磕、釋放時(shí)履肃,調(diào)用 clearDeallocating 函數(shù)。clearDeallocating 函數(shù)首先根據(jù)對象地址獲取所有 weak 指針地址的數(shù)組企锌,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為 nil榆浓,最后把這個(gè) entryweak 表中刪除,最后清理對象的記錄撕攒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陡鹃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抖坪,更是在濱河造成了極大的恐慌萍鲸,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擦俐,死亡現(xiàn)場離奇詭異脊阴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蚯瞧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門嘿期,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人埋合,你說我怎么就攤上這事备徐。” “怎么了甚颂?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵蜜猾,是天一觀的道長。 經(jīng)常有香客問我振诬,道長蹭睡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任赶么,我火速辦了婚禮肩豁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己蓖救,他們只是感情好洪规,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著循捺,像睡著了一般斩例。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上从橘,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天念赶,我揣著相機(jī)與錄音,去河邊找鬼恰力。 笑死叉谜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的踩萎。 我是一名探鬼主播停局,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼香府!你這毒婦竟也來了董栽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤企孩,失蹤者是張志新(化名)和其女友劉穎锭碳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勿璃,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡擒抛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了补疑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歧沪。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莲组,靈堂內(nèi)的尸體忽然破棺而出槽畔,到底是詐尸還是另有隱情,我是刑警寧澤胁编,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站鳞尔,受9級(jí)特大地震影響嬉橙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寥假,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一市框、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧糕韧,春花似錦枫振、人聲如沸喻圃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斧拍。三九已至,卻和暖如春杖小,著一層夾襖步出監(jiān)牢的瞬間肆汹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工予权, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昂勉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓扫腺,卻偏偏與公主長得像岗照,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子笆环,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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