Runtime中的 isa 結(jié)構(gòu)體

原文鏈接

有一定經(jīng)驗(yàn)的iOS開發(fā)者,或多或少的都聽過Runtime。Runtime,也就是運(yùn)行時(shí),是Objective-C語(yǔ)言的特性之一。日常開發(fā)中,可能直接和Runtime打交道的機(jī)會(huì)不多。然而,"發(fā)消息"、"消息轉(zhuǎn)發(fā)"這些名詞開發(fā)者應(yīng)該經(jīng)常聽到,這些名詞所用到的技術(shù)基礎(chǔ)就是Runtime。了解Runtime,有助于開發(fā)者深入理解Objective-C這門語(yǔ)言。

在具體了解Runtime之前,先提一個(gè)問題,什么是動(dòng)態(tài)語(yǔ)言?

Objective-C是一門動(dòng)態(tài)語(yǔ)言

使用Objective-C做iOS開發(fā)的同學(xué)一定都聽說(shuō)過一句話:Objective-C是一門動(dòng)態(tài)語(yǔ)言。動(dòng)態(tài)語(yǔ)言,肯定是和靜態(tài)語(yǔ)言相對(duì)應(yīng)的。那么,靜態(tài)語(yǔ)言有哪些特性,動(dòng)態(tài)語(yǔ)言又有哪些特性?

回顧一下大學(xué)時(shí)期,學(xué)的第一門語(yǔ)言C語(yǔ)言,學(xué)習(xí)C語(yǔ)言的過程中從來(lái)沒聽說(shuō)過運(yùn)行時(shí),也沒聽說(shuō)過什么靜態(tài)語(yǔ)言,動(dòng)態(tài)語(yǔ)言。因此我們有理由相信,C語(yǔ)言是一門靜態(tài)語(yǔ)言。

事實(shí)上也確實(shí)如此,C語(yǔ)言是一門靜態(tài)語(yǔ)言,Objective-C是一門動(dòng)態(tài)語(yǔ)言。然而,還是說(shuō)不出靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言到底有什么區(qū)別……

靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言

靜態(tài)語(yǔ)言,可以理解成在編譯期間就確定一切的語(yǔ)言。以C語(yǔ)言來(lái)舉例,C語(yǔ)言編譯后會(huì)成為一個(gè)可執(zhí)行文件。假設(shè)我們?cè)贑代碼中寫了一個(gè)hello函數(shù),并且在主程序中調(diào)用了這個(gè)hello函數(shù)。倘若在編譯期間,hello函數(shù)的入口地址相對(duì)于主程序入口地址的偏移量是0x0000abcdef(不要在意這個(gè)值,只是用來(lái)舉例),那么在執(zhí)行該程序時(shí),執(zhí)行到hello函數(shù)時(shí),一定執(zhí)行的是相對(duì)主程序入口地址偏移量為0x0000abcdef的代碼塊。也就是說(shuō),靜態(tài)語(yǔ)言,在編譯期間就已經(jīng)確定一切,運(yùn)行期間只是遵守編譯期確定的指令在執(zhí)行

作為對(duì)比,再看一下動(dòng)態(tài)語(yǔ)言,以經(jīng)常用到的Objective-C為例。假設(shè)在Objective-C中寫了hello方法,并且在主程序中調(diào)用了hello方法,也就是發(fā)送hello消息。在編譯期間,只能確定要向某個(gè)對(duì)象發(fā)送hello消息,但是具體執(zhí)行哪個(gè)內(nèi)存塊的代碼是不確定的,具體執(zhí)行的代碼需要在運(yùn)行期間才能確定

到這里,靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言的區(qū)別已經(jīng)很明顯了。靜態(tài)語(yǔ)言在編譯期間就已經(jīng)確定一切,而動(dòng)態(tài)語(yǔ)言編譯期間只能確定一部分,還有一部分需要在運(yùn)行期間才能確定。也就是說(shuō),動(dòng)態(tài)語(yǔ)言成為一個(gè)可執(zhí)行程序并能夠正確的執(zhí)行,除了需要一個(gè)編譯器外,還需要一套運(yùn)行時(shí)系統(tǒng),用于確定到底執(zhí)行哪一塊代碼。Objective-C中的運(yùn)行時(shí)系統(tǒng)內(nèi)就是Runtime。

Runtime源碼

Runtime源碼是一套用C語(yǔ)言實(shí)現(xiàn)的API,整套代碼是開源的,可以從蘋果開源網(wǎng)站上下載Runtime源碼。默認(rèn)下載的Runtime源碼是不能編譯的,通過修改配置和導(dǎo)入必要的頭文件,可以編譯成功Runtime源碼。我在github上放了編譯成功的Runtime源碼,且有我在看Runtime源碼時(shí)的一些注釋,本篇文章中的代碼也是基于此Runtime源碼。

由于Runtime源碼代碼量比較大,一篇文章介紹完Runtime源碼是不可能的。因此這篇文章主要介紹Runtime中的isa結(jié)構(gòu)體,作為Runtime的入門。

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

有經(jīng)驗(yàn)的iOS開發(fā)者可能都聽過一句話:在Objective-C語(yǔ)言中,類也是對(duì)象,且每個(gè)對(duì)象都包含一個(gè)isa指針,isa指針指向該對(duì)象所屬的類。不過現(xiàn)在Runtime中的對(duì)象定義已經(jīng)不是這樣了,現(xiàn)在使用的是isa_t類型的結(jié)構(gòu)體。每一個(gè)對(duì)象都有一個(gè)isa_t類型的結(jié)構(gòu)體isa。之前的isa指針作用是指向該對(duì)象的類,那么isa結(jié)構(gòu)體作為isa指針的替代者,是如何完成這個(gè)功能的呢?

在解決這個(gè)問題之前,我們先來(lái)看一下Runtime源碼中對(duì)象和類的定義。

objc_object

看一下Runtime中對(duì)id類型的定義

typedef struct objc_object *id;

這里的id也就是Objective-C中的id類型,代表任意對(duì)象,類似于C語(yǔ)言中的 void 。可以看到,id實(shí)際上是一個(gè)指向結(jié)構(gòu)體objc_object的指針。

再來(lái)看一下objc_object的定義,該定義位于objc-private.h文件中:

struct objc_object {
    // isa結(jié)構(gòu)體
private:
    isa_t isa;
}

結(jié)構(gòu)體中還包含一些public的方法。可以看到,對(duì)象結(jié)構(gòu)體(objc_object)中的第一個(gè)變量就是isa_t 類型的isa。關(guān)于isa_t具體是什么,后續(xù)再介紹。

Objective-C語(yǔ)言中最主要的就是對(duì)象和類,看完了對(duì)象在Runtime中的定義,再看一下類在Runtime中的定義。

objc_class

Runtime中對(duì)于Class的定義

typedef struct objc_class *Class;

Class實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針。

看一下結(jié)構(gòu)體objc_class的定義,objc_class的定義位于objc-runtime-new.h文件中

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
}

結(jié)構(gòu)體中還包含一些方法。

注意,objc_class是繼承于objc_object的,因此objc_class中也包含isa_t類型的isa。objc_class的定義可以理解成下面這樣:

struct objc_class {
    isa_t isa;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

isa的作用

上面也提到了,isa能夠使該對(duì)象找到自己所屬的類。為什么對(duì)象需要知道自己所屬的類呢?這主要是因?yàn)閷?duì)象的方法是存儲(chǔ)在該對(duì)象所屬的類中的。

這一點(diǎn)是很容易理解的,一個(gè)類可以有多個(gè)對(duì)象,倘若每個(gè)對(duì)象都含有自己能夠執(zhí)行的方法,那對(duì)于內(nèi)存來(lái)說(shuō)是災(zāi)難級(jí)的。

在向?qū)ο蟀l(fā)送消息,也就是實(shí)例方法被調(diào)用時(shí),對(duì)象通過自己的isa找到所屬的類,然后在類的結(jié)構(gòu)中找到對(duì)應(yīng)方法的實(shí)現(xiàn)(關(guān)于在類結(jié)構(gòu)中如何找到方法的實(shí)現(xiàn),后續(xù)的文章再介紹)。

我們知道,Objective-C中區(qū)分類方法和實(shí)例方法。實(shí)例方法是如何找到的我們了解了,那么類方法是如何找到的呢?類結(jié)構(gòu)體中也有isa,類對(duì)象的isa指向哪里呢?

元類(metaClass)

為了解決類方法調(diào)用,Objective-C引入了元類(metaClass),類對(duì)象的isa指向該類的元類,一個(gè)類對(duì)象對(duì)應(yīng)一個(gè)元類對(duì)象。

元類對(duì)象也是類對(duì)象,既然是類對(duì)象,那么元類對(duì)象中也有isa,那么元類的isa又指向哪里呢?總不能指向元元類吧……這樣是無(wú)窮無(wú)盡的。

Objective-C語(yǔ)言的設(shè)計(jì)者已經(jīng)考慮到了這個(gè)問題,所有元類的isa都指向一個(gè)元類對(duì)象,該元類對(duì)象就是 meta Root Class,可以理解成根元類。關(guān)于實(shí)例對(duì)象、類、元類之間的關(guān)系,蘋果官方給了一張圖,非常清晰的表明了三者的關(guān)系,如下

image

isa結(jié)構(gòu)體定義

了解了isa的作用,現(xiàn)在來(lái)看一下isa的定義。isa是isa_t類型,isa_t也是一個(gè)結(jié)構(gòu)體,其定義在objc-private.h中:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    // 相當(dāng)于是unsigned long bits;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

ISA_BITFIELD的定義在 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 deallocating      : 1;                                         \
uintptr_t has_sidetable_rc  : 1;                                         \
uintptr_t extra_rc          : 8

注意:這里的代碼都是x86_64架構(gòu)下的,arm64架構(gòu)下和x86_64架構(gòu)下有區(qū)別,但是不影響我們理解isa_t結(jié)構(gòu)體。

將isa_t結(jié)構(gòu)體中的ISA_BITFIELD使用isa.h文件中的ISA_BITFIELD替換,isa_t的定義可以表示如下:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    // 相當(dāng)于是unsigned long bits;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        uintptr_t nonpointer        : 1; 
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44;
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
    };
#endif
};

注意isa_t是聯(lián)合體,也就是說(shuō)isa_t中的變量,cls、bits和內(nèi)部的結(jié)構(gòu)體全都位于同一塊地址空間。

本篇文章主要分析下isa_t中內(nèi)部結(jié)構(gòu)體中各個(gè)變量的作用

struct {
    uintptr_t nonpointer        : 1; 
    uintptr_t has_assoc         : 1;
    uintptr_t has_cxx_dtor      : 1;
    uintptr_t shiftcls          : 44;
    uintptr_t magic             : 6;
    uintptr_t weakly_referenced : 1;
    uintptr_t deallocating      : 1;
    uintptr_t has_sidetable_rc  : 1;
    uintptr_t extra_rc          : 8;
};

該結(jié)構(gòu)體共占64位,其內(nèi)存分布如下:

image

在了解內(nèi)個(gè)結(jié)構(gòu)體各個(gè)變量的作用前,先通過Runtime代碼看一下isa結(jié)構(gòu)體是如何初始化的。

isa結(jié)構(gòu)體初始化

isa結(jié)構(gòu)體初始化定義在objc_object結(jié)構(gòu)體中,看一下官方提供的函數(shù)和注釋:

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

官方提供的有類對(duì)象初始化isa,協(xié)議對(duì)象初始化isa,實(shí)例對(duì)象初始化isa,其他對(duì)象初始化isa,分別對(duì)應(yīng)不同的函數(shù)。

看下每個(gè)函數(shù)的實(shí)現(xiàn):

inline void objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

inline void objc_object::initClassIsa(Class cls)
{
    if (DisableNonpointerIsa  ||  cls->instancesRequireRawIsa()) {
        initIsa(cls, false/*not nonpointer*/, false);
    } else {
        initIsa(cls, true/*nonpointer*/, false);
    }
}

inline void objc_object::initProtocolIsa(Class cls)
{
    return initClassIsa(cls);
}

inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!cls->instancesRequireRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

可以看到,無(wú)論是類對(duì)象,實(shí)例對(duì)象,協(xié)議對(duì)象,還是其他對(duì)象,初始化isa結(jié)構(gòu)體最終都調(diào)用了

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)

函數(shù),只是所傳的參數(shù)不同而已。

最終調(diào)用的initIsa函數(shù)的代碼,經(jīng)過簡(jiǎn)化后如下:

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        // 實(shí)例對(duì)象的isa初始化直接走else分之
        // 初始化一個(gè)心得isa_t結(jié)構(gòu)體
        isa_t newisa(0);
        // 對(duì)新結(jié)構(gòu)體newisa賦值
        // ISA_MAGIC_VALUE的值是0x001d800000000001ULL,轉(zhuǎn)化成二進(jìn)制是64位
        // 根據(jù)注釋,使用ISA_MAGIC_VALUE賦值,實(shí)際上只是賦值了isa.magic和isa.nonpointer
        newisa.bits = ISA_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;
        // 將當(dāng)前對(duì)象的類指針賦值到shiftcls
        // 類的指針是按照字節(jié)(8bits)對(duì)齊的,其指針后三位都是沒有意義的0,因此可以右移3位
        newisa.shiftcls = (uintptr_t)cls >> 3;
        // 賦值。看注釋這個(gè)地方不是線程安全的??
        isa = newisa;
    }
}

初始化實(shí)例對(duì)象的isa時(shí),傳入的nonpointer參數(shù)是true,所以直接走了else分之。在else分之中,對(duì)isa的bits分之賦值ISA_MAGIC_VALUE。根據(jù)注釋,這樣代碼實(shí)際上只是對(duì)isa中的magic和nonpointer進(jìn)行了賦值,來(lái)看一下為什么。

ISA_MAGIC_VALUE的值是0x001d800000000001ULL,轉(zhuǎn)化成二進(jìn)制就是0000 0000 0001 1101 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001,將每一位對(duì)應(yīng)到isa內(nèi)部的結(jié)構(gòu)體中,看一下對(duì)哪些變量產(chǎn)生了影響:

image

可以看到將nonpointer賦值為1;將magci賦值為110111;其他的仍然都是0。所以說(shuō)只賦值了isa.magci和isa.nonpointer。

nonpointer

在文章開頭也提到了,在Objective-C語(yǔ)言中,類也是對(duì)象,且每個(gè)對(duì)象都包含一個(gè)isa指針,現(xiàn)在改為了isa結(jié)構(gòu)體。nonpointer作用就是區(qū)分這兩者。

  1. 如果nonpointer為1,代表不是isa指針,而是isa結(jié)構(gòu)體。雖然不是isa指針,但是通過isa結(jié)構(gòu)體仍然能獲得類指針(下面會(huì)分析)。
  2. 如果nonpointer為0,代表當(dāng)前是isa指針,訪問對(duì)象的isa會(huì)直接返回類指針。
magic

magic的值調(diào)試器會(huì)用到,調(diào)試器根據(jù)magci的值判斷當(dāng)前對(duì)象已經(jīng)初始過了,還是尚未初始化的空間。

has_cxx_dtor

接下來(lái)就是對(duì)has_cxx_dtor進(jìn)行賦值。has_cxx_dtor表示當(dāng)前對(duì)象是否有C++的析構(gòu)函數(shù)(destructor),如果沒有,釋放時(shí)會(huì)快速的釋放內(nèi)存。

shiftcls

在函數(shù)

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 

中,參數(shù)cls就是類的指針。而

newisa.shiftcls = (uintptr_t)cls >> 3;

shiftcls存儲(chǔ)的到底是什么呢?

實(shí)際上,shiftcls存儲(chǔ)的就是當(dāng)前對(duì)象類的指針。之所以右移三位是出于節(jié)省空間上的考慮。

在Objective-C中,類的指針是按照字節(jié)(8 bits)對(duì)齊的,也就是說(shuō)類指針地址轉(zhuǎn)化成十進(jìn)制后,都是8的倍數(shù),也就是說(shuō),類指針地址轉(zhuǎn)化成二進(jìn)制后,后三位都是0。既然是沒有意義的0,那么在存儲(chǔ)時(shí)就可以省略,用節(jié)省下來(lái)的空間存儲(chǔ)一些其他信息。

在objc-runtime-new.mm文件的

static __attribute__((always_inline)) id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)

函數(shù),類初始化時(shí)會(huì)調(diào)用該函數(shù)。可以在該函數(shù)中打印類對(duì)象的地址

if (!cls) return nil;
// 這里可以打印類指針的地址,類指針地址最后一位是十六進(jìn)制的8或者0,說(shuō)明
// 類指針地址后三位都是0
printf("cls address = %p\n",cls);

打印出的部分信息如下:

cls address = 0x7fff83bca218
cls address = 0x7fff83bcab28
cls address = 0x7fff83bc5290
cls address = 0x7fff83717f58
cls address = 0x7fff83717f58
cls address = 0x100b15140
cls address = 0x7fff83717fa8
cls address = 0x7fff837164c8
cls address = 0x7fff837164c8
cls address = 0x7fff83716e78
cls address = 0x100b15140
cls address = 0x7fff837175a8
cls address = 0x7fff837175a8
cls address = 0x7fff83717fa8

可以看到類對(duì)象的地址最后一位都是8或者0,說(shuō)明類對(duì)象確實(shí)是按照字節(jié)對(duì)齊,后三位都是0。因此在賦值shiftcls時(shí),右移三位是安全的,不會(huì)丟失類指針信息。

我們可以寫代碼驗(yàn)證一下對(duì)象的isa和類對(duì)象指針的關(guān)系。代碼如下:

#import <Foundation/Foundation.h>
#import "objc-runtime.h"

// 把一個(gè)十進(jìn)制的數(shù)轉(zhuǎn)為二進(jìn)制
NSString * binaryWithInteger(NSUInteger decInt){
    NSString *string = @"";
    NSUInteger x = decInt;
    while(x > 0){
        string = [[NSString stringWithFormat:@"%lu",x&1] stringByAppendingString:string];
        x = x >> 1;
    }
    return string;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 把對(duì)象轉(zhuǎn)為objc_object結(jié)構(gòu)體
        struct objc_object *object = (__bridge struct objc_object *)([NSObject new]);
        NSLog(@"binary = %@",binaryWithInteger(object->isa));
        // uintptr_t實(shí)際上就是unsigned long
        NSLog(@"binary = %@",binaryWithInteger((uintptr_t)[NSObject class]));
    }
    return 0;
}

打印出isa的內(nèi)容是:1011101100000000000000100000000101100010101000101000001,NSObject類對(duì)象的指針是:100000000101100010101000101000000。首先將isa的內(nèi)容補(bǔ)充至64位

0000 0101 1101 1000 0000 0000 0001 0000 0000 1011 0001 0101 0001 0100 0001

取第4位到第47位之間的內(nèi)容,也就是shiftcls的值:

000 0000 0000 0001 0000 0000 1011 0001 0101 0001 0100 0

將類對(duì)象的指針右移三位,即去除后三位的0,得到

100000000101100010101000101000

和上面的shiftcls對(duì)比:

                 10 0000 0001 0110 0010 1010 0010 1000
0000 0000 0000 0010 0000 0001 0110 0010 1010 0010 1000

可以確認(rèn):shiftcls中的確包含了類對(duì)象的指針

其他位

上面已經(jīng)介紹了nonpointer、magic、shiftcls、has_cxx_dtor,還有一些其他位沒有介紹,這里簡(jiǎn)單了解一下。

  1. has_assoc: 表示對(duì)象是否含有關(guān)聯(lián)引用(associatedObject)
  2. weakly_referenced: 表示對(duì)象是否含有弱引用對(duì)象
  3. deallocating: 表示對(duì)象是否正在釋放
  4. has_sidetable_rc: 表示對(duì)象的引用計(jì)數(shù)是否太大,如果太大,則需要用其他的數(shù)據(jù)結(jié)構(gòu)來(lái)存
  5. extra_rc:對(duì)象的引用計(jì)數(shù)大于1,則會(huì)將引用計(jì)數(shù)的個(gè)數(shù)存到extra_rc里面。比如對(duì)象的引用計(jì)數(shù)為5,則extra_rc的值為4。

extra_rc和has_sidetable_c可以一起理解。extra_rc用于存放引用計(jì)數(shù)的個(gè)數(shù),extra_rc占8位,也就是最大表示255,當(dāng)對(duì)象的引用計(jì)數(shù)個(gè)數(shù)超過257時(shí),has_sidetable_rc的值應(yīng)該為1。

總結(jié)

至此,isa結(jié)構(gòu)體的介紹就完了。需要提醒的是,上面的代碼是運(yùn)行在macOS上,也就是x86_64架構(gòu)上的,isa結(jié)構(gòu)體也是基于x86_64架構(gòu)的。在arm64架構(gòu)上,isa結(jié)構(gòu)體中變量所占用的位數(shù)和x86_64架構(gòu)是不一樣的,但是表示的含義是一樣的。理解了x86_64架構(gòu)下的isa結(jié)構(gòu)體,相信對(duì)于理解arm架構(gòu)下的isa結(jié)構(gòu)體,應(yīng)該不是什么難事。

參考文章

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

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評(píng)論 0 9
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂樂的簡(jiǎn)書閱讀 2,148評(píng)論 0 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,121評(píng)論 1 32
  • RunTime:指一個(gè)程序在運(yùn)行(或者在被執(zhí)行)的狀態(tài)。也就是說(shuō),當(dāng)你打開一個(gè)程序使它在電腦上運(yùn)行的時(shí)候,那個(gè)程序...
    悟2023閱讀 465評(píng)論 0 1
  • 本文基于objc4-709源碼進(jìn)行分析。關(guān)于源碼編譯:objc - 編譯Runtime源碼objc4-706 ob...
    WeiHing閱讀 846評(píng)論 1 3