Objective-C 對象的本質(zhì) 01 - 底層實(shí)現(xiàn)

Objective-C 對象的本質(zhì) 01 - 底層實(shí)現(xiàn)

我們平時編寫的 Objective-C 代碼,底層實(shí)現(xiàn)為 C/C++ 代碼迁酸。
Objective-C --> C/C++ --> 匯編 --> 機(jī)器碼讲冠。
Objective-C 的面向?qū)ο笫腔?C/C++ 的==結(jié)構(gòu)體==實(shí)現(xiàn)的瓜客。

在終端里使用 ==xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc class.m -o class.cpp== 命令可以將 Objective-C 轉(zhuǎn)換為 C++ 代碼。

Objective-C 底層實(shí)現(xiàn):

// Objective-C
@interface NSObject {
    Class isa;
}       
@end

// C++: NSObject Implementation
// 存儲了 Class 對象的 isa 和 其他成員變量
struct NSObject_IMPL {
    Class isa; // 8 Byte
};

// Class 指針
typedef struct objc_class *Class;

// id竿开,可以作為任何對象的指針
typedef struct objc_object *id;

// Tips:子類的的結(jié)構(gòu)體實(shí)現(xiàn)中有一個成員為父類的結(jié)構(gòu)體
struct Bar_IMPL {
    struct Foo_IMPL Foo_IVARS;
    // ivars...  
}

一個 Objective-C(假設(shè)沒有屬性) 對象占用多少內(nèi)存谱仪?

  • 先拋結(jié)論:一個沒有屬性的 Objective-C 對象占用 16 Byte 的內(nèi)存空間。
  • 使用 class_getInstanceSize() 函數(shù)可以查看一個類底層結(jié)構(gòu)體占用的內(nèi)存大蟹癫省(結(jié)構(gòu)體需要內(nèi)存對齊)疯攒,返回 8 Byte。
  • 使用 malloc_size() 函數(shù)可以查看一個對象占用的內(nèi)存大小列荔,16 Byte敬尺。

試驗(yàn):在類中不斷添加 int 屬性(4 Byte),再次調(diào)用 class_getInstanceSize() 和 malloc_size()贴浙。

  1. 添加 1 ~ 2 個 int 屬性:
    • class_getInstanceSize():return 16 Byte砂吞,
    • malloc_size():return 16 Byte,
  2. 添加 3 ~ 4 個 int 屬性:
    • class_getInstanceSize():return 24 Byte崎溃,
    • malloc_size():return 32 Byte蜻直,
  3. 添加 5 ~ 6 個 int 屬性:
    • class_getInstanceSize():return 32 Byte,
    • malloc_size():return 32 Byte笨奠,
  4. 添加 7 ~ 8 個 int 屬性:
    • class_getInstanceSize():return 40 Byte袭蝗,
    • malloc_size():return 48 Byte,

結(jié)論:

  1. class_getInstanceSize():在添加 int 屬性的過程中般婆,NSObject_IMPL 結(jié)構(gòu)體中寬度最大的元素始終為 isa 指針變量到腥,所以結(jié)構(gòu)體的大小為 8 Byte 的整倍數(shù) 。
  2. malloc_size():實(shí)際在為對象分配內(nèi)存空間時蔚袍,大小為 16 Byte 的整倍數(shù)乡范。

補(bǔ)充:通過在控制臺使用 memory read 指令打印對象的內(nèi)存數(shù)據(jù)配名,也可以看到長度為 16 Byte。

訪問內(nèi)存的 LLDB 命令:


通過上面的試驗(yàn)可以知道晋辆,實(shí)際上在實(shí)例化一個對象時渠脉,需要分配至少 16 Byte 的內(nèi)存空間,接下來看看相關(guān)的源碼瓶佳。

在 objc4-723 版本的源碼中芋膘,從 objc/Project-Headers/objc-runtime-new.h 文件中的 objc_class 結(jié)構(gòu)體中,有一個 instanceSize() 函數(shù)(1279 ~ 1284 行)霸饲,返回值 >= 16为朋,1281 行的注釋為:“// CF requires all objects be at least 16 bytes.”,通過跟蹤類的 alloc 方法內(nèi)部的調(diào)用鏈厚脉,可以看到在分配內(nèi)存前調(diào)用了 instanceSize() 函數(shù)习寸,calloc() 函數(shù)根據(jù)這個函數(shù)的返回值分配內(nèi)存空間給對象使用,并且會保證內(nèi)存大小為 16 Byte 的整倍數(shù)傻工。

class_getInstanceSize() 函數(shù)在蘋果開源的 objc4 源碼中可以看到霞溪。

// objc/Source/objc-class.mm
//
// 查看一個類底層結(jié)構(gòu)體占用的內(nèi)存大小(結(jié)構(gòu)體需要內(nèi)存對齊)
size_t class_getInstanceSize(Class cls) {
    if (!cls) return 0;
    return cls->alignedInstanceSize(); // 調(diào)用 objc_class 結(jié)構(gòu)體中的函數(shù)
}

核心源碼

objc_object中捆、objc_class

// objc/Project-Headers/objc-private.h
//
// objc_class 的基類鸯匹,主要存儲了 Meta-Class 對象的 isa
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // initIsa(): other objects
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);
    
    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);
};

// objc/Project-Headers/objc-runtime-new.h
// 
// Class、Meta-Class 都是通過 objc_class 實(shí)現(xiàn)的
//
// 作為 Class 使用時泄伪,存儲了:
//     isa:Meta-Class 對象的 isa
//     superclass:父類的 Class 對象忽你,如果沒有父類,superclass 為 nil
//     bits.data().ro.ivars:成員變量信息(ivar_list_t)
//     bits.data().properties:屬性信息(method_array_t)
//     bits.data().methods:成員方法信息 (property_array_t)
//     bits.data().protocols:協(xié)議信息(protocol_array_t)
//
// 作為 Meta-Class 使用時臂容,存儲了:
//     isa:基類的 Meta-Class 對象的 isa科雳,基類的 Meta-Class 對象的 isa 指向自身
//     superclass:父類的 Meta-Class 對象,
//                 基類的 Meta-Class 對象的 superclass 指向基類的 Class 對象
//     bits.data().methods:類方法的信息   
// 
struct objc_class : objc_object {
    // Class ISA;                    // isa脓杉,繼承自 objc_object
    Class superclass;          // 父類的 Class 對象或 Meta-Class 對象
    cache_t cache;              // 方法緩存
    class_data_bits_t bits;  // 存放 Class 或 Meta-Class 信息對象的指針

    class_rw_t *data() { 
        return bits.data();
    }
    
    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
            return size;
        }
    }
 };

RW_REALIZED糟秘、word_align()

// objc/Project-Headers/objc-runtime-new.h
//
// Values for class_rw_t->flags
// These are not emitted by the compiler and are never used in class_ro_t. 
// Their presence should be considered in future ABI versions.
// class_t->data is class_rw_t, not class_ro_t
#define RW_REALIZED           (1<<31)

// objc/Project-Headers/objc-os.h
//
#ifdef __LP64__
#   define WORD_SHIFT 3UL
#   define WORD_MASK 7UL
#   define WORD_BITS 64
#else
#   define WORD_SHIFT 2UL
#   define WORD_MASK 3UL
#   define WORD_BITS 32
#endif

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

class_data_bits_t、class_rw_t球散、class_ro_t

// objc/Project-Headers/objc-runtime-new.h
// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放類信息對象的指針
struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits; // typedef unsigned long uintptr_t;

    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放類的成員(變量尿赚、屬性、方法)信息蕉堰、協(xié)議信息或類方法信息凌净,包含分類中的屬性、方法屋讶、協(xié)議信息
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;       // 成員方法信息或類方法信息(包含分類的方法信息)
    property_array_t properties;  // 屬性信息(包含分類的屬性信息)
    protocol_array_t protocols;   // 協(xié)議信息(包含分類的協(xié)議信息)

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

    #if SUPPORT_INDEXED_ISA
        uint32_t index;
    #endif
};

// objc/Project-Headers/objc-runtime-new.h
// 
// 存放類的成員(變量冰寻、屬性、方法)信息皿渗、協(xié)議信息或類方法信息斩芭,不包含分類中的屬性轻腺、方法、協(xié)議信息
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;          // 類底層結(jié)構(gòu)體占用的內(nèi)存大小
    #ifdef __LP64__
        uint32_t reserved;
    #endif

    const uint8_t * ivarLayout;

    const char * name;              // 類名
    method_list_t * baseMethodList; // 成員方法信息或類方法信息(不包含分類的方法信息)
    protocol_list_t * baseProtocols;// 協(xié)議信息(不包含分類的協(xié)議信息)
    const ivar_list_t * ivars;      // 成員變量信息

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;// 屬性信息(不包含分類的屬性信息)

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

SEL划乖、IMP

// 方法名(選擇器)
//
// 可以通過 @selector() 或 sel_registerName() 獲取 SEL贬养。
// 可以通過 sel_getName() 或 NSStringFromSelector() 將 SEL 轉(zhuǎn)換為字符串。
// 不同類中的同名方法所對應(yīng)的 SEL 是相同的琴庵。
//
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
// 函數(shù)指針
//
/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

bucket_t误算、cache_t

typedef unsigned long uintptr_t;

// 方法緩存中的 Key
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
// 方法緩存
struct bucket_t {
private:
    cache_key_t _key; // key 是方法的 SEL
    IMP _imp;         // 函數(shù)指針

public:
    inline cache_key_t key() const { return _key; }
    inline IMP imp() const { return (IMP)_imp; }
    inline void setKey(cache_key_t newKey) { _key = newKey; }
    inline void setImp(IMP newImp) { _imp = newImp; }

    void set(cache_key_t newKey, IMP newImp);
};
// 方法緩存列表
// 
// 用散列表(哈希表)來緩存調(diào)用多的方法,可以提高查找方法的速度迷殿。
// 
// _mask 為什么是 _buckets 的長度 -1 ?
// -1 能確保 (key & _mask) <= (_buckets 的長度 - 1)尉桩,用來作為 _buckets 的下標(biāo)來取值。
// 在 cache_t::find() 函數(shù)中可以看到計(jì)算下標(biāo)的代碼贪庙。
// 
// _buckets 的動態(tài)擴(kuò)容:
// 向 _buckets 添加最后一個緩存時會進(jìn)行擴(kuò)容。
// 每次擴(kuò)容為原來的兩倍(2^n)翰苫。
// 擴(kuò)容時會將緩存清除止邮,因?yàn)閿U(kuò)容后 _mask 發(fā)生了變化。
// 通過 cache_t::expand() 函數(shù)中進(jìn)行擴(kuò)容奏窑。
struct cache_t {
    struct bucket_t *_buckets; // 散列表
    mask_t _mask;              // 散列表的長度 -1
    mask_t _occupied;          // 已經(jīng)緩存的方法數(shù)量(<= _mask)

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(cache_key_t key, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};

其它源碼

entsize_list_tt导披、list_array_tt

// 數(shù)組,ivar_list_t埃唯、method_list_t撩匕、property_list_t 都是 entsize_list_tt 的子類型。
/***********************************************************************
* entsize_list_tt<Element, List, FlagMask>
* Generic implementation of an array of non-fragile structs.
*
* Element is the struct type (e.g. method_t)
* List is the specialization of entsize_list_tt (e.g. method_list_t)
* FlagMask is used to stash extra bits in the entsize field
*   (e.g. method list fixup markers)
**********************************************************************/
template <typename Element, typename List, uint32_t FlagMask>
struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
    Element first;

    uint32_t entsize() const;
    uint32_t flags() const;

    Element& getOrEnd(uint32_t i) const;
    Element& get(uint32_t i) const;

    size_t byteSize() const;
    List* duplicate() const;

    struct iterator;

    const iterator begin() const;
    const iterator end() const;

    struct iterator {
        uint32_t entsize;
        uint32_t index;  // keeping track of this saves a divide in operator-
        Element* element;

        iterator() { }

        iterator(const List& list, uint32_t start = 0)
        : entsize(list.entsize())
        , index(start)
        , element(&list.getOrEnd(start))
        { }
    };
};
// 數(shù)組墨叛,property_array_t止毕、method_array_t、protocol_array_t 都是 list_array_tt 的子類型漠趁。
/***********************************************************************
* list_array_tt<Element, List>
* Generic implementation for metadata that can be augmented by categories.
*
* Element is the underlying metadata type (e.g. method_t)
* List is the metadata's list type (e.g. method_list_t)
*
* A list_array_tt has one of three values:
* - empty
* - a pointer to a single list
* - an array of pointers to lists
*
* countLists/beginLists/endLists iterate the metadata lists
* count/begin/end iterate the underlying metadata elements
**********************************************************************/
template <typename Element, typename List>
class list_array_tt {
    struct array_t {
        uint32_t count;
        List* lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count * sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };

protected:
    class iterator {
        List **lists;
        List **listsEnd;
        typename List::iterator m, mEnd;
    };

public:
    uint32_t count();
    iterator begin();
    iterator end();

    uint32_t countLists();
    List** beginLists();
    List** endLists();

    template<typename Result>
    Result duplicate();
};

ivar_t扁凛、ivar_list_t

// 成員變量信息
struct ivar_t {
    int32_t *offset;
    const char *name;  // 變量名
    const char *type;  // 數(shù)據(jù)類型
    uint32_t alignment_raw;
    uint32_t size;     // 占用內(nèi)存大小
    
     uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
    bool containsIvar(Ivar ivar) const {
        return (ivar >= (Ivar)&*begin()  &&  ivar < (Ivar)&*end());
    }
};

method_t、method_list_t闯传、method_array_t

// 方法信息
// 
// *types 示意:| 返回值 | 參數(shù)1 | 參數(shù)2 | ... | 參數(shù)n |
// 例如一個沒有返回值且沒有參數(shù)的方法谨朝,它的 method_t 中的 *types 為 v16@:8
// | v16  | @0 | :8  |
// | void | id | SEL |
// 兩個參數(shù)為默認(rèn)參數(shù),id 對應(yīng) self甥绿,SEL 對應(yīng) _cmd
// 
// *types 中的數(shù)字:
// 第一個數(shù)字:所有參數(shù)一共占用多少字節(jié)
// 后面的數(shù)字:當(dāng)前參數(shù)從第幾個字節(jié)開始
struct method_t {
    SEL name;          // 方法名
    const char *types; // 包含了返回值類型字币、參數(shù)類型的編碼字符串
    IMP imp;           // 函數(shù)指針
};
// 方法信息列表(一維數(shù)組)
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
    bool isFixedUp() const;
    void setFixedUp();

    uint32_t indexOfMethod(const method_t *meth) const {
        uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
        assert(i < count);
        return i;
    }
};
// 方法信息列表(二維數(shù)組)
class method_array_t : public list_array_tt<method_t, method_list_t> {
    typedef list_array_tt<method_t, method_list_t> Super;

 public:
    method_list_t **beginCategoryMethodLists() {
        return beginLists();
    }
    
    method_list_t **endCategoryMethodLists(Class cls);

    method_array_t duplicate() {
        return Super::duplicate<method_array_t>();
    }
};

Type Encoding(通過 @encode() 可以查看指定類型的字符串編碼)


property_t、property_list_t共缕、property_array_t

struct property_t {
    const char *name;
    const char *attributes;
};
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
class property_array_t : public list_array_tt<property_t, property_list_t> {
    typedef list_array_tt<property_t, property_list_t> Super;

 public:
    property_array_t duplicate() {
        return Super::duplicate<property_array_t>();
    }
};

protocol_t洗出、protocol_list_t、protocol_array_t

typedef uintptr_t protocol_ref_t;  // protocol_t *, but unremapped

// Values for protocol_t->flags
#define PROTOCOL_FIXED_UP_2 (1<<31)  // must never be set by compiler
#define PROTOCOL_FIXED_UP_1 (1<<30)  // must never be set by compiler
// Bits 0..15 are reserved for Swift's use.

#define PROTOCOL_FIXED_UP_MASK (PROTOCOL_FIXED_UP_1 | PROTOCOL_FIXED_UP_2)

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;

    const char *demangledName();

    const char *nameForLogging() {
        return demangledName();
    }

    bool isFixedUp() const;
    void setFixedUp();

#   define HAS_FIELD(f) (size >= offsetof(protocol_t, f) + sizeof(f))

    bool hasExtendedMethodTypesField() const {
        return HAS_FIELD(_extendedMethodTypes);
    }
    bool hasDemangledNameField() const {
        return HAS_FIELD(_demangledName);
    }
    bool hasClassPropertiesField() const {
        return HAS_FIELD(_classProperties);
    }

#   undef HAS_FIELD

    const char **extendedMethodTypes() const {
        return hasExtendedMethodTypesField() ? _extendedMethodTypes : nil;
    }

    property_list_t *classProperties() const {
        return hasClassPropertiesField() ? _classProperties : nil;
    }
};
struct protocol_list_t {
    // count is 64-bit by accident. 
    uintptr_t count;
    protocol_ref_t list[0]; // variable-size

    size_t byteSize() const {
        return sizeof(*this) + count*sizeof(list[0]);
    }

    protocol_list_t *duplicate() const {
        return (protocol_list_t *)memdup(this, this->byteSize());
    }

    typedef protocol_ref_t* iterator;
    typedef const protocol_ref_t* const_iterator;

    const_iterator begin() const {
        return list;
    }
    iterator begin() {
        return list;
    }
    const_iterator end() const {
        return list + count;
    }
    iterator end() {
        return list + count;
    }
};
class protocol_array_t : public list_array_tt<protocol_ref_t, protocol_list_t> {
    typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;

 public:
    protocol_array_t duplicate() {
        return Super::duplicate<protocol_array_t>();
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末图谷,一起剝皮案震驚了整個濱河市共苛,隨后出現(xiàn)的幾起案子判没,更是在濱河造成了極大的恐慌,老刑警劉巖隅茎,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澄峰,死亡現(xiàn)場離奇詭異,居然都是意外死亡辟犀,警方通過查閱死者的電腦和手機(jī)俏竞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堂竟,“玉大人魂毁,你說我怎么就攤上這事〕鲟冢” “怎么了席楚?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長税稼。 經(jīng)常有香客問我烦秩,道長,這世上最難降的妖魔是什么郎仆? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任只祠,我火速辦了婚禮,結(jié)果婚禮上扰肌,老公的妹妹穿的比我還像新娘抛寝。我一直安慰自己,他們只是感情好曙旭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布盗舰。 她就那樣靜靜地躺著,像睡著了一般桂躏。 火紅的嫁衣襯著肌膚如雪岭皂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天沼头,我揣著相機(jī)與錄音爷绘,去河邊找鬼。 笑死进倍,一個胖子當(dāng)著我的面吹牛土至,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猾昆,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼陶因,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了垂蜗?” 一聲冷哼從身側(cè)響起楷扬,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤解幽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烘苹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲株,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年镣衡,在試婚紗的時候發(fā)現(xiàn)自己被綠了霜定。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡廊鸥,死狀恐怖望浩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惰说,我是刑警寧澤磨德,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站吆视,受9級特大地震影響典挑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揩环,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望幅虑。 院中可真熱鬧丰滑,春花似錦、人聲如沸倒庵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擎宝。三九已至郁妈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绍申,已是汗流浹背噩咪。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留极阅,地道東北人胃碾。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像筋搏,于是被迫代替她去往敵國和親仆百。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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