Objective-C语言元素概述

此文概括介绍了 Objective-C 语言中的基本元素。其范围仅限于 Objective-C 语言对 ANSI C 的扩展,而没有介绍 C 语言的基础元素,因为介绍这方面的资料已经有很多了。

A.1 变量

在 Objective-C 中,变量名的词法规则与 ANSI C 中变量名的词法规则相同,即变量名由字母、下划线或数字(1~9)的组合组成,但第一字符不能是数字,而且区分大小写。

A.1.1 Objective-C 保留字

在一门编程语言中,保留字(reserved word)是指在该语言中具有特殊意义的符号,因此它不能用作变量的名称。在 ANSI C 相比的基础上,Objective-C 增加了许多保留字,如表A-1所示:

表A-1 不能用作变量名的 Objective-C 保留字

_Bool _cmd _Complex _Imaginary atomic
bycopy byref BOOL Class id
IMP inline inout instancetype out
NO nonatomic NULL SEL self
Protocol restrict retain Class id
super YES

A.1.2 变量的范围

变量的可见性和可访问性与其范围有关。变量可以存在于语句块、函数、方法、对象实例(或关联起来的对象)、单个文件和多个文件中。ANSI C 定义了以下4种变量范围。

  • 块: 在语句块中声明的变量拥有块范围。在语句块中声明的变量只能在这个语句块中可见和被访问。
  • 文件:拥有文件范围的变量只能在声明它的文件中可见和被访问。
  • 函数:在函数/方法中声明的变量拥有函数范围。这些变量只能在相关函数/方法中可见和被访问。
  • 函数原型:在函数/方法原型的参数列表中定义的变量拥有函数原型范围。这些变量可以在相关函数/方法中可见和被访问。
访问修饰符

Objective-C 增加一系列访问修饰符指令,使用这些指令可以更精细地控制对实例变量的访问。

Objective-C 定义了多条编译指令,使用这些指令可以控制实例变量的范围,即在程序中控制变量的可见性。

  • @private: 实例变量只能在声明它的类和该类的其他实例中被访问。
  • @protected: 实例变量可以在声明它的类和该类子类的其他实例方法中被访问。如果没有为实例变量指定保护级别,这是默认的变量范围。
  • @public: 可以在任何位置访问实例变量。
  • @package: 可在通过任何类实例和函数访问实例变量,但在包之外,实例变量会被当成私有变量。该范围可用于库和框架中的类。

这些指令称为访问修饰符,用于修饰在实例变量声明语句块中声明的实例变量。代码清单A-1展示了类实例变量的声明语句,这段代码声明了一个可在任何位置访问的变量(int型变量counter)、一个只能在声明它的类及该类的子类中访问的变量(float型变量temperature),以及一个只能在声明它的类中访问的变量(NSString *型变量descripton)。

代码清单A-1 使用访问修饰符声明实例变量

{
    @public int counter;
    @protected float temperature;
    @private NSString *description;
}

A.2 数据类型

因为 Objective-C 是 C 语言的严格父集,所以它支持所有 ANSI C 数据类型和类型限定符。Objective-C 还定义了许多本语言专用的数据型(布尔型、类实例类型、id类型、类定义类型、块类型、枚举类型)。下面几节详细介绍这些内容。

A.2.1 布尔型

布尔型是用于存储布尔值的 Objective-C 数据类型,它有两个值:真值和假值。在 Objective-C 中,这两个值被定义为 YES 和 NO,而不是 true(1) 和 false(0)。为一个布尔值分配的存储大小为1字节。Objective-C 还支持 C99 类型的 _Bool 和 bool,但 BOOL 是 Objetive-C 中首选布尔数据类型。

A.2.2 类实例类型

类实例类型代表对象、类和超类(superclass)。Objective-C 运行时环境中有多种类实例类型,其中最常用的是 id 类型。

1. id类型

id 类型是所有 Objective-C 对象指针的根类型,用于存储对任何 Objective-C 对象(不论哪种类型)的引用。下面的语句声明了一个类型�为 id、名为 myObject 的变量。

id myObject; // 该变量的实例化对象可以是任何类型

Objective-C 的 id 类型在程序运行时等价于一种 C 语言结构,该结构被定义为指向类型定义结构 objc_object 的指针,如代码清单A-2所示。

代码清单A-2 id 类型的定义

typedef struct objc_object
{
    Class isa;
}  *id;

换言之,id 仅是一个指向含有 objc_object 标识符的 C 语言结构的指针。

2. instancetype 关键字

instancetype是一个上下文关键字,可用作方法结果的数据类型,它表明方法返回了相关结果类型。方法返回相关结果类型是指方法返回接收对象所属类的实例。通过相关结果类型可以推断出下列方法类型:

  • 以 alloc 或 new 开头的类方法;
  • 以 autorelease、init、retain 或 self 开头的实例方法。

下面语句使用关键字 new 创建了一个 OrderItem 对象。

OrderItem *item = [OrderItem new];

尽管 NSObject 类的 new 方法将 id 设置为返回类型,但编译器会根据名为 item 的接收对象所属的类,推断正确的结果类型(本例中为 OrderItem *)。

instancetype 关键字可用于明确指明,方法返回的是相关结果类型。代码清单A-3展示了 OrderItem 类的一个例子,该类的接口定义了一个返回相关结果类型的方法。

代码清单A-3 使用 instancetype 关键字返回相关结果类型的值

@interface OrderItem : NSObject
+ (instancetype)orderItemWithDescription:(NSString *)description 
                                   price:(float)cost;
@end

A.2.3 类定义类型

类定义类型定义了 Objective-C 运行时数据结构。它们包括 SEL、IMP、Class、objc_object类型。

1. SEL 类型

SEL 类型代表方法选择器,是一种在 Objective-C 运行时环境中代表方法名称的数据类型。使用编译器指令 @selector,可以为名为 helloworld 的方法选择器创建 SEL 类型的变量:

SEL myHelloMethod = @selector(helloWorld);

该选择器可用于调用对象中的 helloWorld 方法。

2. IMP 类型

IMP 类型代表一种方法指针,这种指针指向实现方法的函数的内存起始地址。下面是定义 IMP 的类型的语法:

typedeff id (*IMP)(id, SEL, ...);

如这个类型定义所示,被 IMP 类型指向的函数接收一个 id 类型、一个方法选择器(SEL)和可变参数(由可变参数符号...代表)作为参数,并返回 id 类型的结果。

3. Class 类型

Class 类型代表 Objective-C 类实例(即对象),由各种数据元素构成。下面是定义 Class 数据类型的语法:

typedef struct objc_class *Class;

如这个类型定义所示,Class 数据类型是一种指针,这种指针指向带 objc_class 标识符的不透明类型。objc_class 结构的成员只能由专门定义的函数(尤其是使用 Objective-C 运行时库函数)访问。

4. objc_object 类型

Objective-C 对象拥有相应的运行时数据类型。当编译器解析 Objective-C 对象的代码时,会生成创建运行时对象类型的代码,即 objc_object 类型。如代码清单A-4所示, 这种数据类型是一种带 objc_object标识符的 C 语言结构。

代码清单A-4 objc_object

struct objc_object
{
    Class isa;
    /* ...含有实例变量值的可变长度数据... */
}

objc_object类型含有一个变量,该变量的类型为 Class、名称为 isa;换言之,它是一个指向 objc_class 类型变量的指针。

A.2.4 块类型

块提供了创建一组语句(即语句块)并将其赋予变量(以便在将来调用)的方式。块类似于 C 语言的标准函数,但块除了是可执行代码外,还含有与堆内存或栈内存绑定的变量。块是一种 Objective-C 对象(类型为块),而且与所有 Objective-C 对象一样,它也是通过指针被访问的。像处理 Objective-C 对象一样,使用 id 类型可以引用块对象。下面的语句声明了一个名为 incrementSum 的块类型变量,该变量拥有一个类型为 int 的参数并且不会返回任何值。

void (^incrementSum)(int);

可以使用存储修饰符 __block 创建块变量。它可以应用于同一词法范围内、块常量外部的变量,并使用该变量能够在这个块常量中修改。该存储修饰符无法与本地存储修饰符 auto、register 和 static 应用于同一个变量。代码清单A-5给出了 __block 存储修饰符的用法。

代码清单A-5 __block 存储修饰符的用法

__block int totalSum = 0;
void (^incrementSum)(int) = ^(int amount) {
    totalSum += amount;
    NSLog(@"Current total = %d", totalSum);
};
incrementSum(5);

执行代码清单A-5所示代码,会输出如下结果;

Current total = 5

因此,块变量 totalSum 被初始化为0,然后在调用该块常量时被修改。

A.2.5 枚举类型

Objective-C 支持标准的 C 语言枚举类型,并且为指定枚举的类型提供了支持。枚举类型由使用关键 enum 定义的一系列已命名值(也称为“枚举器”)构成。被声明为枚举型的变量可以被赋予任何枚举器值。代码清单A-6定义了含有一组值的枚举型变量,该变量的名称为 color,含有 Red、Green 和 Blue 值。

代码清单A-6 枚举型定义示例

enum
{
    Red,
    Green,
    Blue,
} color;

枚举器的默认类型为 int。Objective-C 支持显示地设置枚举的类型,这也称为“含有固定基础类型的枚举类型”。枚举支持的基础类型是带符号或不带符号的整型(char、short、int、long)。类型应该在 enum 关键字之后指定,而且前面要加冒号(:)。代码清单A-7所示的代码定义了一个枚举型变量,它的名称为 color,含有一个组类型为 unsigned char 的值:Red、Greed、Blue。

代码清单A-7 含有固定基础类型的枚举类型

enum : unsigned char
{
    Red,
    Green,
    Blue,
} color;

通常,可使用类型定义声明枚举类型。代码清单A-8所示的代码使用类型定义声明了一个枚举型变量,该变量的名称为 color,含有一组类型为 unsigned char 的值:Red、Greed、Blue。

代码清单A-8 含有固定基础类型的枚举类型定义

typedef enum : unsigned char
{
    Red,
    Green,
    Blue,
} ColorType;
ColorType color;

在 Objective-C 中,定义枚举型值的首选方式是用 NS_ENUM 宏。该宏提供一种约定机制来声明枚举值的基础类型,并使用一条语句定义新的枚举类型。代码清单A-9使用 NS_ENUM 宏定义了代码清单A-8中定义的枚举类型。

代码清单A-9 使用 NS_ENUM 宏定义枚举类型

typedef NS_ENUM(unsigned char, ColorType)
{
    Red,
    Green,
    Blue,
};

如代码清单A-9所示,这个宏的第一个参数是基础类型,而它的第二个参数是新枚举类型的名称。像普通块语句一样,这些值会存储在语句块中。

A.3 操作符

Objective-C 支持 ANSI C 定义的操作符,尤其是下面这些操作符:

  • 赋值操作符
  • 算术操作符
  • 复合赋值操作符
  • 增量和减量操作符
  • 逻辑比较操作符
  • 逻辑判断操作符
  • 逐位运算操作符
  • 复合逐位运算操作符
  • 三元运算操作符
  • 类型转换操作符
  • sizeof操作符
  • 地址操作符(&)
  • 间接操作符(*)
  • 逗号操作符

要详细了解这些算术转换法则以及这些操作符的优先等级和关系,请参阅 ANSI C 的规定。

脱字操作符(^)用于处理块对象,特别是声明块对象类型和定义块常量。

A.4 语句

Objective-C 支持 ANSI C 定义的条件、循环和跳转语句,尤其是下面这些语句:

  • 条件语句(if、if else、if else if、switch)
  • 循环语句(for、while、do while)
  • 跳转语句(return、goto、break、continue)

A.5 类元素

Objective-C 中的类由多个结构化元素构成,这些元素通常分为几个部分。本节概括介绍这些基本类元素。

A.5.1 实例变量

实例变量是为类声明的变量,用于在类实例的生命周期中保存实例的值。实例变量会在对象被初次创建时被分配内存,并在对象被释放时被释放内存。实例变量拥有与对象对应的默认作用范围和命名空间。可以在类接口、类实现文件或类扩展的实例变量声明语句块中声明实例变量。实例变量的声明语句块必须紧跟相应的接口、实现文件和类扩展声明语句。代码清单A-10展示了声明 Hello 类实例变量的例子。

代码清单A-10 Hello类实现文件中声明实例变量的语句块

@implementation Hello
{
    // 在此处声明实例变量
    ...
}
...
@end

建议在类的实现文件(而不是公共的类接口)中声明实例变量。通常应在类实现文件专用的变量扩展中声明实例变量。

实例变量的所有权限定符

Objective-C 所有权限定符是一种只能应用于可保留对象指针的类型限定符。要设置变量的所有权规则,应在变量声明中将所有权限定符放置在对象指针类型之前。表A-2列出了4个所有权限定符。

限定符 描述
__autoreleasing 当变量被赋予新值时,新值会被保留和自动释放,而旧值会被发送释放消息
__weak 变量使用简单赋值操作(即不复制或保留输入值)。如果对象被释放了,相应的变量值会被设置为nil
__unsafe_unretained 变量使用简单赋值操作(即不复制或保留输入值)
__strong 当变量被赋予新值时,新值会被保留,而旧值会被发送释放消息

默认的所有权限定符为__strong(在没有明确设置限定符的情况下)。下面语句声明了一个名为hello的变量(类型为 Hello *),其所有权限定符为 __weak。

__weak Hello *hello;

A.5.2 属性

在 Objective-C 中,属性通过访问方法(即获取方法和设置方法),为访问对象的共享状态提供了一种便利机制。属性与实例变量的区别在于,属性不直接访问对象的内部状态。在处理 Objective-C 属性时,编译器可以根据你提供的规格自动生成访问方法。

可以使用 @property 关键字声明属性,后跟一系列可选特性(封闭在圆括号内)、属性类型和属性名称。

 @property (特性) 类型 属性名称;

属性声明特性用于设置存储语义和其他属性行为。表A-3列出了可用的属性特性。

表A-3 属性的特性

属性 描述 注释
readwrite 必选 访问限定符。默认设置
readonly 必选 访问限定符。无法同时选择 readwrite 和 readonly
assign 使设置方法使用简单赋值操作(即不复制或保留输入值) 所有权限定符。在手动内存管理方式(MRR)中使用。MRR 中的默认值
weak 使设置方法使用简单赋值操作。如果属性实例被释放了,那么支持属性的实例变量会被设置为 nil 所有权限定符。在 ARC 内存管理方式中使用
unsafe_unretained 使设置方法使用简单赋值操作 所有权限定符。在 ARC 内存管理方式中使用
copy 在使用设置方法赋值时,调用复制方法并向旧值发送释放信息 所有权限定符
retain 在设置方法中,旧值会被发送释放消息,而属性会使用保留赋值操作 所有权限定符。与强引用特性类似(在手动内存管理方式中使用)
strong 在设置方法中,旧值会被释放消息,而属性会使用保留赋值操作 在 ARC 内存管理方式中使用的所有权限定符。ARC 中的默认设置
getter=getterName 使用“获取方法的名称”指定获取方法的名称 ----
setter=setterName 使用“设置方法的名称”指定设置方法的名称 ----
nonatomic 将属性的访问方法设置为非自动生成(默认设置)。nonatomic 属性拥有更好的性能,但无法保证当多个线程访问属性时,能够通过属性访问方法返回具有线程安全性的值 ----

属性定义是在类的实现文件中实现的。在大多数情况下,属性是由实例变量支持的,因此属性定义包括定义属性的获取方法和设置方法、声明实例变量以及在获取/设置方法中使用这些实例变量。 Objective-C 提供了3种定义属性的方式:显示定义、通过关键字补全和自动补全。

A.5.3 方法

Objective-C 既支持实例方法,也支持类方法。实例方法是指在类实例(对象)中调用的方法,因此,实例方法可以直接访问对象的实例变量。类方法是指在类中调用的方法。可以在类的接口、协议和类别(category)中声明方法。因此,应在相应的类/类别的实现文件中定义已声明的类。

方法的声明设置了方法的类型、返回值的类型、方法的名称、方法的参数和相应的参数类型。方法的类型标识符指明了该方法是类方法还是实例方法。类方法的声明中含有一个加号(+),它表明方法的作用范围为类,这意味着方法作用类一级,无法访问类中的实例变量(除非将它们作为参数传递给方法)。实例方法的声明中含有一个减号(-),它表明这些方法的作用范围为对象。这意味着这些方法作用于对象一级,而且可以直接访问对象及其父对象中的实例变量(在遵守这些实例变量访问控制规定的情况下)。

方法的名称中含有参数和冒号。每个参数的类型都是在圆括号中设置的,后跟方法原型参数。参数的名称和类型由冒号分隔,如下例所示:

-(void)incrementSum:(int)value;

该方法的名称为 incrementSum:,它拥有一个 int 型的参数 value。如果方法含有多个参数,就需要多个名称-参数对,如下例所示:

-(int)addAddend1:(int)a1 addend2:(int)a2;

该方法的名称为 addAddend1:addend2:,它拥有两个 int 型的参数 a1 和 a2。

返回类型用于设置方法返回变量的类型(如果存在返回变量)。返回类型是在圆括号中设置的,后跟方法的类型。如果方法没有返回值,那么就应将返回类型声明为 void。

下面的语句声明了一个类方法,它的名称为 hydrogenWithProthons:neutrons:,拥有两个 unsigned int 型的参数 nProtons 和 nNeutrons。

+ (void)hydrogenWithProthons:(unsigned)nProtons neutrons:(unsigned)nNeutrons;

A.5.4 接口

类接口设置了类的状态和行为,封装了实例变量、属性和方法。类接口的声明以 @interface 指令和类名称开头,以 @end 指令结束。代码清单A-11展示了声明类接口的正式语法。

代码清单A-11 类接口的声明语法

@interface 类的名称 : 父类的名称
{
    // 实例变量的声明
    ...
}
    // 属性和方法的声明
    ...
@end

父类创建了通用接口和实现代码,子类可以继承、修改和补充这些元素。Objective-C 中的绝大多数类都是 NSObject 类的子类,NSObject 类是大多数 Foundation 框架的根类(基类),它提供了 Objective-C 运行时环境的基础接口。

提前声明

使用 @class 指令可以在获取类的完整规格前,在类接口中声明类。这样做可以在无须导入相应头文件的情况下,告诉编译器类的存在,从而使编译器能够执行静态类型检查。提前声明可用于解决文件之间的循环依赖问题。

A.5.5 实现代码

类的实现文件通过实现类的属性和方法定义了类的行为。类实现文件以 @implementation 指令和类的名称开头,以 @end 指令结束。代码清单A-12展示了声明类实现文件的正式语法。

代码清单A-12 类实现文件的语法

@implementation 类的名称
{
    // 实例变量的声明
    ...
}
    // 属性和方法的定义
    ...
@end

在类实现文件中定义的方法必须直接与相应的类接口对应,例如,代码清单A-13所示的代码创建了 Greeting 类的接口和相应的实现代码。

代码清单A-13 Greeting类接口和实现代码

#import <Foundation/Foundation.h>

@interface Greeting : NSObject
@property (readwrite) NSString *salutation;
-(void)hello:(NSString *)user;
@end

#import "Greeting.h"
@implementation Greeting
-(void)hello:(NSString *)user
{
    NSLog(@"%@, %@", self.salutation, user);
}
@end

A.5.6 协议

协议规定了类可以实现的方法和属性。协议的声明以 @protocol 指令开头,后跟协议的名称,以 @end 指令结束。协议中含有必选和可选方法。协议的实现代码无须实现可选方法。@required 指令和 @optional 指令(后跟一个或多个方法名称)用于设置必选和可选方法。如果没有明确设置这两个关键字中的任何一个,那么默认为使用 @required 指令。代码清单A-14展示了声明协议的语法。

代码清单A-14 声明协议的语法

@protocol 协议的名称
// 属性的声明
@required
// 方法的声明
@optional
// 方法的声明
@end

通过在尖括号中设置其他协议,可以组合使用协议,这称为遵守其他协议。逗号用于分隔多个协议,如代码清单A-15所示。

代码清单A-15 组合使用多个协议

@protocol 协议的名称<协议的名称>
// 方法的声明
@end

通过类似的语法可以使用接口遵守多个协议,如代码清单A-16所示。

代码清单A-16 使接口遵守协议的语法

@interface 类的名称 : 父类的名称<协议的名称>
// 方法的声明
@end

通常,类的接口和实现代码存储在不同的文件中,存储接口的头文件拥有 .h 后缀,而实现文件拥有 .m 后缀。

A.5.7 类别

通过类别(category)可在不进行子类化的情况下,为已经存在的类添加功能。类别中的方法会变成类的组成部分(在程序范围之内),并且会被该类的所有子类继承。

类别接口声明以 @interface 关键字开头,后跟已经存在的类名称、由圆括号封装的类别名称以及类别遵守的协议(如果存在),以 @end 关键字结束。应在这些语句之间添加方法的声明。代码清单A-17展示了声明类别的语法。

代码清单A-17 声明类别的语法

@interface 类的名称(类别的名称)
// 方法的声明
@end

与类的实现文件相似,类别在其实现中定义方法。代码清单A-18展示了类别实现的语法:

代码清单A-18 类别实现的语法

@implementation 类的名称(类别的名称)
// 方法的定义
@end

A.5.8 类扩展

可以将类扩展为匿名类别。在类扩展中声明的方法必须在相应类的实现文件中实现。与类别相反,类扩展可以声明实例变量和属性。代码清单A-19展示了类扩展的语法:

代码清单A-19 声明类扩展的语法

@interface 类的名称()
{
    // 实例变量的声明
}
// 属性的声明
// 方法的声明
@end

通常,可以将类扩展放在类的实现文件中,使用它们组织和声明额外的、必要的私有方法(如不属于已声明的公用 API 的方法),以单独在某个类中使用。

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

推荐阅读更多精彩内容