CoreFoundation框架详细解析(二) —— 设计概念

版本记录

版本号 时间
V1.0 2017.10.06

前言

Core Foundation框架(CoreFoundation.framework)是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。接下来我们就详细的解析这个框架。感兴趣的可以看我上面写的几篇。
1. CoreFoundation框架详细解析(一) —— 基本概览

简介

Core Foundation是一个库,具有一系列概念性的基于Objective-C的基础框架,但以C语言实现的编程接口。 为此,Core Foundation在C中实现了有限的对象模型。Core Foundation定义了封装数据和函数的不透明类型,以下称为“对象”。

Core Foundation对象的编程接口设计为易于使用和重用。 一般来说,Core Foundation:

  • 支持在各种框架和库之间共享代码和数据
  • 使一定程度的操作系统独立成为可能
  • 支持Unicode字符串的国际化
  • 提供通用的API和其他有用功能,包括插件架构,XML属性列表和首选项

Core Foundation使OS X上的不同框架和库可以共享代码和数据。 应用程序,库和框架可以定义在其外部接口中并入Core Foundation类型的C例程;因此它们可以通过这些接口将数据(作为Core Foundation对象)传递给彼此。

Core Foundation会还提供某些服务与Cocoa’s Foundation框架之间的toll-free bridging - 免费桥接。 免费桥接使您可以将Cocoa对象替换为功能参数中的Core Foundation对象,反之亦然。

一些Core Foundation的类型和函数是在不同操作系统上具有特定实现的东西的抽象。使用这些API的代码因此更容易移植到不同的平台。

Datenumber类型抽象时间工具,并提供在绝对和公历时间之间进行转换的设施。它还抽象数值,并提供了在这些值的不同内部表示之间进行转换的工具。

Core Foundation为应用程序开发带来的主要优点之一是国际化支持。通过其String对象,Core Foundation可以在所有OS X和Cocoa编程接口和实现方面实现轻松,强大和一致的国际化。此支持的基本部分是CFString类型,其实例表示16位Unicode字符数组。 CFString对象具有足够的灵活性,可以容纳数字字符,并且简单而低级,足以用于传送字符数据的所有编程接口。它的性能与标准C字符串的性能没有太大的不同。

您应该阅读本文档,以了解Core Foundation以及Core Foundation对象与Cocoa (Touch)对象交互的基本设计原则。


Organization of This Document - 文档的组成

这些概念和任务讨论了在Core Foundation中使用的对象模型。

此外,还有其他非对象类型和API约定,您应该在使用Core Foundation之前熟悉它们:


Opaque Types - 不透明类型

Core Foundation的支持封装和多态函数的对象模型基于不透明类型。

基于不透明类型的对象的各个字段从客户端隐藏,但类型的函数提供对这些字段的大多数值的访问。Figure 1描绘了它隐藏的数据中的不透明类型和它呈现给客户端的接口。

注意:“Class”不用于引用不透明类型,因为尽管类和不透明类型有概念上的相似性,但很多人可能会发现术语混淆。然而,Core Foundation文档经常将这些类型的具体的数据承载实例称为“对象”。

Core Foundation会有许多不透明的类型,这些类型的名称反映了他们的特定用途。例如,CFString是一种不透明的类型,“代表”并对Unicode字符数组进行操作。 (“CF”当然是Core Foundation的前缀。),CFArray是基于索引的收集功能的不透明类型。支持不透明类型的函数,常量和其他辅助数据类型通常在具有类型名称的头文件中定义,例如,CFArray.h包含CFArray类型的符号定义。

Figure 1 An opaque type

1. Advantages of Opaque Types - 不透明类型的优点

对某些来说,不透明的类型似乎是通过阻止结构内容的直接访问来施加不必要的限制。与可能影响程序性能的不透明类型似乎也有开销。但是不透明类型的好处超过了这些看似的限制。

不透明类型为底层功能的实现提供了更好的抽象和更大的灵活性。通过隐藏诸如结构体领域的细节,当这些细节发生变化时,Core Foundation可以减少客户端代码中可能发生的错误的机会。此外,不透明类型允许优化,如果暴露可能会令人困惑。例如,CFString“正式”表示UniChar类型的16位字符数组。但是,CFString可能会选择将ASCII范围内的一系列字符存储为8位值。复制一个不可变对象可能(通常会)导致对对象的共享引用,而不是存储器中的两个单独的对象(参见Memory Management Programming Guide for Core Foundation)。

继续使用CFString的示例,使用不透明类型存储字符可能看起来很重。然而,事实证明,这种存储的CPU成本并不比使用简单的C数组字符高得多,并且内存成本通常较少。此外,不透明度并不一定意味着不透明类型不能直接提供访问内容的机制。例如,CFString为此提供了CFStringGetCStringPtr函数。

最后,您可以在一定程度上自定义一些不透明类型。例如,集合类型允许您定义用于在集合的每个成员上调用函数的回调。


Object References - 对象引用

您可以通过引用来引用Core Foundation对象(不透明类型)。 在不透明类型的每个头文件中,您将注意到一行或两行类似于以下内容:

typedef const struct __CFArray * CFArrayRef;
typedef struct __CFArray * CFMutableArrayRef;

这些声明是指向不透明和可变版本的(专用)结构定义不透明类型的指针引用。 许多Core Foundation函数的参数和返回值都是这些对象引用的类型,而不是私有结构的typedef。 例如:

CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef array, CFStringRef separatorString);

有关不透明类型对象的不变,可变和其他变体的更多信息,请参阅Varieties of Objects

每个Core Foundation 不透明类型为其对象定义唯一的类型ID,如CFArray对象的CFArrayRef中所示。类型ID是类型为CFTypeID的整数,用于标识Core Foundation对象“belongs”的不透明类型。您可以在各种上下文中使用类型ID,例如在异构集合上运行时。 Core Foundation提供用于获取和评估类型ID的编程接口。

重要提示:由于类型ID的值可以在释放中更改,您的代码不应该依赖存储的或硬编码的类型ID,也不应该对所有观察到的类型ID的属性进行硬编码(例如,它是一个小整数)。

此外,Core Foundation定义了一种通用对象引用类型CFTypeRef,类似于某些面向对象编程语言中的根类。此通用引用用作多项式函数的参数和返回值的占位符类型,可以引用任何Core Foundation对象。有关此主题的更多信息,请参阅Polymorphic Functions 。有关使用对象引用时与内存管理相关的问题,请参阅Memory Management Programming Guide for Core Foundation


Polymorphic Functions - 多态函数

Core Foundation提供了几个多态函数。这些函数可以将任何Core Foundation对象作为参数,并且(在一个实例中,CFRetain)可以返回任何Core Foundation对象。给定这些参数和返回值的类型为CFTypeRef,一般的对象引用类型。CFType类似于面向对象语言中的根类,因为它的功能可以被所有其他对象重用。

您对所有Core Foundation对象常见的操作使用多态函数:

  • 参考计数。

  • 比较对象

    • CFEqual函数比较任何两个Core Foundation对象(请参见Comparing Objects)。平等的依据取决于比较对象的类型。例如,如果两者都是CFString对象,则测试涉及逐个字符的比较。
  • 哈希对象。

    • CFHash函数返回标识Core Foundation对象的唯一哈希码(请参阅 Comparing Objects)。您可以使用哈希码作为哈希表结构中的表地址。如果两个对象相等(由CFEqual函数确定),则它们必须具有相同的哈希值。
  • 检查对象。

    • CFType为您提供检查对象的方法,从而了解其内容及其所属类型。CFCopyDescription函数返回描述对象的字符串(更准确地说,是对CFString对象的引用)。使用CFTypeID而不是CFTypeRef参数的CFCopyTypeIDDescription函数返回一个字符串引用,该引用描述了类型ID标识的不透明类型。这些函数主要用于协助调试,有关这些功能的更多信息,请参阅Inspecting Objects

您还可以通过使用CFGetTypeID函数获取其类型ID,然后将该值与已知类型ID进行比较,来确定通用类型对象所属的opaque类型。有关此任务的更多信息,请参阅Inspecting Objects


Varieties of Objects - 对象种类

根据其对象中预期的可编辑性和可扩展性的特点,不透明类型最多可以有三种基本种类,或flavor

  • 不变的和固定的大小
  • 可变和固定尺寸
  • 可变和可变大小

可变对象是可编辑的,意味着可以更改其内容。不可变对象不可编辑,一旦创建,他们就不能改变。任何改变不可变对象的尝试通常都会导致某种错误。固定大小的对象具有可以增长到的最大限制,在CFString的情况下,这将是字符数,对于集合,限制将是元素的数量。

一些不透明的类型,如CFString和CFArray,可以创建所有三种风格的对象。大多数不透明类型可以创建不变的固定大小的对象,并且通常至少有一个不合格的创建函数来执行该工作(如CFArrayCreate)。可变固定大小与可变大小的决定因素是TypeCreateMutable函数中容量或最大长度参数的值,任何正值都会导致固定大小的对象,但是0指定可变大小的对象。

对可变对象的引用包括类型名称中的Mutable,例如CFMutableStringRef


Naming Conventions - 命名约定

Core Foundation中的主要编程接口约定是使用与符号最密切相关的不透明类型的名称作为符号的前缀。 对于函数,此前缀不仅标识函数“所属”的类型,而且还标识作为函数动作目标的对象的类型。 (这个约定的一个例外是常量,它将“k”放在类型前缀之前。)这里是头文件中的几个例子:

/* from CFDictionary.h */
CF_EXPORT CFIndex CFDictionaryGetCountOfKey(CFDictionaryRef dict, const void *key);
/* from CFString.h */
typedef UInt32 CFStringEncoding;
/* from CFCharacterSet.h */
typedef enum {
    kCFCharacterSetControl = 1,
    kCFCharacterSetWhitespace,
    kCFCharacterSetWhitespaceAndNewline,
    kCFCharacterSetDecimalDigit,
    kCFCharacterSetLetter,
    kCFCharacterSetLowercaseLetter,
    kCFCharacterSetUppercaseLetter,
    kCFCharacterSetNonBase,
    kCFCharacterSetDecomposable,
    kCFCharacterSetAlphaNumeric,
    kCFCharacterSetPunctuation,
    kCFCharacterSetIllegal
} CFCharacterSetPredefinedSet;

除了与不透明类型和内存管理相关的内容之外,Core Foundation还有一些编程接口约定。

  • 在返回值的函数的名称中,GetCopyCreate之间有一个重要区别。如果使用Get函数,则无法确定返回的对象的使用寿命。为了确保这样的对象的持久性,您可以保留它(使用CFRetain函数),或者在某些情况下复制它。如果您使用Copy或者Create函数,您将负责释放对象(使用CFRelease函数)。有关更多详细信息,请参阅 Memory Management Programming Guide for Core Foundation
  • 一些Core Foundation对象有自己的命名约定来强化常见操作之间的一致性。例如,集合在函数名中嵌入以下动词来表示对集合元素的具体操作:
    • Add是指“如果不存在就添加,如果存在则不做任何事情”(如果是排他性的唯一的集合)。
    • Replace是指“如果存在,请更换,如果不存在则不做任何事情”。
    • Set表示“如果不存在则添加”,如果存在则替换“”。
    • Remove表示“如果存在,删除,如果缺席,不做任何事情”。
  • CFIndex类型用于索引,计数,长度和大小参数以及返回值。当处理器的地址大小增加时,此类型表示的整数值(当前为32位)随着时间的推移而增长。在指针大小不同的体系结构(比如64位)上,CFIndex可能被声明为64位,与int的大小无关。通过使用CFIndex与相同类型的Core Foundation参数进行交互的变量,您可以确保代码的源兼容性更高。
  • 一些Core Foundation头文件似乎可以定义不透明类型,但实际上包含与特定类型无关的便利函数。一个例子是CFPropertyList.hCFPropertyList是任何属性列表类型的占位符类型:CFString,CFData,CFBoolean,CFNumber,CFDate,CFArray和CFDictionary
  • 除非另有说明,否则用于返回值的所有参考参数都可以接受NULL。这表示调用者对该返回值不感兴趣。

Other Types - 其他类型

Core Foundation定义了一些函数中使用的数据类型。这些类型中的一些的目的是抽象可能随着处理器地址空间的变化而改变的原始值。例如,CFIndex类型用于索引,计数,长度和大小参数。CFOptionFlags类型用于位域参数,CFHashCode类型保留从CFHash函数返回的散列结果和某些哈希回调。

其他基本类型用于获取并返回比较和范围值的函数。 CFRange是一个结构,用于指定项目的线性序列的任何部分,从字符串中的字符到集合中的元素。对于比较函数,CFComparisonResult类型定义枚举常量来表示适当的返回值(等于,小于,大于)。一些Core Foundation函数可以回调比较器函数,如果要使用自定义比较器,则该函数必须符合CFComparatorFunction类型指定的签名。

重要提示:某些Core Foundation类型的整数值,特别是CFIndexCFTypeID,可随着处理器的地址大小的增长而随着时间的推移而增长。通过使用与相同类型的Core Foundation参数交互的变量的基本类型,您将确保为代码提供更高程度的源兼容性。

Core Foundation提供的其他不透明类型在不同的主题中讨论。


Comparing Objects - 对象的比较

您使用CFEqual函数将两个Core Foundation对象进行比较。 如果两个对象基本相等,该函数将返回一个布尔值true。“ Essential”相等取决于所比较的对象的类型。 例如,当您比较两个CFString对象时,Core Foundation认为它们在符合逐个字符时基本相同,无论其编码或可变性属性如何。 当两个CFArray对象具有相同的元素数量时,并且一个数组中的每个元素对象与其他数组中的对应基本相同,它们被认为是相等的。显然,被比较的对象必须是相同类型(或相同类型的可变或不可变变体)被认为是相等的。

以下代码片段显示了如何使用CFEqual函数将常量与传入参数进行比较:

// Listing 1  Comparing Core Foundation objects

void stringTest(CFStringRef myString) {
    Boolean equal = CFEqual(myString, CFSTR(“Kalamazoo”));
    if (!equal) {
        printf(“They’re not equal!");
    }
    else {
        printf(“They’re equal!”):
    }
}

Inspecting Objects - 检查对象

Core Foundation对象的主要特征是它们基于不透明(或私有)类型,因此难以直接检查对象的内部数据。 但是,Base Services提供了两个函数,您可以使用它们来检查Core Foundation对象。 这些函数返回对象和对象类型的描述。

要查找Core Foundation对象的内容,请调用该对象的CFCopyDescription函数,然后在引用的字符串对象中打印包含的字符序列:

// Listing 1  Using CFCopyDescription

void describe255(CFTypeRef tested) {
    char buffer[256];
    CFIndex got;
    CFStringRef description = CFCopyDescription(tested);
    CFStringGetBytes(description,
        CFRangeMake(0, CFStringGetLength(description)),
        CFStringGetSystemEncoding(), '?', TRUE, buffer, 255, &got);
    buffer[got] = (char)0;
    fprintf(stdout, "%s", buffer);
    CFRelease(description);
}

此示例仅显示打印描述的一种方法。 您可以使用CFStringGetBytes以外的CFString函数来获取实际的字符串。

要确定“unknown”对象的类型,请使用CFGetTypeID函数获取其类型ID,并将该值与已知类型ID进行比较,直到找到匹配项。 使用CFGetTypeID函数获取对象的类型ID。 每个不透明类型还定义了CFTypeGetTypeID形式的函数(例如,CFArrayGetTypeID),此函数返回该类型的类型ID。 因此,您可以测试CFType对象是否是特定不透明类型的成员,如:

CFTypeID type = CFGetTypeID(anObject);
if (CFArrayGetTypeID() == type)
    printf(“anObject is an array.”);
else
    printf(“anObject is NOT an array.”);

要显示关于调试器中Core Foundation对象类型的信息,请使用CFGetTypeID函数获取其类型ID,然后将该值传递给CFCopyTypeIDDescription函数:

/* aCFObject is any Core Foundation object */
CFStringRef descrip = CFCopyTypeIDDescription(CFGetTypeID(aCFObject));

注意:·String Services·包括两个在·CFString.h·中声明的函数,您可以调用支持的调试器来打印Core Foundation对象的描述:CFShow和CFShowStr

要点CFCopyDescriptionCFCopyTypeIDDescription函数仅用于调试。 因为描述中的信息和格式可能会发生变化,所以不要在代码中创建依赖关系。


Toll-Free Bridged Types - 自由桥接类型

Core Foundation框架中有许多数据类型可以和Foundation框架中的类型互换使用。可互换使用的数据类型也称为自由桥接数据类型。这意味着您可以使用与Core Foundation函数调用的参数相同的数据结构作为OC消息转发的接收方。例如,NSLocale(参见NSLocale Class Reference)可以与其Core Foundation对应的CFLocale(参见CFLocale Reference)互换。

尽管他们的名字可能表明它们是,但是不是所有的数据类型都是自由的桥接,例如,NSRunLoop不是自由桥接到CFRunLoopNSBundle不是自由桥接到CFBundle,并且NSDateFormatter不是自由桥接到CFDateFormatter。Table 1提供了支持自由桥接的数据类型的列表。

注意:如果在您正在使用的Core Foundation集合上安装的自定义回调,包括NULL回调,则当从Objective-C访问时,其内存管理行为未定义。

1. Casting and Object Lifetime Semantics - 转换对象终身语义

通过自由桥接,在您可以看到例如NSLocale *参数的方法中,您可以传递CFLocaleRef,并且在您看到CFLocaleRef参数的函数中,可以传递NSLocale实例。 您还必须为编译器提供其他信息:首先,必须将一种类型转换为另一种类型;此外,您可能必须指示对象生命周期语义。

编译器了解返回Core Foundation类型并遵循历史Cocoa命名约定的Objective-C方法(请参阅 Advanced Memory Management Programming Guide)。 例如,编译器知道,在iOS中,由UIColorCGColor方法返回的CGColor不是它所拥有的。 您仍然必须使用适当的类型转换,如本示例所示:

NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];

编译器不会自动管理Core Foundation对象的生命周期。 您可以使用cast(在objc / runtime.h中定义)或Core Foundation-style宏(在NSObject.h中定义)告知编译器对象的所有权语义:

  • __bridge在Objective-C和Core Foundation之间传输一个指针,而不转移所有权。
  • __bridge_retainedCFBridgingRetain将一个Objective-C指针转换为Core Foundation指针,并将所有权转让给您。您负责调用CFRelease或相关函数以放弃该对象的所有权。
  • __bridge_transferCFBridgingRelease将非Objective-C指针移动到Objective-C,并将所有权转移给ARC。ARC负责放弃对象的所有权。

其中一些显示在以下示例中:

NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
CFLocaleRef gbCFLocale = (__bridge CFLocaleRef)gbNSLocale;
CFStringRef cfIdentifier = CFLocaleGetIdentifier(gbCFLocale);
NSLog(@"cfIdentifier: %@", (__bridge NSString *)cfIdentifier);
// Logs: "cfIdentifier: en_GB"
 
CFLocaleRef myCFLocale = CFLocaleCopyCurrent();
NSLocale *myNSLocale = (NSLocale *)CFBridgingRelease(myCFLocale);
NSString *nsIdentifier = [myNSLocale localeIdentifier];
CFShow((CFStringRef)[@"nsIdentifier: " stringByAppendingString:nsIdentifier]);
// Logs identifier for current locale

下一个示例显示了使用Core Foundation内存管理函数,这些功能由Core Foundation内存管理规则指定:

- (void)drawRect:(CGRect)rect {

CGContextRef ctx = UIGraphicsGetCurrentContext();

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

CGFloat locations[2] = {0.0, 1.0};

NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];

[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];

CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);

CGColorSpaceRelease(colorSpace); // Release owned Core Foundation object.

CGPoint startPoint = CGPointMake(0.0, 0.0);

CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds));

CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint,

kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);

CGGradientRelease(gradient); // Release owned Core Foundation object.

}

2. Toll-Free Bridged Types - 自由桥接类型

Table 1提供了Core Foundation和Foundation之间可互换的数据类型列表。 对于每一对,该表还列出了它们之间的自由桥接可用的OS X版本。

Core Foundation type Foundation class Availability
CFArrayRef NSArray OS X 10.0
CFAttributedStringRef NSAttributedString OS X 10.4
CFBooleanRef NSNumber OS X 10.0
CFCalendarRef NSCalendar OS X 10.4
CFCharacterSetRef NSCharacterSet OS X 10.0
CFDateRef NSDate OS X 10.0
CFDictionaryRef NSDictionary OS X 10.0
CFErrorRef NSError OS X 10.5
CFLocaleRef NSLocale OS X 10.4
CFMutableArrayRef NSMutableArray OS X 10.0
CFMutableAttributedStringRef NSMutableAttributedString OS X 10.4
CFMutableCharacterSetRef NSMutableCharacterSet OS X 10.0
CFMutableDataRef NSMutableData OS X 10.0
CFMutableDictionaryRef NSMutableDictionary OS X 10.0
CFMutableSetRef NSMutableSet OS X 10.0
CFMutableStringRef NSMutableString OS X 10.0
CFNullRef NSNull OS X 10.2
CFNumberRef NSNumber OS X 10.0
CFReadStreamRef NSInputStream OS X 10.0
CFRunLoopTimerRef NSTimer OS X 10.0
CFSetRef NSSet OS X 10.0
CFStringRef NSString OS X 10.0
CFTimeZoneRef NSTimeZone OS X 10.0
CFURLRef NSURL OS X 10.0
CFWriteStreamRef NSOutputStream OS X 10.0

后记

未完,待续~~~

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

推荐阅读更多精彩内容