iOS runtime2-成員變量、屬性虱岂、方法玖院、消息、分類第岖、協(xié)議

本文是參考南峰子Objective-C Runtime系列文章
做一個(gè)自己的總結(jié)难菌,強(qiáng)烈推薦查看原文

1、類型編碼(Type Encoding)

南峰子關(guān)于Type Encoding的翻譯

蘋果官方文檔蔑滓,Type Encodings

蘋果官方文檔郊酒,Property Type String

在定義一個(gè)方法如下:第四個(gè)參數(shù)就是參考Type Encoding

class_addMethod([self class],@selector(eat:),(IMP) eatIMP, "v@:@");

v@:@ : 沒(méi)有返回值遇绞,參數(shù)類型依次為,id猎塞、SEL试读、id

定義一個(gè)屬性如下:就是參考Property Type String
官方文檔寫了杠纵,T為開(kāi)始荠耽,V為結(jié)束,中間參考表

The string starts with a T followed by the @encode type and a comma, and finishes with a V followed by the name of the backing instance variable. Between these, the attributes are specified by the following descriptors, separated by commas:

   objc_property_attribute_t type = { "T", "@\"NSString\"" };//類型
    objc_property_attribute_t ownership = { "C", "" }; // C = copy
    objc_property_attribute_t nonatomic = { "N", "" }; //nonatomic
    objc_property_attribute_t backingivar  = { "V", "_property1" };//V 實(shí)例變量
    objc_property_attribute_t attrs[] = { type, ownership,nonatomic, backingivar };
    class_addProperty(cls, "property1", attrs, 4);

2比藻、Ivar

Ivar表示實(shí)例成員變量類型铝量,其實(shí)際是指向objc_ivar結(jié)構(gòu)體的指針,其定義如下:

typedef struct objc_ivar *Ivar;
struct objc_ivar {
    char * _Nullable ivar_name       // 變量名                        OBJC2_UNAVAILABLE;
    char * _Nullable ivar_type       //變量類型                           OBJC2_UNAVAILABLE;
    int ivar_offset                // 基地址偏移字節(jié)                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
} 

成員變量操作函數(shù)

// 獲取成員變量名
const char * ivar_getName ( Ivar v );
// 獲取成員變量類型編碼
const char * ivar_getTypeEncoding ( Ivar v );
// 獲取成員變量的偏移量
ptrdiff_t ivar_getOffset ( Ivar v );
  1. ivar_getOffset : 對(duì)于類型id或其它對(duì)象類型的實(shí)例變量银亲,可以調(diào)用id object_getIvar(id _Nullable obj, Ivar _Nonnull ivar)void object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)來(lái)直接訪問(wèn)成員變量慢叨,而不使用偏移量

3、objc_property_t

objc_property_t是表示屬性類型务蝠,其實(shí)際是指向objc_property結(jié)構(gòu)體的指針拍谐,其定義如下:

typedef struct objc_property *objc_property_t;

3.1、objc_property_attribute_t

objc_property_attribute_t定義了屬性的特性(attribute)馏段,它是一個(gè)結(jié)構(gòu)體轩拨,定義如下:

typedef struct {
    const char *name;           // 特性名
    const char *value;          // 特性值
} objc_property_attribute_t;

屬性操作函數(shù)如下:

// 獲取屬性名
const char * property_getName ( objc_property_t property );
// 獲取屬性特性描述字符串
const char * property_getAttributes ( objc_property_t property );
// 獲取屬性中指定的特性
char * property_copyAttributeValue ( objc_property_t property, const char *attributeName );
// 獲取屬性的特性列表
objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount );

注意:property_copyAttributeValueproperty_copyAttributeList使用完后需要調(diào)用free()釋放

4、關(guān)聯(lián)對(duì)象(Associated Object)

關(guān)聯(lián)對(duì)象類似于成員變量院喜,不過(guò)是在運(yùn)行時(shí)添加的亡蓉。我們通常會(huì)把成員變量(Ivar)放在類聲明的頭文件中,或者放在類實(shí)現(xiàn)的@implementation后面喷舀。但這有一個(gè)缺點(diǎn)砍濒,我們不能在分類中添加成員變量。如果我們嘗試在分類中添加新的成員變量硫麻,編譯器會(huì)報(bào)錯(cuò)爸邢。

我們可能希望通過(guò)使用(甚至是濫用)全局變量來(lái)解決這個(gè)問(wèn)題。但這些都不是Ivar拿愧,因?yàn)樗麄儾粫?huì)連接到一個(gè)單獨(dú)的實(shí)例甲棍。

Objective-C針對(duì)這一問(wèn)題,提供了一個(gè)解決方案:即關(guān)聯(lián)對(duì)象(Associated Object)

關(guān)聯(lián)對(duì)象操作函數(shù)如下:

// 設(shè)置關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy );
// 獲取關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject ( id object, const void *key );
// 移除關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects ( id object );

objc_AssociationPolicy如下:

BJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY

當(dāng)宿主對(duì)象被釋放時(shí)赶掖,會(huì)根據(jù)指定的內(nèi)存管理策略來(lái)處理關(guān)聯(lián)對(duì)象感猛。
如果指定的策略是assign,則宿主釋放時(shí)奢赂,關(guān)聯(lián)對(duì)象不會(huì)被釋放陪白;
如果指定的是retaincopy膳灶,則宿主釋放時(shí)咱士,關(guān)聯(lián)對(duì)象會(huì)被釋放

5立由、Method

typedef struct objc_method *Method;
struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

  1. method_name : 方法名
  2. method_imp : 方法實(shí)現(xiàn)

objc_method 結(jié)構(gòu)體中包含了一個(gè)SELIMP,實(shí)際相當(dāng)于在SELIMP之間做了一個(gè)映射,有了SEL序厉,我們便能找到對(duì)應(yīng)的IMP

objc_msgSend有兩個(gè)隱藏參數(shù):

  1. 消息接收對(duì)象
  2. 方法的selector

這兩個(gè)參數(shù)為方法的實(shí)現(xiàn)提供了調(diào)用者的信息锐膜。之所以說(shuō)是隱藏的,是因?yàn)樗鼈冊(cè)诙x方法的源代碼中沒(méi)有聲明弛房。它們是在編譯期被插入實(shí)現(xiàn)代碼的道盏。

雖然這些參數(shù)沒(méi)有顯示聲明,但在代碼中仍然可以引用它們文捶。我們可以使用self來(lái)引用接收者對(duì)象荷逞,使用_cmd來(lái)引用選擇器。

5.1粹排、SEL(方法名)

SEL又叫選擇器种远,是表示一個(gè)方法的selector的指針,其定義如下:

typedef struct objc_selector *SEL;

Objective-C在編譯時(shí)顽耳,會(huì)根據(jù)每個(gè)方法的名字坠敷、參數(shù)、生成一個(gè)唯一的標(biāo)識(shí)(Int類型地址)射富,這個(gè)標(biāo)識(shí)就是SEL膝迎,根據(jù)一個(gè)方法名hash化了的key值,能唯一代表一個(gè)方法辉浦,它的存在只是為了加快了方法的查詢速度

我們可以在運(yùn)行時(shí)添加新的selector弄抬,也可以在運(yùn)行時(shí)獲取已存在的selector,我們可以通過(guò)下面三種方法來(lái)獲取SEL:

  1. sel_registerName函數(shù)
  2. Objective-C編譯器提供的@selector()
  3. NSSelectorFromString()方法

選擇器相關(guān)操作如下:

// 返回給定選擇器指定的方法的名稱
const char * sel_getName ( SEL sel );
// 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法宪郊,將方法名映射到一個(gè)選擇器掂恕,并返回這個(gè)選擇器
SEL sel_registerName ( const char *str );
// 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法
SEL sel_getUid ( const char *str );
// 比較兩個(gè)選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );

1.sel_registerName:在我們將一個(gè)方法添加到類定義時(shí),我們必須在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法名以獲取方法的選擇器

5.2弛槐、IMP(方法實(shí)現(xiàn))

IMP實(shí)際上是一個(gè)函數(shù)指針懊亡,指向方法實(shí)現(xiàn)的首地址,其定義如下:

typedef void (*IMP)(void /* id, SEL, ... */ ); 

第一個(gè)參數(shù)是指向self的指針 (如果是對(duì)象方法乎串,則指向?qū)ο笏鶎兕惖暝妫绻穷惙椒ǎ瑒t指向元類)
第二參數(shù)是方法選擇器selector
第三個(gè)參數(shù)起叹誉,就是方法參數(shù)

方法名SEL的查找就是為了查找方法的實(shí)現(xiàn)IMP鸯两。由于每個(gè)方法對(duì)應(yīng)唯一的SEL,因此我們可以通過(guò)SEL快速準(zhǔn)確地獲取它所以對(duì)應(yīng)的IMP长豁。獲取IMP后钧唐,我們就獲得了執(zhí)行這個(gè)方法代碼的入口點(diǎn),就可以像調(diào)用普通C函數(shù)來(lái)使用這個(gè)函數(shù)了匠襟。

通過(guò)取得IMP钝侠,我們可以跳過(guò)Runtime的消息傳遞機(jī)制该园,直接執(zhí)行IMP指向的函數(shù)實(shí)現(xiàn),這樣省去了Runtime消息傳遞過(guò)程中所做的一系列查找操作帅韧,會(huì)比直接向?qū)ο蟀l(fā)送消息高效一些里初。

5.2.1、objc_method_description

方法描述包括:方法名和方法類型(類方法忽舟、實(shí)例方法)

struct objc_method_description {
    SEL _Nullable name;               /**< The name of the method */
    char * _Nullable types;           /**< The types of the method arguments */
};

方法操作相關(guān)函數(shù)如下:

// 調(diào)用指定方法的實(shí)現(xiàn)
id method_invoke ( id receiver, Method m, ... );
// 調(diào)用返回一個(gè)數(shù)據(jù)結(jié)構(gòu)的方法的實(shí)現(xiàn)
void method_invoke_stret ( id receiver, Method m, ... );
// 獲取方法名
SEL method_getName ( Method m );
// 返回方法的實(shí)現(xiàn)
IMP method_getImplementation ( Method m );
// 獲取描述方法參數(shù)和返回值類型的字符串
const char * method_getTypeEncoding ( Method m );
// 獲取方法的返回值類型的字符串
char * method_copyReturnType ( Method m );
// 獲取方法的指定位置參數(shù)的類型字符串
char * method_copyArgumentType ( Method m, unsigned int index );
// 通過(guò)引用返回方法的返回值類型字符串
void method_getReturnType ( Method m, char *dst, size_t dst_len );
// 返回方法的參數(shù)的個(gè)數(shù)
unsigned int method_getNumberOfArguments ( Method m );
// 通過(guò)引用返回方法指定位置參數(shù)的類型字符串
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );
// 返回指定方法的方法描述結(jié)構(gòu)體
struct objc_method_description * method_getDescription ( Method m );
// 設(shè)置方法的實(shí)現(xiàn)
IMP method_setImplementation ( Method m, IMP imp );
// 交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations ( Method m1, Method m2 );
  1. method_invoke : 返回的是實(shí)際實(shí)現(xiàn)的返回值双妨。參數(shù)receiver不能為空。這個(gè)方法的效率會(huì)比method_getImplementationmethod_getName更快
  2. method_getName : 返回的是一個(gè)SEL萧诫。如果想獲取方法名的C字符串斥难,可以使用sel_getName(method_getName(method))枝嘶。
  3. method_getReturnType : 類型字符串會(huì)被拷貝到dst
  4. method_setImplementation : 注意該函數(shù)返回值是方法之前的實(shí)現(xiàn)

6帘饶、消息

給一個(gè)對(duì)象發(fā)送消息:如[person run];
會(huì)被編譯成objc_msgSend(person, @selector(run));
objc_msgSend具體查找流程如下

  1. 通過(guò)isa指針找到對(duì)象所屬的類
  2. 查找類的cache列表,成功群扶,跳轉(zhuǎn)到第4步及刻,失敗,跳轉(zhuǎn)到第3步
  3. 查找類的methodLists列表
  4. 查找名字相同的方法竞阐,成功跳轉(zhuǎn)到代碼實(shí)現(xiàn)缴饭,失敗,跳轉(zhuǎn)到第5步
  5. 沿著集成體系向上查找骆莹,成功跳轉(zhuǎn)到代碼實(shí)現(xiàn)颗搂,失敗,消息轉(zhuǎn)發(fā)

6.1幕垦、消息轉(zhuǎn)發(fā)

  1. 動(dòng)態(tài)方法解析:詢問(wèn)是否有動(dòng)態(tài)方法來(lái)處理這個(gè)未知消息丢氢,有則消息轉(zhuǎn)發(fā)結(jié)束
  2. 備用接收者:詢問(wèn)其他接收者是否能處理這條消息,有先改,則將這條消息轉(zhuǎn)發(fā)給該接收者疚察,消息轉(zhuǎn)發(fā)結(jié)束
  3. 完整轉(zhuǎn)發(fā):將這個(gè)未知消息所有細(xì)節(jié)封裝到NSInvocation對(duì)象中,再次詢問(wèn)仇奶,如果此時(shí)還未能處理貌嫡,則消息處理失敗
+ (BOOL)resolveInstanceMethod:(SEL)sel{}

- (id)forwardingTargetForSelector:(SEL)aSelector{}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{}

- (void)forwardInvocation:(NSInvocation *)anInvocation{}

7、Protocol

Protocol定義如下该溯,可以看出Protocol就是一個(gè)對(duì)象結(jié)構(gòu)體

typedef struct objc_object Protocol;

Protocol相關(guān)操作函數(shù)如下:

// 返回指定的協(xié)議
Protocol * objc_getProtocol ( const char *name );
// 獲取運(yùn)行時(shí)所知道的所有協(xié)議的數(shù)組
Protocol ** objc_copyProtocolList ( unsigned int *outCount );
// 創(chuàng)建新的協(xié)議實(shí)例
Protocol * objc_allocateProtocol ( const char *name );
// 在運(yùn)行時(shí)中注冊(cè)新創(chuàng)建的協(xié)議
void objc_registerProtocol ( Protocol *proto );
// 為協(xié)議添加方法
void protocol_addMethodDescription ( Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod );
// 添加一個(gè)已注冊(cè)的協(xié)議到協(xié)議中
void protocol_addProtocol ( Protocol *proto, Protocol *addition );
// 為協(xié)議添加屬性
void protocol_addProperty ( Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty );
// 返回協(xié)議名
const char * protocol_getName ( Protocol *p );
// 測(cè)試兩個(gè)協(xié)議是否相等
BOOL protocol_isEqual ( Protocol *proto, Protocol *other );
// 獲取協(xié)議中指定條件的方法的方法描述數(shù)組
struct objc_method_description * protocol_copyMethodDescriptionList ( Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount );
// 獲取協(xié)議中指定方法的方法描述
struct objc_method_description protocol_getMethodDescription ( Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod );
// 獲取協(xié)議中的屬性列表
objc_property_t * protocol_copyPropertyList ( Protocol *proto, unsigned int *outCount );
// 獲取協(xié)議的指定屬性
objc_property_t protocol_getProperty ( Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty );
// 獲取協(xié)議采用的協(xié)議
Protocol ** protocol_copyProtocolList ( Protocol *proto, unsigned int *outCount );
// 查看協(xié)議是否采用了另一個(gè)協(xié)議
BOOL protocol_conformsToProtocol ( Protocol *proto, Protocol *other );
  1. objc_getProtocol : 如果僅僅是聲明了一個(gè)協(xié)議岛抄,而未在任何類中實(shí)現(xiàn)這個(gè)協(xié)議,則該函數(shù)返回的是nil狈茉。
  2. objc_copyProtocolList : 獲取到的數(shù)組需要使用free()來(lái)釋放
  3. objc_allocateProtocol : 如果同名的協(xié)議已經(jīng)存在夫椭,則返回nil
  4. objc_registerProtocol : 創(chuàng)建一個(gè)新的協(xié)議后,必須調(diào)用該函數(shù)以在運(yùn)行時(shí)中注冊(cè)新的協(xié)議论皆。協(xié)議注冊(cè)后便可以使用益楼,但不能再做修改猾漫,即注冊(cè)完后不能再向協(xié)議添加方法或協(xié)議

注意: 協(xié)議一旦注冊(cè)后就不可再修改,即無(wú)法再通過(guò)調(diào)用protocol_addMethodDescription感凤、protocol_addProtocolprotocol_addProperty往協(xié)議中添加方法屬性等

8悯周、 Category

Category定義如下,表示指向分類結(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; // 實(shí)例方法列表
    struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 類方法列表
    struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分類所實(shí)現(xiàn)的協(xié)議列表
}                                                           

runtime并沒(méi)有提供過(guò)多的函數(shù)來(lái)處理分類

9陪竿、id

typedef struct objc_object *id;

它是一個(gè)objc_object結(jié)構(gòu)類型的指針禽翼。它的存在可以讓我們實(shí)現(xiàn)類似于C++中泛型的一些操作。該類型的對(duì)象可以轉(zhuǎn)換為任何一種對(duì)象族跛,有點(diǎn)類似于C語(yǔ)言void *指針類型的作用闰挡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市礁哄,隨后出現(xiàn)的幾起案子长酗,更是在濱河造成了極大的恐慌,老刑警劉巖桐绒,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺脾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茉继,警方通過(guò)查閱死者的電腦和手機(jī)咧叭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)烁竭,“玉大人菲茬,你說(shuō)我怎么就攤上這事∨伤海” “怎么了婉弹?”我有些...
    開(kāi)封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)腥刹。 經(jīng)常有香客問(wèn)我马胧,道長(zhǎng),這世上最難降的妖魔是什么衔峰? 我笑而不...
    開(kāi)封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任佩脊,我火速辦了婚禮,結(jié)果婚禮上垫卤,老公的妹妹穿的比我還像新娘威彰。我一直安慰自己,他們只是感情好穴肘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布歇盼。 她就那樣靜靜地躺著,像睡著了一般评抚。 火紅的嫁衣襯著肌膚如雪豹缀。 梳的紋絲不亂的頭發(fā)上伯复,一...
    開(kāi)封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音邢笙,去河邊找鬼啸如。 笑死,一個(gè)胖子當(dāng)著我的面吹牛氮惯,可吹牛的內(nèi)容都是我干的叮雳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼妇汗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帘不!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起杨箭,我...
    開(kāi)封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寞焙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后告唆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棺弊,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晶密,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年擒悬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稻艰。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懂牧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尊勿,到底是詐尸還是另有隱情僧凤,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布元扔,位于F島的核電站躯保,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏澎语。R本人自食惡果不足惜途事,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擅羞。 院中可真熱鬧尸变,春花似錦、人聲如沸减俏。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)娃承。三九已至奏夫,卻和暖如春怕篷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酗昼。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工匙头, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仔雷。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓蹂析,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親碟婆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子电抚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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