"2022" 新 iOS 開發(fā)進階+面試大全(一)

這篇文章可能有點長已烤,所以分了三篇文章記錄。內(nèi)容來自網(wǎng)上的面試題以及自己面試過程中遇到的問題總結(jié)妓羊,也會定期更新,不合理的地方歡迎指正稍计。

  • 更多技術(shù)題合集:

iOS技術(shù)題大全(上)

iOS技術(shù)題大全(中)

iOS技術(shù)題大全(下)

提升iOS開發(fā)技能學(xué)習(xí)網(wǎng)址:docs.qq.com/doc/DVWlQam9Qd3B1cEF2

為自己的面試躁绸,為自己的跳槽,加油吧 iOS開發(fā)

一臣嚣、分類和擴展

  • 分類和擴展有什么區(qū)別净刮?

category

1、分類給類添加方法
2硅则、不能通過正常模式給類添加屬性淹父,但是可以通過 runtime 添加
3、如果在分類中通過@property定義屬性怎虫,那么只會對屬性的 getter setter 方法進行聲明暑认,不會實現(xiàn)。同時也不會生成帶下劃線的成員變量
4大审、在運行時才會編譯代碼

extension

1蘸际、擴展可以看成是特殊的分類 匿名分類
2、可以給類添加屬性徒扶,私有
3粮彤、可以給類添加方法,也是私有
4姜骡、在編譯時期就會編譯导坟,與 .h 文件中的@interface和.m文件里的@implement一起形成了一個完整的類
5、擴展一般用來隱藏類的信息圈澈,所以使用擴展的前提是要有類的源碼惫周!所以針對系統(tǒng)自帶的類,是無法使用擴展的士败。

為什么分類可以添加方法闯两,而不能添加成員變量褥伴??漾狼?

因為在運行時重慢,類的內(nèi)部布局早已經(jīng)確定,如果添加實例變量逊躁,會破壞類的內(nèi)部布局似踱。

  • 分類的結(jié)構(gòu)體里面有哪些成員?

category 是一個指向類結(jié)構(gòu)體的指針稽煤,其結(jié)構(gòu)體的定義如下:

typedef struct objc_category *Category;

struct objc_category {
    char *category_name                          OBJC2_UNAVAILABLE; // 分類名
    char *class_name                             OBJC2_UNAVAILABLE; // 分類所屬的類名
    struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 實例方法列表
    struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 類方法列表
    struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分類所實現(xiàn)的協(xié)議列表
}

可以 與 objc_class 的結(jié)構(gòu)體進行對比:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父類
    const char *name                        OBJC2_UNAVAILABLE;  // 類名
    long version                            OBJC2_UNAVAILABLE;  // 類的版本信息核芽,默認為0
    long info                               OBJC2_UNAVAILABLE;  // 類信息,供運行期使用的一些位標(biāo)識
    long instance_size                      OBJC2_UNAVAILABLE;  // 該類的實例變量大小
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 該類的成員變量鏈表
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定義的鏈表
    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法緩存
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;

經(jīng)過對比酵熙,發(fā)現(xiàn)少了一個 struct objc_ivar_list *ivars 成員變量列表T颉!匾二!這也就說明了 分類是不能添加成員變量的哮独。

  • 分類加載和方法調(diào)用順序

    1、 加載:先加載原始類的 load() 方法 察藐,再去加載 分類中的 load() 方法皮璧,如果有多個分類,則按照編譯順序加載

    2分飞、調(diào)用:先調(diào)用分類中的方法悴务,再去調(diào)用原始類中的方法,如果要是重名譬猫,則會覆蓋原始類中的方法(因為在方法列表中讯檐,分類的方法會排在原始類中同名方法的前面)。

二删窒、atomic的實現(xiàn)機制裂垦;為什么不能保證絕對的線程安全(最好可以結(jié)合場景來說)?

  • atomic

atomic

1肌索、會對屬性的 setter/getter 方法進行加鎖蕉拢,這僅僅只能保證在 操作 setter/getter 方法是安全的。不能保證其他線程的安全
2诚亚、例如 : 線程1調(diào)用了某一屬性的setter方法并進行到了一半,線程2調(diào)用其getter方法,那么會執(zhí)行完setter操作后,在執(zhí)行g(shù)etter操作,線程2會獲取到線程1 setter后的完整的值.
3晕换、當(dāng)幾個線程同時調(diào)用同一屬性的setter、getter方法時,會get到一個完整的值,但get到的值不可控站宗。

例如 : 線程1 調(diào)用getter 闸准,線程2 調(diào)用setter,線程3 調(diào)用setter梢灭,這3個線程并行同時開始,線程1會get到一個值,但是這個值不可控,可能是線程2,線程3 set之前的原始值,可能是線程2 set的值,也可能是線程3 set的值

  • atomic是線程安全的嗎?

不是,很多文章談到atomic和nonatomic的區(qū)別時,都說atomic是線程安全,其實這個說法是不準(zhǔn)確的.atomic只是對屬性的getter/setter方法進行了加鎖操作,這種安全僅僅是set/get 的讀寫安全,并非真正意義上的線程安全,因為線程安全還有讀寫之外的其他操作

比如:如果當(dāng)一個線程正在get或set時,又有另一個線程同時在進行release操作,可能會直接crash

  • nonatomic

nonatomic

系統(tǒng)生成的getter/setter方法沒有加鎖線程不安全,但更快當(dāng)多個線程同時訪問同一個屬性,會出現(xiàn)無法預(yù)料的結(jié)果

  • atomic的seter getter內(nèi)部實現(xiàn)
- (void)setCurrentImage:(UIImage *)currentImage
{
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];

        }
    }
}

- (UIImage *)currentImage
{
    @synchronized(self) {
        return _currentImage;
    }
}

  • nonatomic的seter getter內(nèi)部實現(xiàn)
- (void)setCurrentImage:(UIImage *)currentImage
{
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];

    }
}
- (UIImage *)currentImage
{
    return _currentImage;
}

三夷家、被weak修飾的對象在被釋放的時候會發(fā)生什么蒸其?是如何實現(xiàn)的?知道sideTable么库快?里面的結(jié)構(gòu)可以畫出來么摸袁?

參考:http://www.reibang.com/p/b93d61418f17
這個問題在 數(shù)據(jù)結(jié)構(gòu)&&算法里面做了解答

四、關(guān)聯(lián)對象有什么應(yīng)用义屏,系統(tǒng)如何管理關(guān)聯(lián)對象靠汁?其被釋放的時候需要手動將其指針置空么?

我們在 iOS 開發(fā)中經(jīng)常需要使用分類(Category)闽铐,為已經(jīng)存在的類添加屬性的需求蝶怔,但是使用 @property 并不能在分類中正確創(chuàng)建實例變量和存取方法。這時候就會用到關(guān)聯(lián)對象兄墅。

分類中的 @property
@interface DKObject : NSObject

@property (nonatomic, strong) NSString *property;

@end

在使用上述代碼時會做三件事:

  • 生成帶下劃線的實例變量 _property
  • 生成 getter 方法 - property
  • 生成 setter 方法 - setProperty:
@implementation DKObject {
    NSString *_property;
}

- (NSString *)property {
    return _property;
}

- (void)setProperty:(NSString *)property {
    _property = property;
}

@end

這些代碼都是編譯器為我們生成的踢星,雖然你看不到它,但是它確實在這里隙咸,我們既然可以在類中使用 @property 生成一個屬性斩狱,那么為什么在分類中不可以呢?

我們來做一個小實驗:創(chuàng)建一個 DKObject 的分類 Category扎瓶,并添加一個屬性 categoryProperty

@interface DKObject (Category)

@property (nonatomic, strong) NSString *categoryProperty;

@end

看起來還是很不錯的,不過 Build 一下這個 Demo泌枪,會發(fā)現(xiàn)有這么一個警告:

image

在這里的警告告訴我們 categoryProperty 屬性的存取方法需要自己手動去實現(xiàn)概荷,或者使用 @dynamic 在運行時實現(xiàn)這些方法。

換句話說碌燕,分類中的 @property 并沒有為我們生成實例變量以及存取方法误证,而需要我們手動實現(xiàn)。

使用關(guān)聯(lián)對象

Q:我們?yōu)槭裁匆褂藐P(guān)聯(lián)對象修壕?

A:因為在分類中 @property 并不會自動生成實例變量以及存取方法愈捅,所以一般使用關(guān)聯(lián)對象為已經(jīng)存在的類添加『屬性』。

以下是與關(guān)聯(lián)對象有關(guān)的 API慈鸠,并在分類中實現(xiàn)一個偽屬性:

#import "DKObject+Category.h"
#import <objc/runtime.h>

@implementation DKObject (Category)

- (NSString *)categoryProperty {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setCategoryProperty:(NSString *)categoryProperty {
    objc_setAssociatedObject(self, @selector(categoryProperty), categoryProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

這里的 _cmd 代指當(dāng)前方法的選擇子蓝谨,也就是 @selector(categoryProperty)

我們使用了兩個方法 objc_getAssociatedObject 以及 objc_setAssociatedObject 來模擬『屬性』的存取方法青团,而使用關(guān)聯(lián)對象模擬實例變量譬巫。

在這里有必要解釋兩個問題:

  • 為什么向方法中傳入 @selector(categoryProperty)?
  • OBJC_ASSOCIATION_RETAIN_NONATOMIC 是干什么的督笆?

關(guān)于第一個問題芦昔,我們需要看一下這兩個方法的原型:

id objc_getAssociatedObject(id object, const void *key);
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

@selector(categoryProperty) 也就是參數(shù)中的key,其實可以使用靜態(tài)指針 static void *類型的參數(shù)來代替娃肿,不過在這里咕缎,筆者強烈推薦使用 @selector(categoryProperty) 作為 key 傳入珠十。因為這種方法省略了聲明參數(shù)的代碼,并且能很好地保證 key 的唯一性凭豪。

OBJC_ASSOCIATION_RETAIN_NONATOMIC 又是什么呢焙蹭?如果我們使用 Command 加左鍵查看它的定義:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

從這里的注釋我們能看到很多東西,也就是說不同的 objc_AssociationPolicy 對應(yīng)了不通的屬性修飾符:

objc_AssociationPolicy modifier
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic, strong
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic, copy
OBJC_ASSOCIATION_RETAIN atomic, strong
OBJC_ASSOCIATION_COPY atomic, copy

而我們在代碼中實現(xiàn)的屬性 categoryProperty 就相當(dāng)于使用了 nonatomic 和 strong 修飾符墅诡。

在obj dealloc時候會調(diào)用object_dispose壳嚎,檢查有無關(guān)聯(lián)對象,有的話_object_remove_assocations刪除

五末早、KVO的底層實現(xiàn)烟馅?如何取消系統(tǒng)默認的KVO并手動觸發(fā)(給KVO的觸發(fā)設(shè)定條件:改變的值符合某個條件時再觸發(fā)KVO)?

實現(xiàn)原理:

image
  • 當(dāng)某個類的對象第一次被觀察時然磷,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類郑趁,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。
  • 派生類在被重寫的 setter 方法中實現(xiàn)真正的通知機制姿搜,就如前面手動實現(xiàn)鍵值觀察那樣寡润。這么做是基于設(shè)置屬性會調(diào)用 setter 方法,而通過重寫就獲得了 KVO 需要的通知機制舅柜。當(dāng)然前提是要通過遵循 KVO 的屬性設(shè)置方式來變更屬性值梭纹,如果僅是直接修改屬性對應(yīng)的成員變量,是無法實現(xiàn) KVO 的致份。
  • 同時派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個類变抽。然后系統(tǒng)將這個對象的 isa 指針指向這個新誕生的派生類,因此這個對象就成為該派生類的對象了氮块,因而在該對象上對 setter 的調(diào)用就會調(diào)用重寫的 setter绍载,從而激活鍵值通知機制。此外滔蝉,派生類還重寫了 dealloc 方法來釋放資源击儡。

KVO與Notification之間的區(qū)別:

  • notification是需要一個發(fā)送notification的對象,一般是notificationCenter蝠引,來通知觀察者阳谍。

  • KVO是直接通知到觀察對象,并且邏輯非常清晰立肘,實現(xiàn)步驟簡單边坤。

六、Autoreleasepool所使用的數(shù)據(jù)結(jié)構(gòu)是什么谅年?AutoreleasePoolPage結(jié)構(gòu)體了解么茧痒?


每創(chuàng)建一個池子,會在首部創(chuàng)建一個 哨兵 對象,作為標(biāo)記

最外層池子的頂端會有一個next指針融蹂。當(dāng)鏈表容量滿了旺订,就會在鏈表的頂端弄企,并指向下一張表。

Autorelease對象什么時候釋放区拳?

這個問題拿來做面試題拘领,問過很多人,沒有幾個能答對的樱调。很多答案都是“當(dāng)前作用域大括號結(jié)束時釋放”约素,顯然木有正確理解Autorelease機制。

在沒有手加Autorelease Pool的情況下笆凌,Autorelease對象是在當(dāng)前的runloop迭代結(jié)束時釋放的圣猎,而它能夠釋放的原因是系統(tǒng)在每個runloop迭代中都加入了自動釋放池Push和Pop

例子:

__weak id reference = nil;
- (void)viewDidLoad {
    [super viewDidLoad];    NSString *str = [NSString stringWithFormat:@"sunnyxx"];    // str是一個autorelease對象,設(shè)置一個weak的引用來觀察它
    reference = str;
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];    
    NSLog(@"%@", reference); 
    // Console: sunnyxx
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];    
    NSLog(@"%@", reference); 
    // Console: (null)
}

當(dāng)然乞而,我們也可以手動干預(yù)Autorelease對象的釋放時機:

- (void)viewDidLoad
{
    [super viewDidLoad];
    @autoreleasepool {        NSString *str = [NSString stringWithFormat:@"sunnyxx"];
    }    NSLog(@"%@", str); 
// Console: (null)
}

Autorelease原理

AutoreleasePoolPage

ARC下送悔,我們使用@autoreleasepool{}來使用一個AutoreleasePool,隨后編譯器將其改寫成下面的樣子:

void *context = objc_autoreleasePoolPush();
// {}中的代碼objc_autoreleasePoolPop(context);

而這兩個函數(shù)都是對AutoreleasePoolPage的簡單封裝爪模,所以自動釋放機制的核心就在于這個類欠啤。

AutoreleasePoolPage是一個C++實現(xiàn)的類

image
  • AutoreleasePool并沒有單獨的結(jié)構(gòu),而是由若干個AutoreleasePoolPage以雙向鏈表的形式組合而成(分別對應(yīng)結(jié)構(gòu)中的parent指針和child指針)屋灌。
  • AutoreleasePool是按線程一一對應(yīng)的(結(jié)構(gòu)中的thread指針指向當(dāng)前線程)洁段。
  • AutoreleasePoolPage每個對象會開辟4096字節(jié)內(nèi)存(也就是虛擬內(nèi)存一頁的大小)共郭,除了上面的實例變量所占空間眉撵,剩下的空間全部用來儲存autorelease對象的地址
  • 上面的id *next指針作為游標(biāo)指向棧頂最新add進來的autorelease對象的下一個位置落塑。
  • 一個AutoreleasePoolPage的空間被占滿時,會新建一個AutoreleasePoolPage對象罐韩,連接鏈表憾赁,后來的autorelease對象在新的page加入。

所以散吵,若當(dāng)前線程中只有一個AutoreleasePoolPage對象龙考,并記錄了很多autorelease對象地址時內(nèi)存如下圖:

image

圖中的情況,這一頁再加入一個autorelease對象就要滿了(也就是next指針馬上指向棧頂)矾睦,這時就要執(zhí)行上面說的操作晦款,建立下一頁page對象,與這一頁鏈表連接完成后枚冗,新page的next指針被初始化在棧底(begin的位置)缓溅,然后繼續(xù)向棧頂添加新對象。

所以赁温,向一個對象發(fā)送- autorelease消息坛怪,就是將這個對象加入到當(dāng)前AutoreleasePoolPage的棧頂next指針指向的位置

釋放時刻

每當(dāng)進行一次objc_autoreleasePoolPush調(diào)用時淤齐,runtime向當(dāng)前的AutoreleasePoolPage中add進一個哨兵對象,值為0(也就是個nil)袜匿,那么這一個page就變成了下面的樣子:

image

objc_autoreleasePoolPush的返回值正是這個哨兵對象的地址更啄,被objc_autoreleasePoolPop(哨兵對象)作為入?yún)ⅲ谑牵?/p>

1.根據(jù)傳入的哨兵對象地址找到哨兵對象所處的page

2.在當(dāng)前page中居灯,將晚于哨兵對象插入的所有autorelease對象都發(fā)送一次- release消息祭务,并向回移動next指針到正確位置

3.補充2:從最新加入的對象一直向前清理,可以向前跨越若干個page怪嫌,直到哨兵所在的page(在一個page中义锥,是從高地址向低地址清理)

剛才的objc_autoreleasePoolPop執(zhí)行后,最終變成了下面的樣子:

image
嵌套的AutoreleasePool

知道了上面的原理喇勋,嵌套的AutoreleasePool就非常簡單了缨该,pop的時候總會釋放到上次push的位置為止,多層的pool就是多個哨兵對象而已川背,就像剝洋蔥一樣贰拿,每次一層,互不影響熄云。

七膨更、class_ro_t 和 class_rw_t 的區(qū)別?

Class的結(jié)構(gòu)
image
class_rw_t

class_rw_t里面的methods缴允、properties荚守、protocols是二維數(shù)組,是可讀可寫的练般,包含了類的初始內(nèi)容矗漾、分類的內(nèi)容

image
class_ro_t

class_ro_t里面的baseMethodList、baseProtocols薄料、ivars敞贡、baseProperties是一維數(shù)組,是只讀的摄职,包含了類的初始內(nèi)容

image

收錄文章來源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末誊役,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谷市,更是在濱河造成了極大的恐慌蛔垢,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迫悠,死亡現(xiàn)場離奇詭異鹏漆,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門甫男,熙熙樓的掌柜王于貴愁眉苦臉地迎上來且改,“玉大人,你說我怎么就攤上這事板驳∮瞩耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵若治,是天一觀的道長慨蓝。 經(jīng)常有香客問我,道長端幼,這世上最難降的妖魔是什么礼烈? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮婆跑,結(jié)果婚禮上此熬,老公的妹妹穿的比我還像新娘。我一直安慰自己滑进,他們只是感情好犀忱,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扶关,像睡著了一般阴汇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上节槐,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天搀庶,我揣著相機與錄音,去河邊找鬼铜异。 笑死哥倔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的揍庄。 我是一名探鬼主播未斑,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼币绩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起府阀,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缆镣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后试浙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體董瞻,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了钠糊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挟秤。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抄伍,靈堂內(nèi)的尸體忽然破棺而出艘刚,到底是詐尸還是另有隱情,我是刑警寧澤截珍,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布攀甚,位于F島的核電站,受9級特大地震影響岗喉,放射性物質(zhì)發(fā)生泄漏秋度。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一钱床、第九天 我趴在偏房一處隱蔽的房頂上張望荚斯。 院中可真熱鬧,春花似錦查牌、人聲如沸事期。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刑赶。三九已至,卻和暖如春懂衩,著一層夾襖步出監(jiān)牢的瞬間撞叨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工浊洞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牵敷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓法希,卻偏偏與公主長得像枷餐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苫亦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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