OC底層原理(九):runtime之isa

之前在OC底層原理(二)這章內(nèi)容中有講過 instance對象的isa & ISA_MASK 指向class對象改含,class對象的isa & ISA_MASK指向meta-class對象,但是并沒有詳細講isa的內(nèi)部結(jié)構(gòu)

isa內(nèi)部結(jié)構(gòu)

在arm64架構(gòu)之前亲铡,isa就是一個普通指針,存儲著class對象或meta-class對象的地址
在arm64架構(gòu)之后绎速,蘋果用union結(jié)構(gòu)優(yōu)化了isa指針亩钟,讓isa能夠存儲更多的信息
在objc4源碼中搜索isa_t绢涡,其結(jié)構(gòu)如下

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);
};

# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
#   if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#     define ISA_MASK        0x007ffffffffffff8ULL
#     define ISA_MAGIC_MASK  0x0000000000000001ULL
#     define ISA_MAGIC_VALUE 0x0000000000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 0
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t shiftcls_and_sig  : 52;                                      \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 8
#     define RC_ONE   (1ULL<<56)
#     define RC_HALF  (1ULL<<7)
#   else
#     define ISA_MASK        0x0000000ffffffff8ULL
#     define ISA_MAGIC_MASK  0x000003f000000001ULL
#     define ISA_MAGIC_VALUE 0x000001a000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 1
#     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
#     define RC_ONE   (1ULL<<45)
#     define RC_HALF  (1ULL<<18)
#   endif

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_HAS_CXX_DTOR_BIT 1
#   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
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

// SUPPORT_PACKED_ISA
#endif

精簡一下代碼如下

union isa_t {
    uintptr_t bits;
    struct {
        //arm64
        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
    };
};

union 共用體

那么什么是共用體呢牲剃,就是共用體內(nèi)部的成員變量都共享一塊內(nèi)存區(qū)域

union testUnion {
    int a;
    int b;
    int c;
};

struct testStruct {
    int a;
    int b;
    int c;
};

我們通過union和struct來做對比

struct和union內(nèi)存結(jié)構(gòu)示意圖
內(nèi)存示意圖.png

我們通過代碼來驗證下
創(chuàng)建一個命令行項目,在main函數(shù)中寫入如下代碼雄可,并運行

union testUnion {
    int a;
    int b;
    int c;
};

struct testStruct {
    int a;
    int b;
    int c;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        union testUnion testUnion;
        testUnion.a = 10;
        testUnion.b = 11;
        testUnion.c = 12;
        NSLog(@"union : %d  %d   %d", testUnion.a, testUnion.b, testUnion.c);
        
        struct testStruct testStruct;
        testStruct.a = 10;
        testStruct.b = 11;
        testStruct.c = 12;
        NSLog(@"struct : %d  %d   %d", testStruct.a, testStruct.b, testStruct.c);
    }
    return 0;
}

輸出結(jié)果如下


輸出結(jié)果.png

可以看到struct的存儲的值互不影響凿傅,而union里的a、b的值被c的值給覆蓋了
我們回到isa_t的結(jié)構(gòu)中來看

union isa_t {
    uintptr_t bits;
    struct {
        //arm64
        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
    };
};

可以看出 uintptr_t bits 和 struct 共用一個內(nèi)存滞项,那么struct 內(nèi)部又是什么呢
這里要引申出另一個概念狭归,位域


位域

在了解位域之前我們先設想一個場景夭坪,我們需要存儲三個字段文判,高富帥,bool類型
按照我們原來的寫法我們會申明三個bool屬性來保存

@interface ZJPerson : NSObject
@property(nonatomic, assign) bool isTall;
@property(nonatomic, assign) bool isHandsome;
@property(nonatomic, assign) bool isRich;
@end

本來簡簡單單存三個bool變量室梅,用了三個字節(jié)有點浪費內(nèi)存
簡簡單單的0和1可以用1個字節(jié)的3bit來存儲戏仓,這就涉及位域數(shù)據(jù)結(jié)構(gòu)了
位域的寫法和結(jié)構(gòu)體類似

struct testWeiYu {
    char a : 1;
    char b : 1;
    char c : 1;
};

這代表著a占用1bit疚宇,不是字節(jié)(一字節(jié)等于8bit),b占用1bit赏殃,c占用1bit
比如testWeiYu分配了一個字節(jié)的內(nèi)存敷待,其內(nèi)存示意圖如下


內(nèi)存示意圖.png

我們通過代碼來驗證下

struct testWeiYu {
    char a : 1;
    char b : 1;
    char c : 1;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        struct testWeiYu testWeiYu;
        testWeiYu.a = 0;
        testWeiYu.b = 1;
        testWeiYu.c = 1;
        NSLog(@"weiYu : %d  %d   %d", !!testWeiYu.a, !!testWeiYu.b, !!testWeiYu.c);
        
    }
    return 0;
}

然后在NSLog處打上斷點,運行
然后在控制臺查看testWeiYu的內(nèi)存地址仁热,再窺探這個地址存儲的數(shù)據(jù)
使用如下代碼查看其地址

p/x &(testWeiYu)

其輸出如下


地址.png

然后再用如下代碼窺探其存儲數(shù)據(jù)

x 0x00007ffeefbff528

其輸出如下


數(shù)據(jù).png

06即為其存儲的數(shù)據(jù)
我們講0x06轉(zhuǎn)換成二進制數(shù)據(jù)如下


二進制.png

可以看到存儲的值為0b110
按照上面的內(nèi)存示意圖就是
內(nèi)存示意圖.png

我們看下最后的輸出


輸出結(jié)果.png

可以看到確實實現(xiàn)了1個字節(jié)存儲3個bool變量
另外細心的朋友可能也看到了NSLog里的兩個!!
NSLog(@"weiYu : %d  %d   %d", !!testWeiYu.a, !!testWeiYu.b, !!testWeiYu.c);

為什么要這樣寫呢
這又要涉及另外一個概念了榜揖,原碼、反碼和補碼

原碼抗蠢、反碼举哟、補碼

原碼

原碼就是第一位為符號位,0代表正迅矛,1代表負妨猩,別的位表示數(shù)值
比如+1原碼為

0000 0001

-1原碼為

1000 0001

所以8位2進制的原碼取值范圍為[-127, +127]
但是如果計算(+1) + (-1)的話,原碼計算結(jié)果如下

0000 0001 + 1000 0001 = 1000 0002 

結(jié)果為-2,所以基于這種情況秽褒,反碼被發(fā)明了出來

反碼
如果為正數(shù)壶硅,原碼與反碼一致
如果為負數(shù),反碼為原碼除符號位外每一位取反
反碼就是在原碼的基礎(chǔ)上销斟,每一位取反
如原碼中-0的表示為

1000 0000

反碼為

1111 1111

原碼中+0的表示為

0000 0000

反碼為

0000 0000

原碼中+1的表示為

0000 0001

反碼為

0000 0001

原碼中-1的表示為

1000 0001

反碼為

1111 1110

所以(+1) + (-1)等于-0

1000 0001 + 1111 1110 = 1111 1111

雖然反碼解決了正負相加等于0的問題庐椒,卻存在兩個0,+0和-0
所以蚂踊,再反碼的基礎(chǔ)上又提出了補碼的概念

補碼
如果為正數(shù)扼睬,反碼與補碼一致
如果為負數(shù),補碼為反碼+1,并丟棄最高位
反碼中-0表示為

1111 1111

補碼中-0表示為

1111 1111 + 0000 0001 = 1 0000 0000
丟棄最高位為 
0000 0000

所以-0和+0表示一樣

原碼中-1的表示為

1000 0001

反碼為

1111 1110

補碼為

1111 1111

計算機采用的是補碼來存儲值


回到主題

NSLog(@"weiYu : %d  %d   %d", !!testWeiYu.a, !!testWeiYu.b, !!testWeiYu.c);

為什么要這樣寫呢悴势?
我們從反面角度來考慮窗宇,如果不這么寫會出現(xiàn)什么情況

NSLog(@"weiYu : %d  %d   %d", testWeiYu.a, testWeiYu.b, testWeiYu.c);

我們運行看下結(jié)果


結(jié)果.png

為什么會打印-1呢?
因為char b是有符號類型特纤,而char b又只占用了1位军俊,所以會將1當作符號位負數(shù)來處理,計算機存儲值用的是補碼捧存,所以存儲的值為1取反再加上1粪躬,最后結(jié)果還是1,再加上符號位就打印出-1
我們也可以通過加a昔穴、b镰官、c申明為無符號類型來解決這個問題

struct testWeiYu {
    unsigned char a : 1;
    unsigned char b : 1;
    unsigned char c : 1;
};

共用體+位域
在了解了共用體和位域的概念后,我們回過頭看isa_t的結(jié)構(gòu)

union isa_t {
    uintptr_t bits;
    struct {
        //arm64
        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
    };
};

可以看到isa其實就是運用了共用體+位域的數(shù)據(jù)結(jié)構(gòu)來做的優(yōu)化
那么這種結(jié)構(gòu)的值的存與取又如何實現(xiàn)呢吗货?
我們用代碼來實現(xiàn)
申明一個ZJPerson類泳唠,它需要存儲三個bool變量isTall,isRich宙搬,isHandsome笨腥,使用共用體+位域技術(shù)來實現(xiàn)存值與取值

//.h
@interface ZJPerson : NSObject
- (void)setTall:(BOOL)tall;
- (BOOL)tall;
- (void)setRich:(BOOL)rich;
- (BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)handsome;
@end

//.m
@interface ZJPerson()
{
    union man {
        char bits;
        struct {
            unsigned char isTall;
            unsigned char isRich;
            unsigned char isHandsome;
        };
    }man;
}
@end

@implementation ZJPerson

- (void)setTall:(BOOL)tall {
    
}

- (BOOL)tall {
    return NO;
}

- (void)setRich:(BOOL)rich {
    
}

- (BOOL)rich {
    return NO;
}

- (void)setHandsome:(BOOL)handsome {
    
}

- (BOOL)handsome {
    return NO;
}

@end

我們首先考慮取值
比如tall為YES拓哺,rich為NO,handSome為YES脖母,那么共用體存的值應該如下

0000 0101

想要取tall的話士鸥,需要用這個值& 0000 0001
計算過程如下

 0000 0101
&0000 0001
=0000 0001

同理,取rich谆级,需要用這個值& 0000 0010
同理烤礁,取handSome,需要用這個值& 0000 0100
可以看到肥照,取最右邊的一位需要&(1<<0)
取右邊的第二位需要&(1<<1)
取右邊的第三位需要&(1<<2)
那么我們來更新.m文件中的代碼鸽凶,申明三位掩碼來分別取tall,rich和handsome的值

#define ZJTallMask (1<<0)
#define ZJRichMask (1<<1)
#define ZJHandSome (1<<2)
@interface ZJPerson()
{
    union man {
        char bits;
        struct {
            unsigned char isTall;
            unsigned char isRich;
            unsigned char isHandsome;
        };
    }man;
}
@end

@implementation ZJPerson

- (void)setTall:(BOOL)tall {
    
}

- (BOOL)tall {
    return !!(man.bits & ZJTallMask);
}

- (void)setRich:(BOOL)rich {
    
}

- (BOOL)rich {
    return !!(man.bits & ZJRichMask);
}

- (void)setHandsome:(BOOL)handsome {
    
}

- (BOOL)handsome {
    return !!(man.bits & ZJHandSome);
}

@end

這樣建峭,我們?nèi)≈稻屯瓿闪?br> 為什么要加上!!玻侥,是因為
tall值取出來是0000 0001,轉(zhuǎn)換成10進制就是1
rich值取出來是0000 0010亿蒸,轉(zhuǎn)換成10進制就是2
handsome值取出來是0000 0100凑兰,轉(zhuǎn)換成10進制就是4
取出來的值并不是bool類型,而取兩次反就可以得到正確的bool值
比如拿handsome的值4來做例子边锁,!4就是0姑食,!0就是1,1就是YES
再比如拿0來做例子茅坛,!0就是1音半,!1就是0,0就是NO
通過兩次取反運算就可以得到我們想要的bool值

我們繼續(xù)研究存值
如果我們要將tall的值設置為YES
就需要將共用體的值 | ZJTallMask
其運算過程如下

//原來tall值為NO
  0000 0110
| 0000 0001
= 0000 0111

//原來tall值為YES
  0000 0111
| 0000 0001
= 0000 0111

如果我們要將tall的值設置為NO贡蓖,那么|運算則不能完成這個任務了
那么要怎么實現(xiàn)存值為NO呢曹鸠?
首先,我們需要將掩碼按位取反斥铺,然后用共用體的值&這個取反的值就可以了

//將掩碼按位取反
~ZJTallMask//值為1111 1110

//然后再做&運算
//原來tall值為NO
  0000 0110
| 1111 1110
= 0000 0110

//原來tall值為YES
  0000 0111
| 1111 1110
= 0000 0111

這樣NO的存儲也完成了
我們完善代碼如下

#define ZJTallMask (1<<0)
#define ZJRichMask (1<<1)
#define ZJHandSome (1<<2)
@interface ZJPerson()
{
    union man {
        char bits;
        struct {
            unsigned char isTall;
            unsigned char isRich;
            unsigned char isHandsome;
        };
    }man;
}
@end

@implementation ZJPerson

- (void)setTall:(BOOL)tall {
    if (tall) {
        man.bits |= ZJTallMask;
    }else {
        man.bits &= ~ZJTallMask;
    }
}

- (BOOL)tall {
    return !!(man.bits & ZJTallMask);
}

- (void)setRich:(BOOL)rich {
    if (rich) {
        man.bits |= ZJRichMask;
    }else {
        man.bits &= ~ZJRichMask;
    }
}

- (BOOL)rich {
   return !!(man.bits & ZJRichMask);
}

- (void)setHandsome:(BOOL)handsome {
    if (handsome) {
        man.bits |= ZJHandSome;
    }else {
        man.bits &= ~ZJHandSome;
    }
}

- (BOOL)handsome {
    return !!(man.bits & ZJHandSome);
}

@end

這樣彻桃,通過共用體+位域?qū)崿F(xiàn)三個bool變量的存取功能就完成了
我們測試一下

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        ZJPerson *person = [[ZJPerson alloc]init];
        person.tall = YES;
        person.rich = YES;
        person.handsome = YES;
        NSLog(@"tall:%d rich:%d handsome:%d", person.tall, person.rich, person.handsome);
    }
    return 0;
}
結(jié)果.png

之前我們說的arm64之后需要isa指針&Mask獲取class對象地址或者meta-class對象地址
我們看看掩碼的值

define ISA_MASK        0x0000000ffffffff8ULL

將其轉(zhuǎn)換成2進制


二進制.png

再看isa_t結(jié)構(gòu)

union isa_t {
    uintptr_t bits;
    struct {
        //arm64
        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
    };
};

isa & Mask之后就是將shiftcls的值取出來,而shiftcls里存儲的就是class對象晾蜘、meta-class對象的地址


isa_t其他字段的釋義

union isa_t {
    uintptr_t bits;
    struct {
        //arm64
        // 是否開啟 isa 指針優(yōu)化
        uintptr_t nonpointer        : 1;                                       \
        // 是否有設置過關(guān)聯(lián)對象邻眷,如果沒有,釋放時會更快
        uintptr_t has_assoc         : 1;                                       \
        // 是否有 C++ 的析構(gòu)函數(shù)(.cxx_destruct)剔交,如果沒有肆饶,釋放時會更快
        uintptr_t has_cxx_dtor      : 1;                                       \
        存儲著Class、Meta-Class對象的內(nèi)存地址信息
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        // 用于在調(diào)試時分辨對象是否未完成初始化
        uintptr_t magic             : 6;                                       \
        // 是否有被弱引用指向過岖常,如果沒有驯镊,釋放時會更快
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        // 引用計數(shù)器是否過大無法存儲在 isa 中。如果為 1,那么引用計數(shù)會存儲在一個叫 SideTable 的類的屬性中
        uintptr_t has_sidetable_rc  : 1;                                       \
        // 里面存儲的值是引用計數(shù) - 1
        uintptr_t extra_rc          : 19
    };
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阿宅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子笼蛛,更是在濱河造成了極大的恐慌洒放,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滨砍,死亡現(xiàn)場離奇詭異往湿,居然都是意外死亡,警方通過查閱死者的電腦和手機惋戏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門领追,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人响逢,你說我怎么就攤上這事绒窑。” “怎么了舔亭?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵些膨,是天一觀的道長。 經(jīng)常有香客問我钦铺,道長订雾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任矛洞,我火速辦了婚禮洼哎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沼本。我一直安慰自己噩峦,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布抽兆。 她就那樣靜靜地躺著壕探,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郊丛。 梳的紋絲不亂的頭發(fā)上李请,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音厉熟,去河邊找鬼导盅。 笑死,一個胖子當著我的面吹牛揍瑟,可吹牛的內(nèi)容都是我干的白翻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼滤馍!你這毒婦竟也來了岛琼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤巢株,失蹤者是張志新(化名)和其女友劉穎槐瑞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁苞,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡困檩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了那槽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼沿。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骚灸,靈堂內(nèi)的尸體忽然破棺而出尚卫,到底是詐尸還是另有隱情回还,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站姆蘸,受9級特大地震影響邪码,放射性物質(zhì)發(fā)生泄漏宾尚。R本人自食惡果不足惜孝情,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望著恩。 院中可真熱鬧院尔,春花似錦、人聲如沸喉誊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伍茄。三九已至栋盹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敷矫,已是汗流浹背例获。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留曹仗,地道東北人榨汤。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像怎茫,于是被迫代替她去往敵國和親收壕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

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