類的結(jié)構(gòu)分析

前言

書接上回isa結(jié)構(gòu)分析,我們得知,對(duì)象的isa指針指向類,確切的說,是isa指針的shiftcls位域中指向類對(duì)象Class的地址。那OC層的Class在底層C、C++層面里,所對(duì)應(yīng)的結(jié)構(gòu)體里,有哪些成員變量和函數(shù)呢?下面將大致分析下類的底層結(jié)構(gòu)。

isa指向

首先我們來看看isa指針的指向問題,我們都知道,對(duì)象的isa指針指向類對(duì)象Class,那類對(duì)象Class的isa指針指向哪里呢?我們先通過在lldb中打印地址來查看詳情。

我們先定義一個(gè)類LGPerson

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGPerson : NSObject

@end

NS_ASSUME_NONNULL_END

//--------------------分割線----------------------

#import "LGPerson.h"

@implementation LGPerson

@end

在程序入口main.m中生成LGperson對(duì)象

#import <Foundation/Foundation.h>
#import "LGPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        LGPerson *person = [[LGPerson alloc] init];
        

    }
    return 0;
}

打斷點(diǎn)查看person對(duì)象的地址信息

(lldb) po person
<LGPerson: 0x10124dcb0>

(lldb) p person
(LGPerson *) $2 = 0x000000010124dcb0

我們常用的LLDB指令po,p,他倆的區(qū)別如下:

指令名稱 釋義
p 是 expression 的別名,打印某個(gè)東西,可以是變量和表達(dá)式
po 一般用于打印對(duì)象,是 expression -O 的別名,會(huì)輸出對(duì)應(yīng)的值

再來看看另外兩個(gè)LLDB指令

指令名稱 釋義
p/x 以16進(jìn)制形式讀取對(duì)象的地址或者值
x/4gx 以16進(jìn)制形式讀取4段8位的內(nèi)存空間里面存儲(chǔ)的地址或者值

用上面指令看看person對(duì)象的地址

(lldb) p/x person
(LGPerson *) $4 = 0x000000010124dcb0

(lldb) x/4gx person
0x10124dcb0: 0x001d8001000020e9 0x0000000000000000
0x10124dcc0: 0x0000000080080000 0x00007fff8d07d208

首先第一段0x10124dcb0: 0x001d8001000020e9這個(gè)就是person對(duì)象的isa指針地址,再查看isa指針中位域shiftcls的值,有個(gè)小技巧,通過和ISA_MASK進(jìn)行與運(yùn)算,過濾出shiftcls的地址值

在查看isa_t結(jié)構(gòu)體中,可以找到ISA_MASK的宏定義(以x86_64為例)
define ISA_MASK 0x00007ffffffffff8ULL

(lldb) p/x 0x001d8001000020e9 & 0x00007ffffffffff8ULL
(unsigned long long) $6 = 0x00000001000020e8
(lldb) po $6
LGPerson

驗(yàn)證得出:person對(duì)象isa指針的位域shiftcls中指向的是類LGPerson的地址。
同理,我們?cè)诶^續(xù)看看類LGPerson對(duì)象的isa指針位域shiftcls中存放的什么值?

// 以4段方式查看LGPeson類對(duì)象地址
(lldb) x/4gx $6
0x1000020e8: 0x00000001000020c0 0x00000001003ef190
0x1000020f8: 0x0000000101063830 0x0004801000000007

// 首地址isa & ISA_MASK,得到位域shiftcls中存放的值
(lldb) p/x 0x00000001000020c0 & 0x00007ffffffffff8ULL
(unsigned long long) $7 = 0x00000001000020c0

// 打印shiftcls值
(lldb) po $7
LGPerson

發(fā)現(xiàn),類對(duì)象LGPerson的isa指針中shiftcls存儲(chǔ)的值也是LGPerson,但是$6 = 0x00000001000020e8$7 = 0x00000001000020c0的地址值顯然不同,說明$7明顯不是指向類對(duì)象LGPerson它自己,但是打印出的值是LGPerson,這就是蘋果提出的一個(gè)概念元類(meta class)

驗(yàn)證得出,類對(duì)象的isa指向與對(duì)象的isa指向不同,對(duì)象的isa指向類,而類的isa指向元類(蘋果爸爸定義為meta class)。

接著我們繼續(xù)查看$7的isa指針的位域shiftcls中存放的地址值是什么?

(lldb) x/4gx $7
0x1000020c0: 0x00000001003ef140 0x00000001003ef140
0x1000020d0: 0x0000000101063b40 0x0003e03100000007
(lldb) p/x 0x00000001003ef140 & 0x00007ffffffffff8ULL
(unsigned long long) $8 = 0x00000001003ef140
(lldb) po $8
NSObject

$7指針的位域shiftcls中存放的地址$8值是NSObject。接著看$8shiftcls的值,如下:

(lldb) x/4gx $8
0x1003ef140: 0x00000001003ef140 0x00000001003ef190
0x1003ef150: 0x0000000100640ff0 0x0004e03100000007

(lldb) p/x 0x00000001003ef140 & 0x00007ffffffffff8ULL
(unsigned long long) $9 = 0x00000001003ef140

(lldb) po $9
NSObject

$8 = 0x00000001003ef1400x1003ef140: 0x00000001003ef140(isa指針地址)一樣,shiftcls打印出的地址也是0x00000001003ef140,值也是NSObject,說明$8isa指針指向自己。
那這個(gè)$9的值NSObject,是不是我們通常的基類NSObject類呢?接下來驗(yàn)證一下

(lldb) p/x NSObject.class
(Class) $10 = 0x00000001003ef190 NSObject

$10的地址與$9不同,說明不是基類。

驗(yàn)證得出,元類的isa指針指向NSObject根元類(蘋果爸爸定義為root meta class)。

上一張isa指向經(jīng)典圖:

isa指向.png

以上通過LLDB中的指令查看地址,得出isa指針的指向是:

對(duì)象 --> 類 --> 元類 --> 根元類-->根元類 指向自身

特殊問題:類信息在內(nèi)存中存在幾份

以上我們得知,類對(duì)象的isa指針指向元類,那類對(duì)象Class的isa指針指向的地址是同一個(gè)還是不同的?我們還是用代碼驗(yàn)證一下:

Class class1 = [LGPerson class];
Class class2 = [LGPerson alloc].class;
Class class3 = object_getClass([LGPerson alloc]);
NSLog(@"\n%p\n%p\n%p", class1, class2, class3);

編譯運(yùn)行后得出:

2020-09-14 17:23:07.926664+0800 KCObjc[9752:305959] 
0x100002100
0x100002100
0x100002100

地址一模一樣,說明類信息在內(nèi)存中只存在一份。

類Class結(jié)構(gòu)分析

言歸正傳,分析了isa指針指向問題后,現(xiàn)在再來回頭看看Class結(jié)構(gòu)的具體情況:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

Class對(duì)應(yīng)底層結(jié)構(gòu)體是objc_class,再來看看objc_class

struct objc_class : objc_object {
    // 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

    class_rw_t *data() const {
        return bits.data();
    }
    
    // 省略部分代碼.......
}
  • objc_class繼承objc_object
  • superclass是父類Class
  • cache_t cache緩存
  • class_data_bits_t bits成員

cache_t結(jié)構(gòu)體

// 只列舉了成員變量
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
    
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;
}

// 省略部分代碼...
  • CACHE_MASK_STORAGE_OUTLINED這種緩存模式,則占用內(nèi)存大小是:bucket_t +mask_t+uint16_t+uint16_t

  • CACHE_MASK_STORAGE這種緩存模式,則占用內(nèi)存大小是:uintptr_t+mask_t+uint16_t+uint16_t

uintptr_t內(nèi)存大小
#ifndef _UINTPTR_T
#define _UINTPTR_T
typedef unsigned long           uintptr_t;
#endif /* _UINTPTR_T */

uintptr_t是long類型,8個(gè)字節(jié)

bucket_t內(nèi)存大小
struct bucket_t {
private:
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    // SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
    explicit_atomic<uintptr_t> _imp;
    explicit_atomic<SEL> _sel;
#else
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
#endif
// 省略部分代碼...
}

不管是__arm64__還是其它,都是uintptr_t+_sel指針 --> 4 + 8 = 12

mask_t內(nèi)存大小
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif

一目了然,即整型,大小為4。

綜上所述,

  • CACHE_MASK_STORAGE_OUTLINED模式下,explicit_atomic<struct bucket_t *> _buckets是結(jié)構(gòu)體bucket_t的指針 8,+ mask_t 4 + 2 * uint16_t 2 = 16
  • CACHE_MASK_STORAGE模式下是8+4+2+2,也是16;

結(jié)論cache_t 所占內(nèi)存大小:16

重點(diǎn) class_data_bits_t

結(jié)構(gòu)體class_data_bits_t大致分為幾個(gè)部分:

  1. bits操作相關(guān),為私有函數(shù),包含獲取、設(shè)置、清空等操作
 // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) const
    {
        return bits & bit;
    }

    // Atomically set the bits in `set` and clear the bits in `clear`.
    // set and clear must not overlap.
    void setAndClearBits(uintptr_t set, uintptr_t clear)
    {
       // ...省略部分代碼
    }

    void setBits(uintptr_t set) {
        __c11_atomic_fetch_or((_Atomic(uintptr_t) *)&bits, set, __ATOMIC_RELAXED);
    }

    void clearBits(uintptr_t clear) {
        __c11_atomic_fetch_and((_Atomic(uintptr_t) *)&bits, ~clear, __ATOMIC_RELAXED);
    }

2.重要部分data(),返回結(jié)構(gòu)體class_rw_t *指針類型,里面存放了屬性列表,實(shí)例方法列表,協(xié)議列表等信息

class_rw_t* data() const {
    return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData) {
    // ...省略部分代碼
}

3.ro data相關(guān)

// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
const class_ro_t *safe_ro() {
    // ...省略部分代碼
}

void setClassArrayIndex(unsigned Idx) {
#if SUPPORT_INDEXED_ISA
    // 0 is unused as then we can rely on zero-initialisation from calloc.
    ASSERT(Idx > 0);
    data()->index = Idx;
#endif
}

unsigned classArrayIndex() {
#if SUPPORT_INDEXED_ISA
    return data()->index;
#else
    return 0;
#endif
}

4.swift相關(guān)

bool isAnySwift() {
    return isSwiftStable() || isSwiftLegacy();
}

bool isSwiftStable() {
    return getBit(FAST_IS_SWIFT_STABLE);
}
void setIsSwiftStable() {
    setAndClearBits(FAST_IS_SWIFT_STABLE, FAST_IS_SWIFT_LEGACY);
}

bool isSwiftLegacy() {
    return getBit(FAST_IS_SWIFT_LEGACY);
}
void setIsSwiftLegacy() {
    setAndClearBits(FAST_IS_SWIFT_LEGACY, FAST_IS_SWIFT_STABLE);
}

// fixme remove this once the Swift runtime uses the stable bits
bool isSwiftStable_ButAllowLegacyForNow() {
    return isAnySwift();
}

_objc_swiftMetadataInitializer swiftMetadataInitializer() {
    // This function is called on un-realized classes without
    // holding any locks.
    // Beware of races with other realizers.
    return safe_ro()->swiftMetadataInitializer();
}        
class_rw_t內(nèi)部信息

class_rw_t里存放了類的實(shí)例方法列表,屬性列表,協(xié)議列表等信息,如何查看呢?下面舉例說明。

首先在LGPerson類中添加屬性和方法

@interface LGPerson : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) NSString *nikeName;

- (void)sayHello;
+ (void)sayByebye;

@end

查看LGPerson類的地址

(lldb) p/x LGPerson.class
(Class) $0 = 0x00000001000021f8
// 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

根據(jù)上面objc_class的結(jié)構(gòu)分析,要獲取class_data_bits_t bits的地址,需要位移isa8位+Class8位 + cache_t16位 = 32位

0x00000001000021f8 + 0x20 = 0x0000000100002218

獲取class_data_bits_t

(lldb) p (class_data_bits_t *)0x0000000100002218
(class_data_bits_t *) $1 = 0x0000000100002218

獲取data()

(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000101142b80
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294975608
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
1. 查看屬性列表信息
(lldb) p $3.properties()
(const property_array_t) $4 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100002188
      arrayAndFlag = 4294975880
    }
  }
}

(lldb) p $4.list
(property_list_t *const) $5 = 0x0000000100002188

(lldb) p *$5
(property_list_t) $6 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 2
    first = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
  }
}

count = 2說明有2個(gè)屬性,再仔細(xì)查看這兩個(gè)屬性:


(lldb) p $6.get(0)
(property_t) $7 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
(lldb) p $6.get(1)
(property_t) $8 = (name = "nikeName", attributes = "T@\"NSString\",C,N,V_nikeName")
(lldb) p $6.get(2)
Assertion failed: (i < count), function get, file /Users/Aron/objc4_debug/objc4-781/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

name nikeName都有了,當(dāng)獲取第三個(gè)時(shí)越界,Assertion failed: (i < count)表示下標(biāo)必須小于count:2

2. 查看方法列表信息

同理,查看方法列表methods()

(lldb) p $3.methods()
(const method_array_t) $9 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x00000001000020c0
      arrayAndFlag = 4294975680
    }
  }
}
(lldb) p $9.list
(method_list_t *const) $10 = 0x00000001000020c0

(lldb) p *$10
(method_list_t) $11 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 5
    first = {
      name = "nikeName"
      types = 0x0000000100000f98 "@16@0:8"
      imp = 0x0000000100000e20 (KCObjc`-[LGPerson nikeName])
    }
  }
}

count = 5說明有5個(gè)方法,明明我只定義了2個(gè)方法,- (void)sayHello;+ (void)sayByebye;,再一一打印看看是哪5個(gè)方法:

(lldb) p $11.get(0)
(method_t) $12 = {
  name = "nikeName"
  types = 0x0000000100000f98 "@16@0:8"
  imp = 0x0000000100000e20 (KCObjc`-[LGPerson nikeName])
}
(lldb) p $11.get(1)
(method_t) $13 = {
  name = "setNikeName:"
  types = 0x0000000100000fa0 "v24@0:8@16"
  imp = 0x0000000100000e50 (KCObjc`-[LGPerson setNikeName:])
}
(lldb) p $11.get(2)
(method_t) $14 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f90 "v16@0:8"
  imp = 0x0000000100000d90 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $11.get(3)
(method_t) $15 = {
  name = "name"
  types = 0x0000000100000f98 "@16@0:8"
  imp = 0x0000000100000dd0 (KCObjc`-[LGPerson name])
}
(lldb) p $11.get(4)
(method_t) $16 = {
  name = "setName:"
  types = 0x0000000100000fa0 "v24@0:8@16"
  imp = 0x0000000100000df0 (KCObjc`-[LGPerson setName:])
}

原來是2個(gè)屬性的get set方法和一個(gè)析構(gòu)函數(shù)cxx_destruct,那定義的2個(gè)方法去哪了?

補(bǔ)坑:
原來之前只定義了方法,并未實(shí)現(xiàn)sayHello sayByebye這2個(gè)方法。。。

先補(bǔ)上方法實(shí)現(xiàn),重新編譯

@implementation LGPerson

- (void)sayHello {
    
}

+ (void)sayByebye {
    
}

@end

再打印方法列表看看:

(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 6
    first = {
      name = "sayHello"
      types = 0x0000000100000f35 "v16@0:8"
      imp = 0x0000000100000d60 (KCObjc`-[LGPerson sayHello])
    }
  }
}

發(fā)現(xiàn)count = 6,不是7個(gè),看看少了哪個(gè)?

(lldb) p $6.get(0)
(method_t) $7 = {
  name = "sayHello"
  types = 0x0000000100000f35 "v16@0:8"
  imp = 0x0000000100000d60 (KCObjc`-[LGPerson sayHello])
}
(lldb) p $6.get(1)
(method_t) $8 = {
  name = "nikeName"
  types = 0x0000000100000f49 "@16@0:8"
  imp = 0x0000000100000e00 (KCObjc`-[LGPerson nikeName])
}
(lldb) p $6.get(2)
(method_t) $9 = {
  name = "setNikeName:"
  types = 0x0000000100000f51 "v24@0:8@16"
  imp = 0x0000000100000e30 (KCObjc`-[LGPerson setNikeName:])
}
(lldb) p $6.get(3)
(method_t) $10 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f35 "v16@0:8"
  imp = 0x0000000100000d70 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $6.get(4)
(method_t) $11 = {
  name = "name"
  types = 0x0000000100000f49 "@16@0:8"
  imp = 0x0000000100000db0 (KCObjc`-[LGPerson name])
}
(lldb) p $6.get(5)
(method_t) $12 = {
  name = "setName:"
  types = 0x0000000100000f51 "v24@0:8@16"
  imp = 0x0000000100000dd0 (KCObjc`-[LGPerson setName:])
}

發(fā)現(xiàn)少了sayByebye,這是類方法,根據(jù)isa指針走位分析,實(shí)例方法存儲(chǔ)在類中,那類方法應(yīng)該存儲(chǔ)在元類中。去元類里看看:

(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002230

(lldb) x/4gx 0x0000000100002230
0x100002230: 0x0000000100002208 0x00000001003f0190
0x100002240: 0x000000010142d880 0x0001802400000003

LGPersonisa指針地址是0x0000000100002208,偏移32位獲取class_data_bits_t,即0x0000000100002228,再打印出這個(gè)地址對(duì)應(yīng)的方法列表信息:

(lldb) p (class_data_bits_t *)0x0000000100002228
(class_data_bits_t *) $1 = 0x0000000100002228
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010142d7e0
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294975536
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff8dcd5c60
}
(lldb) p $3.methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002078
      arrayAndFlag = 4294975608
    }
  }
}
(lldb) p $4.list
(method_list_t *const) $5 = 0x0000000100002078
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "sayByebye"
      types = 0x0000000100000f35 "v16@0:8"
      imp = 0x0000000100000d50 (KCObjc`+[LGPerson sayByebye])
    }
  }
}

count = 1只有1個(gè)方法sayByebye給自己一個(gè)666,哈哈!印證了isa的走位!

補(bǔ)充:objc_class繼承objc_object
struct objc_class : objc_object {
    // 省略部分代碼.......
}

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

分析:

  • 結(jié)構(gòu)體類型objc_class 繼承自objc_object類型,其中objc_object也是一個(gè)結(jié)構(gòu)體,且有一個(gè)isa屬性,所以objc_class也擁有了isa屬性;
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
  • NSObject中的isa在底層是Class類型,而Class的底層編碼來自 objc_class類型,所以NSObject也擁有了isa指針

  • NSObject類初始化一個(gè)實(shí)例對(duì)象objc,objc 滿足 objc_object 的特性(即有isa指針),主要是因?yàn)?code>isa 是NSObjectobjc_class繼承過來的,而objc_class繼承自objc_objectobjc_objectisa指針。所以對(duì)象都有一個(gè) isaisa表示指向,來自于當(dāng)前的objc_object

  • objc_object就好比一個(gè)模板,不論是Class對(duì)象還是繼承NSObject的對(duì)象,都最終指向繼承objc_object(實(shí)質(zhì)是繼承了成員 isa),所以說萬(wàn)物皆對(duì)象。

總結(jié)

1.首先從底層源碼分析了isa指針的走位,并且通過示例得以證明。

2.通過isa的走位,我們分析了類Class的底層結(jié)構(gòu)objc_class ,通過示例打印屬性列表信息和方法列表信息,證明了實(shí)例方法存儲(chǔ)在類中,類方法存儲(chǔ)在元類中

3.最后通過objc_classobjc_object的繼承關(guān)系,得出萬(wàn)物皆對(duì)象,但始終離不開isa

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374