iOS Masonry 源碼閱讀

<code>Masonry</code>源碼閱讀

<strong>閱讀源碼是一種美妙的體驗(yàn)</strong>

這么強(qiáng)大的布局庫(kù)哪替,就不做解釋了红氯。因?yàn)橄到y(tǒng)的自動(dòng)布局寫(xiě)起來(lái)很麻煩泣懊,所以 Masonry 成了當(dāng)前流行的使用代碼布局的方式(當(dāng)然是在OC中)

具體使用如下:

[self.headView addSubview:self.headLabel];
[self.headLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.mas_equalTo(-2);
    make.left.mas_equalTo(13);
    make.height.mas_equalTo(15);
}];
    

<code>Masonry</code> 也是支持鏈?zhǔn)秸{(diào)用的。

view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

不做解釋了煤墙。

<strong>閱讀源碼是重點(diǎn)</strong>

<code>Masonry</code> 在 手機(jī) 開(kāi)發(fā) 和 Mac 開(kāi)發(fā)都能用缤底,所以宏定義了

#if TARGET_OS_IPHONE || TARGET_OS_TV

    #import <UIKit/UIKit.h>
    #define MAS_VIEW UIView
    #define MAS_VIEW_CONTROLLER UIViewController
    #define MASEdgeInsets UIEdgeInsets

    typedef UILayoutPriority MASLayoutPriority;
    static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
    static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
    static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
    static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
    static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

#elif TARGET_OS_MAC

    #import <AppKit/AppKit.h>
    #define MAS_VIEW NSView
    #define MASEdgeInsets NSEdgeInsets

    typedef NSLayoutPriority MASLayoutPriority;
    static const MASLayoutPriority MASLayoutPriorityRequired = NSLayoutPriorityRequired;
    static const MASLayoutPriority MASLayoutPriorityDefaultHigh = NSLayoutPriorityDefaultHigh;
    static const MASLayoutPriority MASLayoutPriorityDragThatCanResizeWindow = NSLayoutPriorityDragThatCanResizeWindow;
    static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 501;
    static const MASLayoutPriority MASLayoutPriorityWindowSizeStayPut = NSLayoutPriorityWindowSizeStayPut;
    static const MASLayoutPriority MASLayoutPriorityDragThatCannotResizeWindow = NSLayoutPriorityDragThatCannotResizeWindow;
    static const MASLayoutPriority MASLayoutPriorityDefaultLow = NSLayoutPriorityDefaultLow;
    static const MASLayoutPriority MASLayoutPriorityFittingSizeCompression = NSLayoutPriorityFittingSizeCompression;

#endif

根據(jù)平臺(tái)的不同,重新定義了 MAS_VIEW 和 MASEdgeInsets

在布局之前番捂,內(nèi)部代碼會(huì)自動(dòng)加上<code>self.translatesAutoresizingMaskIntoConstraints = NO;</code>个唧,所以不用我們外部加。

跟一個(gè)布局流程设预,看看內(nèi)部怎樣做的徙歼。

內(nèi)部定義了一個(gè) <code> MAS_VIEW </code> 的 分類(lèi),這樣子我們?cè)谕獠浚灰?lt;code>UIView</code>或者 <code>UIView</code>的子類(lèi)魄梯,然后引入頭文件桨螺,就可以調(diào)用方法了。

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
/// 設(shè)置自動(dòng)布局酿秸。
    self.translatesAutoresizingMaskIntoConstraints = NO;
    /// 生成 MASConstraintMaker
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    /// 執(zhí)行外部的block
    block(constraintMaker);
    /// 安裝布局信息
    return [constraintMaker install];
}

<code> mas_makeConstraints </code> 添加布局信息

  • 生成<code> MASConstraintMaker</code>
  • <code>block(constraintMaker)</code>把上一步生成的對(duì)象作為參數(shù)灭翔。然后調(diào)用外面的<code>block</code>,重點(diǎn)是設(shè)置 上一步生成對(duì)象的屬性
  • 讓布局信息生效

<strong>接下來(lái),我們的注意力只要轉(zhuǎn)移到<code> MASConstraintMaker</code> 就行了</strong>

生成 <code>MASConstraintMaker</code>對(duì)象的過(guò)程比較簡(jiǎn)單

- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    self.view = view;
    self.constraints = NSMutableArray.new;
    
    return self;
}

  • 用 view 初始化一個(gè)對(duì)象 這里 view 的引用是 <code>weak</code>的辣苏。
  • 創(chuàng)建一個(gè) <code> self.constraints </code>數(shù)組肝箱,字面意思理解,是存放所有約束的稀蟋。我們大膽猜測(cè)煌张,跟鏈?zhǔn)秸{(diào)用,是有關(guān)系的退客。

<strong><code>install</code></strong>的過(guò)程也簡(jiǎn)單骏融,

- (NSArray *)install {
    //判斷要不要?jiǎng)h除本身存在的約束
    if (self.removeExisting) {
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        for (MASConstraint *constraint in installedConstraints) {
        //卸載約束
            [constraint uninstall];
        }
    }
    NSArray *constraints = self.constraints.copy;
    for (MASConstraint *constraint in constraints) {
        constraint.updateExisting = self.updateExisting;
        /// 安裝約束
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}

  • 判斷要不要?jiǎng)h除之前已經(jīng)存在的約束 <code>removeExisting</code>,如果要?jiǎng)h除,則調(diào)用 <code> MASConstraint 的 uninstall </code>方法卸載掉約束 <code> removeExisting </code>只有在 <code>mas_remakeConstraints</code>的時(shí)候才為 <code>YES</code>
  • 遍歷 <code>self.constraints</code> 然后調(diào)用 <code> MASConstraint 的 install </code>方法萌狂,讓約束生效
  • 清空 <code> self.constraints </code>數(shù)組档玻,因?yàn)楸4娴募s束都已經(jīng)生效了。

<strong>接下來(lái)只要分析<code> MASConstraint </code> 思路就清楚了</strong>

<code>MASConstraint</code>是MAS庫(kù)茫藏,封裝的误趴,關(guān)于約束的一個(gè)類(lèi),外面能夠進(jìn)行鏈?zhǔn)秸{(diào)用的設(shè)置約束刷允,也是得益于<code>MASConstraint</code>。定義了很多常用的約束操作碧囊。比如:

- (MASConstraint *)left;
- (MASConstraint *)top;
- (MASConstraint *)right;
- (MASConstraint *)bottom;
- (MASConstraint *)leading;
- (MASConstraint *)trailing;
- (MASConstraint *)width;
- (MASConstraint *)height;
- (MASConstraint *)centerX;
- (MASConstraint *)centerY;
- (MASConstraint *)baseline;

~~~~~ 還有N多個(gè)操作树灶。

上面的函數(shù)都返回自身對(duì)象,所以才能夠進(jìn)行鏈?zhǔn)秸{(diào)用糯而。

我們上面的例子中

block 執(zhí)行了

make.bottom.mas_equalTo(-2);
make.left.mas_equalTo(13);
make.height.mas_equalTo(15);

因?yàn)?lt;code> bottom left height </code>屬性都是lazy load 的天通,所以只要調(diào)用了,就會(huì)添加一個(gè)約束給 view

- (MASConstraint *)bottom {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom];
}

最終會(huì)調(diào)用 ps(我們標(biāo)記一下這個(gè)函數(shù) 為 <strong>S</strong>)

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    /// 生成 MASViewAttribute MASViewAttribute 是用來(lái)描述一個(gè)view 和 約束的 關(guān)系
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    /// 生成 MASViewConstraint MASViewConstraint 是 MAS描述約束的熄驼。 
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    /// 我們先記下S這里的作用像寒,以后看看作用,
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    if (!constraint) {
        newConstraint.delegate = self;
        /// 添加到約束數(shù)組中
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

到這一步瓜贾,可以看到<code>self.constraints</code>中存的是<code>MASViewConstraint</code> 對(duì)象诺祸,并且把代理指向了<code> maker </code>

設(shè)置約束的值,一般使用已經(jīng)定義好的宏

#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))

#define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))


#ifdef MAS_SHORTHAND_GLOBALS

#define equalTo(...)                     mas_equalTo(__VA_ARGS__)
#define greaterThanOrEqualTo(...)        mas_greaterThanOrEqualTo(__VA_ARGS__)
#define lessThanOrEqualTo(...)           mas_lessThanOrEqualTo(__VA_ARGS__)

#define offset(...)                      mas_offset(__VA_ARGS__)

#endif

如果不想使用 <code>mas</code>開(kāi)頭的宏祭芦,可以在全局定義 <code> MAS_SHORTHAND_GLOBALS </code> 比如:

#define MAS_SHORTHAND_GLOBALS

就可以了

所以筷笨,類(lèi)似

栗子 <strong>A</strong>

UIButton *button = [UIButton new];
    [self.view addSubview:button];
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(10);
        make.top.mas_equalTo(10);
        make.size.mas_equalTo(CGSizeMake(20, 20));
    }];

中設(shè)置 <code>left.mas_offset(10);</code>左邊距為10 最后就是調(diào)用,<code>MASConstraint</code>的

- (MASConstraint * (^)(NSValue *value))valueOffset {
    return ^id(NSValue *offset) {
        NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
        [self setLayoutConstantWithValue:offset];
        return self;
    };
}

函數(shù),函數(shù)同樣是返回一個(gè) <code>block</code>,并且 <code>block</code>中返回<code>self</code>方便鏈?zhǔn)秸{(diào)用.

- (void)setLayoutConstantWithValue:(NSValue *)value {
    if ([value isKindOfClass:NSNumber.class]) {
        self.offset = [(NSNumber *)value doubleValue];
    } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) {
        CGPoint point;
        [value getValue:&point];
        self.centerOffset = point;
    } else if (strcmp(value.objCType, @encode(CGSize)) == 0) {
        CGSize size;
        [value getValue:&size];
        self.sizeOffset = size;
    } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) {
        MASEdgeInsets insets;
        [value getValue:&insets];
        self.insets = insets;
    } else {
        NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value);
    }
}

<code> setLayoutConstantWithValue </code>函數(shù)中胃夏,會(huì)判斷設(shè)置的值得類(lèi)型轴或,然后進(jìn)行不同的設(shè)置,根據(jù)約束的不同 比如:<code>NSLayoutAttributeWidth NSLayoutAttributeLeft</code> 等等仰禀。照雁。不做多余解釋?zhuān)?/p>

跟 <strong>栗子 A</strong> 有同樣作用的鏈?zhǔn)秸{(diào)用,可以這樣子寫(xiě)答恶。

<strong> 栗子 B</strong>

[button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(10).top.mas_equalTo(10);
        make.size.mas_equalTo(CGSizeMake(20, 20));
}];
    

不同之處在于

make.left.mas_offset(10).top.mas_equalTo(10);

第一個(gè) <code>.left</code> 是 <code> MASConstraintMaker </code>中的屬性饺蚊,調(diào)用完成后會(huì)返回 自己。 第二個(gè) <code>top</code> 是 <code>MASViewConstraint</code> 自己的亥宿。下面分析一下卸勺,這個(gè)怎么玩的。

你看烫扼,調(diào)用 .top 的時(shí)候曙求,<code>MASViewConstraint</code> 沒(méi)有做什么事情,直接調(diào)用 <code>delegate</code> 的 <code>[constraint:addConstraintWithLayoutAttribute:];</code>方法

#pragma mark - attribute chaining

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");

    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}

峰回路轉(zhuǎn)映企,調(diào)用到我們剛才標(biāo)記的 <strong>S</strong> 那里了悟狱,這回,第一個(gè)參數(shù)有值了堰氓。我們可以揭開(kāi)神秘的面紗了挤渐。

if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
  • 首先生成<code> MASCompositeConstraint </code> 對(duì)象。
  • 然后把 對(duì)象的代理設(shè)置為 <code>self</code>
  • 然后替換 傳進(jìn)來(lái)的第一個(gè)參數(shù)在<code> constraints</code> 中的值双絮。
  • 最后返回 對(duì)象浴麻,因?yàn)?lt;code> MASCompositeConstraint</code> 繼承自 <code> MASConstraint </code> 所以后面的鏈?zhǔn)秸{(diào)用也是可以繼續(xù)的。

替換函數(shù)如下

- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
    NSUInteger index = [self.constraints indexOfObject:constraint];
    NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
    [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
}

之所以要替換囤攀,是因?yàn)?防止在最后 遍歷 <code> self.constraints </code>設(shè)置約束的時(shí)候重復(fù) 因?yàn)?<code>MASCompositeConstraint</code> 也是繼承自 <code>MASConstraint</code> 和 <code>MASViewConstraint</code> 一樣 實(shí)現(xiàn)了很多之后要用到的方法软免。比如 <code>install</code> 等等。

<code>MASConstraint</code> 定義了約束 操作的函數(shù)焚挠,但是對(duì)約束的操作都放在子類(lèi)中進(jìn)行

  • <code>MASViewConstraint</code>一個(gè)單獨(dú)的約束
  • <code>MASComposisteConstraint</code>一組約束

<code>MASConstraint</code>中需要子類(lèi)重寫(xiě)的方法膏萧,都拋出了異常

#define MASMethodNotImplemented() \
    @throw [NSException exceptionWithName:NSInternalInconsistencyException \
                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
                                 userInfo:nil]

<code>MASViewConstraint</code> 單獨(dú)的約束,重寫(xiě)了 <code>MASConstraint</code> 標(biāo)記的需要重寫(xiě)的方法蝌衔。在父類(lèi)鏈?zhǔn)秸{(diào)用的基礎(chǔ)上榛泛,增加了。

@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;
@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;

用來(lái)描述跟約束相關(guān)的信息噩斟。

<code> MASCompositeConstraint </code> 代表一組約束曹锨。內(nèi)部存放了一個(gè)私有變量

@property (nonatomic, strong) NSMutableArray *childConstraints;

用來(lái)存放單個(gè)的約束的信息,一般都是存放 <code>MASViewConstraint</code>.

mas_makeConstraints 
mas_updateConstraints 
mas_remakeConstraints

函數(shù)的區(qū)別:

<code>updateConstraints</code> 函數(shù)里里會(huì)設(shè)置<code>updateExisting</code>為 <code>YES</code>
<code>remakeConstraints</code> 函數(shù)里會(huì)設(shè)置<code>removeExisting = YES;</code>

如果 <code>updateExisting</code>為<code> YES </code>的時(shí)候再一個(gè)約束<code>install</code>的時(shí)候會(huì)先尋找當(dāng)前<code>view</code> 有沒(méi)有相同的約束剃允,如果有艘希,就直接更新約束的 <code>constant</code>值硼身。代碼如下:

MASLayoutConstraint *existingConstraint = nil;
    if (self.updateExisting) {
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    if (existingConstraint) {
        // just update the constant
        existingConstraint.constant = layoutConstraint.constant;
        self.layoutConstraint = existingConstraint;
    }
    

如果 <code>removeExisting = YES;</code> 在 <code>install</code>之前,會(huì)刪除<code>view</code>的所有約束

if (self.removeExisting) {
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        for (MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }

<strong> Masonry </strong>這個(gè)經(jīng)常使用庫(kù)我們就摸到了冰山一角了覆享。以后有空一行一行的扣一下細(xì)節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佳遂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撒顿,更是在濱河造成了極大的恐慌丑罪,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤壁,死亡現(xiàn)場(chǎng)離奇詭異吩屹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)拧抖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)煤搜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人唧席,你說(shuō)我怎么就攤上這事擦盾。” “怎么了淌哟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵迹卢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我徒仓,道長(zhǎng)腐碱,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任掉弛,我火速辦了婚禮症见,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘殃饿。我一直安慰自己谋作,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布壁晒。 她就那樣靜靜地躺著瓷们,像睡著了一般业栅。 火紅的嫁衣襯著肌膚如雪秒咐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,785評(píng)論 1 290
  • 那天碘裕,我揣著相機(jī)與錄音携取,去河邊找鬼。 笑死帮孔,一個(gè)胖子當(dāng)著我的面吹牛雷滋,可吹牛的內(nèi)容都是我干的不撑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼晤斩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼焕檬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起澳泵,我...
    開(kāi)封第一講書(shū)人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤实愚,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后兔辅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體腊敲,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年维苔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碰辅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡介时,死狀恐怖没宾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潮尝,我是刑警寧澤榕吼,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站勉失,受9級(jí)特大地震影響羹蚣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乱凿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一顽素、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧徒蟆,春花似錦胁出、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至寺枉,卻和暖如春抑淫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姥闪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工始苇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筐喳。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓催式,卻偏偏與公主長(zhǎng)得像函喉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荣月,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

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