Objective-C 面向对象(一)——对象的本质

对于一门编程语言而言,当初学者掌握了其基本语法和标准库的使用以后,如果能够继续了解该语言的核心思想与底层实现,则会渐入佳境,窥探语言实现的本质技术,能够为以后的性能优化以及规避技术陷阱等复杂工作提供思路。
了解Objective-C语言的面向对象的本质,有益于程序员更加深刻理解该语言是如何实践OOP思想,并使得在构建面向对象程序的时候更加游刃有余。

背景知识

Objective-C是C语言的超集,也就是说,C语言的语法全部被Objective-C兼容,而面向对象的特性则是建立在C语言的基础之上,当熟悉过C语言的指针、内存管理、自定义数据-结构体等一系列知识以后,对于Objective-C的面向对象实现的理解,就容易多了,因为本质上,Objective-C的面向对象,就是使用这些东西构建出来的。
我们需要了解的是,对于C语言来说,除了语言本身定义的数据类型,程序员想要自定义数据类型以供编程使用,结构体是必然选择,基于这样的事实,那么理应能够猜到,Objective-C中的一切面向对象概念,诸如类、对象等,都是基于C语言的结构体之上构建的,而如何进行对象方法的调用、类方法调用等等,则通过Objective-C从smalltalk借鉴过来的消息调用思想而实现的Runtime思想,后者是消息调用思想的鼻祖。

什么是类和对象

C语言是没有面向对象概念的,只有基本数据类型、指针、结构体等等。那么如何通过这些概念构建面向对象的概念,要明白这个的前提是大体总结一下对象和类有什么特点。

类是描述一个对象规格的模板,即它说明了一个对象有什么样的属性,有什么样的方法。对象的构建,通过指定类,并且初始化,从而得到类的实例-对象,那么也就是说类是一种描述实例对象的数据结构。
在Objective-C中,标准库为Foundation,事实上几乎所有的类都继承与NSObject,那么类具体有如下表现

  1. 类具有方法和类方法的声明,描述对象实例有什么方法和类有什么方法
  2. 类具有属性的声明,描述对象实例有什么样的属性
  3. 类可以被集成或集成其他类,从而给他人提供或从父类获取对象描述的规格信息

对象

对象是一个根据类实例化出来的数据结构。具有实例方法,实例变量,对象没有继承概念,只有持有其他对象或被其他对象持有,具有以下特点。

  1. 对象具有实例方法
  2. 对象具有实例属性
  3. 对象可以被其他对象持有或持有其他对象

类和对象的实现

既然类和对象只不过是特点不同的自定义数据类型,那么类和对象必然要使用结构体实现,Objetcive-C也是这样设计的,我们看一下NSObject的定义:

*@interface* NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
+ (void)load;
+ (void)initialize;
- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
    NS_DESIGNATED_INITIALIZER
#endif
    ;
+ (instancetype) new  OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)allocWithZone:(struct _NSZone )zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");

*@end*

NSObject的定义中,有类方法定义、属性定义、实例方法定义,如何使用C语言的结构体来表达和存储这样的自定义数据结构呢?NSObject是一个Class也就是一个类,在描述中有一个Class isa的变量,按图索骥查找到Class的数据结构:

typedef struct objc_class *Class; //class是一个objc_class结构体的指针
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE; //objc_class是一个结构体,描述了一个类的信息

从上面定义可以看出来,一个类包含哪些信息,是通过objc_class 结构体来表示的,NSObject的定义中,有一个Class isa属性,而Class是一个指向objc_class结构体的指针,也就是说,NSObject通过isa指针来寻找到其类的信息所在的结构体。
该结构体中有几个比较重要的变量:

  • Class isa OBJC_ISA_AVAILABILITY; 又是一个指向objc_class结构体的指针,指向另外一个类信息的结构体,那么一个类指向一个说明其规格的类结构体,其意义是来描述类的信息,一般称描述类的结构的数据类型称之为元类,即meta-class,以为着使用元类来描述类的规格,那么从对象与类的关系类比中,可以将类看作是元类的实例,也就说,元类是类对象的类。
  • super_class 是该类父类的信息,使用super_class指针,找到父类信息的结构体。NSObject的实例对象的superclass为null
  • long info OBJC2_UNAVAILABLE; 类信息,供运行期使用的一些位标识
  • long instance_size OBJC2_UNAVAILABLE; /该类的实例变量大小
  • struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 类的成员变量链表
  • struct objc_method_list methodLists OBJC2_UNAVAILABLE; 方法定义的链表
  • struct objc_cache *cache OBJC2_UNAVAILABLE; 方法缓存,对象接到一个消息会根据isa指针查找消息对象,这时会在methodLists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。
  • struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 协议链表

类的实例->对象也是通过一个objc_class结构体描述其结构。如下:

struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};

typedef struct objc_object *id;

id 类型,即对象,其为一个指向objc_object结构体的指针,意味着任意一个Objective-C的对象,其本质是一个指向objc_object的结构体指针,而objc_object结构体中,有一个isa指针,指向objc_class结构体,来描述其属于哪个类,也就是上面的类信息结构体。
当定义一个类的时候,如下:

//main.m
@interface ClassA : NSObject
@property(nonatomic,copy)NSString * name;
-(void)sayHello;
+(void)SayHello;
@end
@implementation ClassA
-(void)sayHello{
    NSLog(@"object say hello");
}
+(void)SayHello{
    NSLog(@"class say hello");
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
        NSObject * obj1 = [[NSObject alloc]init];
        ClassA * obj2 = [[ClassA alloc]init];
        [obj2 sayHello];
        [ClassA SayHello];
        NSLog(@"%@", NSStringFromClass([obj1 superclass]));
    }
    return 0;
}

从上面的定义中,按照之前的说明,将该文件转换为C++代码,将在C++代码中得到确切的信息。

# 得到main.cpp文件
clang -rewrite-objc main.m 

对于NSObject这个类,可以得到:

#ifndef _REWRITER_typedef_NSObject
#define _REWRITER_typedef_NSObject
typedef struct objc_object NSObject;
typedef struct {} _objc_exc_NSObject;
#endif

NSObject是一个objc_object结构体。
ClassA的结构如下:

typedef struct objc_object ClassA;

ClassA是一个指向objc_object结构体,其相关的其他部分为:

#ifndef _REWRITER_typedef_ClassA
#define _REWRITER_typedef_ClassA
typedef struct objc_object ClassA;
typedef struct {} _objc_exc_ClassA;
#endif

extern "C" unsigned long OBJC_IVAR_$_ClassA$_name;
struct ClassA_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};

// @property(nonatomic,copy)NSString * name;
// -(void)sayHello;
// +(void)SayHello;
/* @end */

// @implementation ClassA

static void _I_ClassA_sayHello(ClassA * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_0);
}

static void _C_ClassA_SayHello(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_1);
}

static NSString * _I_ClassA_name(ClassA * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ClassA$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_ClassA_setName_(ClassA * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ClassA, _name), (id)name, 0, 1); }
// @end

这个结构中,清楚地描述出了ClassA类中的实例变量、类方法、实例方法的结构和实现。
ClassA是一个objc_object结构体,其类方法和静态方法在声明以后,被使用在如下:

extern "C" unsigned long int OBJC_IVAR_$_ClassA$_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ClassA, _name);

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    1,
    {{(unsigned long int *)&OBJC_IVAR_$_ClassA$_name, "_name", "@\"NSString\"", 3, 8}}
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    3,
    {{(struct objc_selector *)"sayHello", "v16@0:8", (void *)_I_ClassA_sayHello},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_ClassA_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_ClassA_setName_}}
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"SayHello", "v16@0:8", (void *)_C_ClassA_SayHello}}
};

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"name","T@\"NSString\",C,N,V_name"}}
};

OBJC_INSTANCE_VARIABLES_ClassA,_OBJC__INSTANCE_METHODS_ClassA ,OBJC_CLASS_METHODS_ClassA,_OBJC__PROP_LIST_ClassA为该类的类方法、属性、实例方法等定义的结构体,这些结构体的被用到在:

static struct _class_ro_t _OBJC_CLASS_RO_$_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, __OFFSETOFIVAR__(struct ClassA, _name), sizeof(struct ClassA_IMPL), 
    (unsigned int)0, 
    0, 
    "ClassA",
    (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_ClassA,
    0, 
    (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_ClassA,
    0, 
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_ClassA,
};

OBJC_CLASS_RO$_ClassA 结构体变量将类方法、属性等结构体进行包装,其数据类型_class_ro_t如下:

struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;
    const char *name;
    const struct _method_list_t *baseMethods;
    const struct _objc_protocol_list *baseProtocols;
    const struct _ivar_list_t *ivars;
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;
};

该结构体其实就是objc_class结构体的变形,代表根类的结构体类型。 OBJC_CLASS_RO$_ClassA又被另外一个结构体进行包装:

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_ClassA,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_ClassA,
};

OBJC_CLASS_$_ClassA 则就是ClassA这个类的结构体,其结构体是_class_t。

struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};

OBJC_CLASS__ClassA与_OBJC_CLASS_RO__ClassA两个结构体被用在:

static void OBJC_CLASS_SETUP_$_ClassA(void ) {
    OBJC_METACLASS_$_ClassA.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_ClassA.superclass = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_ClassA.cache = &_objc_empty_cache;
    OBJC_CLASS_$_ClassA.isa = &OBJC_METACLASS_$_ClassA;
    OBJC_CLASS_$_ClassA.superclass = &OBJC_CLASS_$_NSObject;
    OBJC_CLASS_$_ClassA.cache = &_objc_empty_cache;
}

这是一个类初始化函数,从函数中得到明确的信息是:

  1. OBJC_METACLASS__ClassA是一个_class_t(即objc_class)结构体,其isa指针指向OBJC_METACLASS__NSObject结构体,其类型为_class_ro_t(objc_class)的结构体
  2. OBJC_METACLASS__ClassA.superclass指明其父类是OBJC_METACLASS__NSObject结构体指针
  3. OBJC_CLASS__ClassA的isa指针指向ClassA元类结构OBJC_METACLASS__ClassA
  4. OBJC_CLASS__ClassA.superclass指明其父类是OBJC_CLASS__NSObject根类数据结构
    至此,可以非常清晰得出以下结论:
    如下图所示:
关系图
  1. ClassA的实例对象,是一个objc_object结构体指针,其isa指针指向ClassA类的objc_object结构体
  2. ClassA,是一个_class_t(即objc_class)的结构体,其isa指针指向MetaClassA结构体
  3. ClassA的父类,是NSObject,其superclass是一个指向NSObject类对象的isa指针
  4. ClassA的元类,是MetaClassA,OBJC_METACLASS_元类结构体表明,其isa指针指向MetaNSObject结构体指针
  5. 一个类所拥有的方法、属性,都会存储在类的元类中,当调用对象的方法的时候,也就是向对象发送消息,runtime会在这个对象所属的类方法列表中查找消息对应的方法,但向类发送消息的时候,runtime就会在这个类的meta class的方法列表中查找。
    更加通用和清晰的关系图如下:
关系图

即:

  1. 对象的isa指针指向类对象
  2. 对象的superclass的指针指向父类类对象
  3. 类对象的isa指针指向元类
  4. 类对象的superclass的指针指向父类元类
  5. 元类的isa指针,指向根类(NSObject)元类
  6. 元类的superclass指针指向父类元类,直接继承根类的类的元类的superclass指向根类元类(NSObject)
  7. 根类(NSObject)的isa,指向根元类
  8. 根类(NSObject)的superclass为nil
  9. 根元类的superclass指向NSObject类
  10. 根元类的isa指针,指向自身

类与对象的相关信息

name

const char * class_getName(Class cls);

super_class和meta-class

//获取父类
Class class_getSuperclass(Class cls);
//判定类是否为一个meta class
BOOL class_isMetaClass(Class cls);

instance_size

size_t class_gerInstanceSize(Class cls);

objc_ivar_list与objc_method

//objc_ivar_list结构体存储objc_ivar数组列表
struct objc_ivar_list {
     int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
     int space OBJC2_UNAVAILABLE;
#endif
     /* variable length structure */
     struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;

//objc_method_list结构体存储着objc_method的数组列表
struct objc_method_list {
     struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
     int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
     int space OBJC2_UNAVAILABLE;
#endif
     /* variable length structure */
     struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}

objc_ivar_list 为成员变量单向链表,其结构体最后一个成员变量是一个objc_ivar结构体数组,该数组为变长,所以objc_ivar_list可以是一个变长结构体,objc_ivar标示一个成员变量:

struct objc_ivar {
    char * _Nullable ivar_name  OBJC2_UNAVAILABLE;//变量名
    char * _Nullable ivar_type  OBJC2_UNAVAILABLE;//变量类型 
    int ivar_offset   OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space    OBJC2_UNAVAILABLE;
#endif
}   OBJC2_UNAVAILABLE;

objc_method_list为方法列表

struct objc_method_list {
    struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE;
    int method_count  OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space    OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]  OBJC2_UNAVAILABLE;
}            

objc_method为方法结构体,如下:

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}         

IMP与SEL

typedef struct  *SEL;
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

IMP是一个void * ()的函数指针,实际上就是方法的实现,SEL为一个char * 字符串。
每一个objc_class结构体中都有objc_method_list列表,而objc_method_list列表中有objc_method结构体,该结构体为一个SEL对应一个IMP实现。
在runtime运行的时候,加载的每一个类对应有一个virtual table,用来缓存SEL与IMP的对应关系,从而能够通过SEL快速找到其对应实现。

成员变量(ivars)及其属性

//成员变量操作函数
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );

// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); //这个只能够向在runtime时创建的类添加成员变量

// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); //必须使用free()来释放这个数组

//属性操作函数
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );

// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );

// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

methodLists

// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); //和成员变量不同的是可以为类动态添加方法。如果有同名会返回NO,修改的话需要使用method_setImplementation

// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );

// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );

// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

// 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

// 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );

objc_protocol_list与Protocol

objc_protocol_list:

struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};

Protocol的定义如下:

#ifdef __OBJC__
@class Protocol;
#else
typedef struct objc_object Protocol;
#endif

对protocol的操作为:

// 添加协议
BOOL class_addProtocol ( Class cls, Protocol *protocol );

// 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );

// 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );

总结

Objective-C基于C语言的结构体定义面向对象的大部分概念,利用结构体指针与函数指针,来实现面向对象的类定义、类继承、实例化对象、对象成员变量和方法的存储与定义。
由此,Objective-C这本语言是一种在运行时发挥强大能力的语言,而这又归功于runtime这一消息分发系统,在运行时能够对类进行扫描、查找、调用、修改等等,这部分知识被称为rumtime核心技术,消息调用与动态类型的结合,使得Objective-C这门语言能够给予程序员非常大的自由度去享受编程的乐趣。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容