3.iOS底層學(xué)習(xí)之oc對(duì)象的本質(zhì)

如何探究對(duì)象的本質(zhì)扇丛?

因?yàn)閛c的底層是c和c++實(shí)現(xiàn)的,clang可以將oc還原為c或者c++的代碼,所以通過(guò)clang可以看到一個(gè)對(duì)象的c或者c++的基層實(shí)現(xiàn)程拭。

Clang的擴(kuò)展,什么是Clang?

Clang: a C language family frontend for LLVM筷狼。Clang 項(xiàng)目為LLVM 項(xiàng)目的 C 語(yǔ)言家族(C瓶籽、C++、Objective C/C++埂材、OpenCL塑顺、CUDA 和 RenderScript)中的語(yǔ)言提供了語(yǔ)言前端和工具基礎(chǔ)結(jié)構(gòu)。提供了 GCC 兼容的編譯器驅(qū)動(dòng)程序和 MSVC 兼容的編譯器驅(qū)動(dòng)程序 俏险。

Clang編譯oc文件

模擬器版cpp文件生成命令:

xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
生成了main-arm64.cpp文件:
301623835223_.pic.jpg

查看文件的內(nèi)容严拒,我在main.m中聲明和實(shí)現(xiàn)了NNPerson這個(gè)類,對(duì)應(yīng)的main.cpp文件的代碼如下:


WechatIMG33.jpeg

可以看到NNPerson生成了一個(gè)結(jié)構(gòu)體:

struct NNPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _myAge;
};

其中竖独,有一個(gè)NSObject_IMPL裤唠,還一個(gè)我在oc中定義的myAge。進(jìn)一步查看NSObject_IMPL是什么预鬓,全局搜索可以看到如下代碼塊:

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;
typedef struct objc_object *id;
typedef struct classref *classref_t;

由此可見(jiàn)巧骚,一個(gè)對(duì)象的本質(zhì)是一個(gè)結(jié)構(gòu)體,里面主要有一個(gè)結(jié)構(gòu)體NSObject_IMPL格二,也就是isa劈彪,是一個(gè)objc_class指針。
在這里我們也看到了關(guān)于id的定義顶猜,就是一個(gè)objc_object指針沧奴,所以我們通常用id來(lái)聲明對(duì)象的時(shí),直接id聲明就好长窄,不用再加對(duì)象的星號(hào)了滔吠。

然后進(jìn)一步查找objc_class可以看到objc_class的定義如下:

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

通過(guò)這里可以看到objc_class繼承自objc_object,我們點(diǎn)進(jìn)去objc_object看到了objc_class的結(jié)構(gòu):

#if !OBJC_TYPES_DEFINED

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

/// An opaque type that represents a category.
typedef struct objc_category *Category;

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

#endif

看到這個(gè)挠日,但是在現(xiàn)在的oc版本中這段代碼已經(jīng)廢棄了疮绷,最前面有宏定義#if !OBJC_TYPES_DEFINED,然后查看這個(gè)OBJC_TYPES_DEFINED發(fā)現(xiàn)定義如下:

#ifdef _OBJC_OBJC_H_
#error include objc-private.h before other headers
#endif

#define OBJC_TYPES_DEFINED 1
#undef OBJC_OLD_DISPATCH_PROTOTYPES
#define OBJC_OLD_DISPATCH_PROTOTYPES 0

始終為1嚣潜,取非的話就是0就不會(huì)走這個(gè)地方了冬骚。

再搜索看到可以看大objc_class的結(jié)構(gòu)如下:

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
  //此處省略下面的一堆方法等等等
}

objc_class繼承自objc_object,所以我們來(lái)看objc2在使用的objc_object的結(jié)構(gòu)懂算,查找如下:

struct objc_object {
private:
    isa_t isa;
//此處省略下面的一堆定義 主要可以看到一個(gè)isa
}

isa為isa_t類型只冻,isa_t定義如下:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    uintptr_t bits;
private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;
public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

    bool isDeallocating() {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    void setDeallocating() {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif
    void setClass(Class cls, objc_object *obj);
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated);
};

isa_t是一個(gè)聯(lián)合體,所以下面說(shuō)下結(jié)構(gòu)體和共用體的區(qū)別计技。

結(jié)構(gòu)體和共用體

-結(jié)構(gòu)體:結(jié)構(gòu)體(Struct)是一種集合喜德,它里面包含了多個(gè)變量或數(shù)組,它們的類型可以相同垮媒,也可以不同舍悯,每個(gè)這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員(Member)航棱。定義格式如下:

struct 結(jié)構(gòu)體名{
    結(jié)構(gòu)體所包含的變量或數(shù)組
};

-共用體:共用體(Union)有時(shí)也被稱為聯(lián)合或者聯(lián)合體。定義格式如下:

union 共用體名{
    成員列表
};

結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體占用的內(nèi)存大于等于所有成員占用的內(nèi)存的總和(成員之間可能會(huì)存在縫隙)贱呐,共用體占用的內(nèi)存等于最長(zhǎng)的成員占用的內(nèi)存丧诺。共用體使用了內(nèi)存覆蓋技術(shù),同一時(shí)刻只能保存一個(gè)成員的值奄薇,如果對(duì)新的成員賦值驳阎,就會(huì)把原來(lái)成員的值覆蓋掉。

舉一個(gè)共用體的例子??:

union NNTestUnion{
    int a;
    char b;
    double c;
}testUnion;

 NSLog(@"占用的大小是:%lu",sizeof(testUnion));
 testUnion.a = 5;
 NSLog(@"a=%d,b=%c,c=%f",testUnion.a,testUnion.b,testUnion.c);
 testUnion.b = 'a';
 NSLog(@"a=%d,b=%c,c=%f",testUnion.a,testUnion.b,testUnion.c);
 testUnion.c = 8.0;
 NSLog(@"a=%d,b=%c,c=%f",testUnion.a,testUnion.b,testUnion.c);

打印如下:


351624008206_.pic.jpg

可以根據(jù)上面的結(jié)果看到馁蒂,同一時(shí)刻只能保存一個(gè)成員的值呵晚。成員之間是互斥的

如果union的成員定義改成如下:

union NNTestUnion{
    int a;
    char b;
    float c;
}testUnion;

那么占用的大小第一個(gè)打印變成:
2021-06-18 17:23:57.350435+0800 KCObjcBuild[48834:10108220] 占用的大小是:4
所以共用體占用的內(nèi)存等于最長(zhǎng)的成員占用的內(nèi)存。

位域

位域:有些數(shù)據(jù)在存儲(chǔ)時(shí)并不需要占用一個(gè)完整的字節(jié)沫屡,只需要占用一個(gè)或幾個(gè)二進(jìn)制位即可饵隙。例如開(kāi)關(guān)只有通電和斷電兩種狀態(tài),用 0 和 1 表示足以沮脖,也就是用一個(gè)二進(jìn)位金矛。正是基于這種考慮,C語(yǔ)言又提供了一種叫做位域的數(shù)據(jù)結(jié)構(gòu)勺届。

在結(jié)構(gòu)體定義時(shí)驶俊,我們可以指定某個(gè)成員變量所占用的二進(jìn)制位數(shù)(Bit),這就是位域免姿。

舉一個(gè)位域的例子??:

struct direction{
    bool up:1;
    bool down:1;
    bool left:1;
    bool right:1;
}direction1;

struct Direction{
    bool up;
    bool down;
    bool left;
    bool right;
}Direction1;

  NSLog(@"direction1占用的大小是:%lu",sizeof(direction1));
  NSLog(@"Direction1占用的大小是:%lu",sizeof(Direction1));

打印結(jié)果如下:


361624009262_.pic.jpg

就很清楚的可以看到結(jié)果饼酿,指定完畢之后就節(jié)省了很多內(nèi)存

ISA_BITFIELD

在union isa_t 中有一個(gè)ISA_BITFIELD這個(gè),點(diǎn)進(jìn)去看一下這個(gè)的定義:

typedef unsigned long          uintptr_t;
# if __arm64__
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t has_cxx_dtor      : 1;                                       \
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        uintptr_t magic             : 6;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 19

# elif __x86_64__
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t unused            : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8

可以看到ISA_BITFIELD是一個(gè)宏定義胚膊,所以那個(gè)isa_t中:

#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

可以替換為:

typedef unsigned long          uintptr_t;
#if defined(ISA_BITFIELD)
    struct {
          // defined in isa.h
      uintptr_t nonpointer        : 1;                                         
      uintptr_t has_assoc         : 1;                                        
      uintptr_t has_cxx_dtor      : 1;                                         
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ 
      uintptr_t magic             : 6;                                         
      uintptr_t weakly_referenced : 1;                                         
      uintptr_t unused            : 1;                                         
      uintptr_t has_sidetable_rc  : 1;                                         
      uintptr_t extra_rc          : 8;
    };

可以了解到故俐,isa的定義,它里面定義了一個(gè)位域。并且可以看到紊婉,x86_64和arm64下的位域定義是不一樣的药版,不過(guò)都是占滿了所有的64位(1+1+1+33+6+1+1+1+19 = 64,x86_64同理)喻犁,下面來(lái)說(shuō)明一下每一個(gè)位域參數(shù)的含義:

-nonpointer:表示是否對(duì)isa開(kāi)啟指針優(yōu)化 槽片。0代表是純isa指針,1代表除了地址外株汉,還包含了類的一些信息筐乳、對(duì)象的引用計(jì)數(shù)等歌殃。
-has_assoc:關(guān)聯(lián)對(duì)象標(biāo)志位乔妈。
-has_cxx_dtor:該對(duì)象是否有C++或Objc的析構(gòu)器,如果有析構(gòu)函數(shù)氓皱,則需要做一些析構(gòu)的邏輯處理路召,如果沒(méi)有勃刨,則可以更快的釋放對(duì)象。
-shiftcls:存在類指針的值股淡,開(kāi)啟指針優(yōu)化的情況下身隐,arm64位中有33位來(lái)存儲(chǔ)類的指針。
-magic:判斷當(dāng)前對(duì)象是真的對(duì)象還是一段沒(méi)有初始化的空間唯灵。
-weakly_referenced:是否被指向或者曾經(jīng)指向一個(gè)ARC的弱變量贾铝,沒(méi)有弱引用的對(duì)象釋放的更快。
-unused:標(biāo)志是否未被使用過(guò)埠帕。
-has_sidetable_rc:當(dāng)對(duì)象引用計(jì)數(shù)大于10時(shí)垢揩,則需要進(jìn)位。
-extra_rc:表示該對(duì)象的引用計(jì)數(shù)值敛瓷,實(shí)際上是引用計(jì)數(shù)減一叁巨。例如:如果引用計(jì)數(shù)為10,那么extra_rc為9呐籽。如果引用計(jì)數(shù)大于10锋勺,則需要使用has_sidetable_rc。

( 說(shuō)明:以上參數(shù)含義摘抄自網(wǎng)絡(luò)資料狡蝶,不是官方文檔解釋庶橱,我自己想查官方的解釋沒(méi)找到哇ε=(′ο`))) )*

類和isa是如何綁定的?

之前學(xué)習(xí)alloc的流程時(shí)牢酵,最后在方法_class_createInstanceFromZone中悬包,做了三件事情,一個(gè)是計(jì)算所需要的空間大小馍乙,一個(gè)是開(kāi)辟內(nèi)存空間布近,另外一個(gè)是isa的的初始化,和相關(guān)類進(jìn)行綁定丝格。方法是initIsa撑瞧,那么具體來(lái)看看這個(gè)方法里做了些什么。

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);

    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());


#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

根據(jù)我們前面介紹的ISA_BITFIELD各個(gè)位域的意義显蝌,可以了解到shiftcls是用來(lái)存放類指針的值预伺,magic是用來(lái)判斷這個(gè)類有沒(méi)有被初始化完畢。
那么上面的代碼可以看到曼尊,會(huì)先初始化一個(gè)isa_t酬诀,根據(jù)nonpointer,也就是是不是純的ISA指針進(jìn)行不同分支的初始化后續(xù)操作骆撇。
我這個(gè)走的是純指針的流程:


371624250036_.pic.jpg

然后會(huì)來(lái)到setClass方法:

isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
    // Match the conditional in isa.h.
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#   if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE
    // No signing, just use the raw pointer.
    uintptr_t signedCls = (uintptr_t)newCls;

#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT
    // We're only signing Swift classes. Non-Swift classes just use
    // the raw pointer
    uintptr_t signedCls = (uintptr_t)newCls;
    if (newCls->isSwiftStable())
        signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));

#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
    // We're signing everything
    uintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));

#   else
#       error Unknown isa signing mode.
#   endif

    shiftcls_and_sig = signedCls >> 3;

#elif SUPPORT_INDEXED_ISA
    // Indexed isa only uses this method to set a raw pointer class.
    // Setting an indexed class is handled separately.
    cls = newCls;

#else // Nonpointer isa, no ptrauth
    shiftcls = (uintptr_t)newCls >> 3;
#endif
}

我這個(gè)進(jìn)到setClass方法以后直接走的這句

 shiftcls = (uintptr_t)newCls >> 3;

shiftcls這個(gè)的值是newCls右移三位得到瞒御,我查看相關(guān)資料說(shuō)這里是為了內(nèi)存對(duì)齊,指針占8字節(jié)所以是八字節(jié)對(duì)齊神郊。
此時(shí)我打印右移之前的和右移之后的內(nèi)存地址如下:


381624250375_.pic.jpg

過(guò)掉斷點(diǎn)走到這個(gè)地方:


391624250523_.pic_hd.jpg

此時(shí)打印下newisa為:


401624250583_.pic.jpg

根據(jù)顯示的地址肴裙,此時(shí)isa中的cls已經(jīng)關(guān)聯(lián)上了類趾唱,shiftcls也保存上了相關(guān)的類信息。

(雖然我還是有點(diǎn)迷糊蜻懦,再有新的理解會(huì)更新的甜癞,太難了哇??)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宛乃,隨后出現(xiàn)的幾起案子悠咱,更是在濱河造成了極大的恐慌,老刑警劉巖征炼,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乔煞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡柒室,警方通過(guò)查閱死者的電腦和手機(jī)渡贾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雄右,“玉大人空骚,你說(shuō)我怎么就攤上這事±奕裕” “怎么了囤屹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逢渔。 經(jīng)常有香客問(wèn)我肋坚,道長(zhǎng),這世上最難降的妖魔是什么肃廓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任智厌,我火速辦了婚禮,結(jié)果婚禮上盲赊,老公的妹妹穿的比我還像新娘铣鹏。我一直安慰自己,他們只是感情好哀蘑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布诚卸。 她就那樣靜靜地躺著,像睡著了一般绘迁。 火紅的嫁衣襯著肌膚如雪合溺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天缀台,我揣著相機(jī)與錄音棠赛,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恭朗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播依疼,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼痰腮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了律罢?” 一聲冷哼從身側(cè)響起膀值,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎误辑,沒(méi)想到半個(gè)月后沧踏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巾钉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年翘狱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砰苍。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡潦匈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赚导,到底是詐尸還是另有隱情茬缩,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布吼旧,位于F島的核電站凰锡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏圈暗。R本人自食惡果不足惜掂为,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望员串。 院中可真熱鬧菩掏,春花似錦、人聲如沸昵济。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)访忿。三九已至瞧栗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間海铆,已是汗流浹背迹恐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卧斟,地道東北人殴边。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓憎茂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親锤岸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竖幔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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