diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md index 36f02c3..a2f9d4d 100644 --- a/README.md +++ b/README.md @@ -8,42 +8,43 @@ The Swift Programming Language 中文化项目 # 任务和进度 * 欢迎使用 Swift - * Swift 介绍 [已完成 by 飞长] - * Swift 初见 [认领 by 容隽] + * Swift 介绍 [已完成 by 飞长] [review by 容隽] + * Swift 初见 [已完成 by 容隽] [review by 闻西] * Swift 教程 - * 基础部分 [认领 by 琼雪] - * 基本操作符 [认领 by 冰浠] - * 字符串和字符 [认领 by 筱谷] - * 集合类型 [认领 by 尘境] - * 控制流 [认领 by 墨昕] - * 函数 [认领 by 紫溪] - * 闭包 [认领 by 闻西] - * 枚举 [认领 by 灵吾] - * 类和结构体 [认领 by 晓毒] - * 属性 [认领 by 周源] - * 方法 [认领 by 米尔] - * 下标 [已完成 by 递归] - * 继承 [认领 by 晗光] - * 构造过程 [认领 by 刘康] - * 析构过程 [认领 by 许诺] - * 自动引用计数 [认领 by 韩国兴/MK] - * 可选链 [认领 by 重鱼] - * 类型转换 [认领 by 孟志昂] - * 嵌套类型 [认领 by 祁涛] - * 扩展 [认领 by 袁鹏] - * 协议 [认领 by 姜天意] - * 泛型 - * 高级操作符 [认领 by 林晚] + * 基础部分 [已完成 by 琼雪] [review by 栖邀] + * 基本操作符 [已完成 by 冰浠] [review by 姜天意] + * 字符串和字符 [已完成 by 筱谷] [review by 尘境] + * 集合类型 [已完成 by 尘境] [review by 紫溪] + * 控制流 [已完成 by 墨昕] + * 函数 [已完成 by 紫溪] [review by 飞长] + * 闭包 [已完成 by 闻西] [review by 米尔] + * 枚举 [已完成 by 灵吾] [review by 筱谷] + * 类和结构体 [已完成 by 晓毒] + * 属性 [已完成 by 周源] [review by 林晚] + * 方法 [已完成 by 米尔] [review by 灵吾] + * 下标 [已完成 by 递归] [review by 琼雪] + * 继承 [已完成 by 晗光] [review by 递归] + * 构造过程 [已完成 by 刘康] [review by 晓毒] + * 析构过程 [已完成 by 许诺] [review by 祁涛] + * 自动引用计数 [已完成 by 韩国兴/MK] [review by 孟志昂] + * 可选链 [已完成 by 重鱼] [review by 玩家] + * 类型转换 [已完成 by 孟志昂] [review by 耿霄] + * 嵌套类型 [已完成 by 祁涛] [review by 袁鹏] + * 扩展 [已完成 by 袁鹏] [review by 姜天意] + * 协议 [已完成 by 姜天意] [review by 重鱼] + * 泛型 [已完成 by 晴时] [review by 隐若] + * 高级操作符 [认领 by 林晚] [review by 周源] * 语言参考 - * 关于语言参考 [认领 by 筱强] - * 词法结构 [认领 by 筱强] - * 类型 [认领 by 兰梦] - * 表达式 [认领 by 懂象] - * 声明 - * 属性 - * 模式 - * 泛型参数 - * 语法总结 + * 关于语言参考 [已完成 by 筱强] [review by 懂象] + * 词法结构 [已完成 by 筱强] [review by 懂象] + * 类型 [已完成 by 兰梦] [review by 筱强] + * 表达式 [已完成 by 懂象] [review by 兰梦] + * 语句 [已完成 by 玩家] + * 声明 [已完成 by 墨峰] [review by 龙刚] + * 属性 [已完成 by 隐若] [review by 姜天意] + * 模式 [已完成 by 栖邀] [review by 紫溪] + * 泛型参数 [已完成 by 龙刚] [review by 墨峰] + * 语法总结 [已完成 by 无独] [review by 晗光] # 分工 * 西溪分会 10人 第一部分1-2章 第二部分1-8章 @@ -66,13 +67,13 @@ The Swift Programming Language 中文化项目 建库时间点:6月7日 (6月7日 - 已完成) -三大分会参与翻译工作具体人员确定时间点:6月8日 (进行中) +三大分会参与翻译工作具体人员确定时间点:6月8日 (已完成) -认领完成结束时间点:6月10日 (未开始) +认领完成结束时间点:6月10日 (已完成) -原文翻译完成时间点:6月20日 (未开始) +原文翻译完成时间点:6月20日 (已完成) -Review完成时间点:6月27日 (未开始) +Review完成时间点:6月27日 (进行中) # 关于术语 diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..bef9842 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,41 @@ +# Summary + +* 欢迎使用 Swift + * [Swift 介绍](src/chapter1/01_About_Swift.md) + * [Swift 初见](src/chapter1/02_A_Swift_Tour.md) +* Swift 教程 + * [基础部分](src/chapter2/01_The_Basics.md) + * [基本操作符](src/chapter2/02_Basic_Operators.md) + * [字符串和字符](src/chapter2/03_Strings_and_Characters.md) + * [集合类型](src/chapter2/04_Collection_Types.md) + * [控制流](src/chapter2/05_Control_Flow.md) + * [函数](src/chapter2/06_Functions.md) + * [闭包](src/chapter2/07_Closures.md) + * [枚举](src/chapter2/08_Enumerations.md) + * [类和结构体](src/chapter2/09_Classes_and_Structures.md) + * [属性](src/chapter2/10_Properties.md) + * [方法](src/chapter2/11_Methods.md) + * [下标](src/chapter2/12_Subscripts.md) + * [继承](src/chapter2/13_Inheritance.md) + * [构造过程](src/chapter2/14_Initialization.md) + * [析构过程](src/chapter2/15_Deinitialization.md) + * [自动引用计数](src/chapter2/16_Automatic_Reference_Counting.md) + * [可选链](src/chapter2/17_Optional_Chaining.md) + * [类型转换](src/chapter2/18_Type_Casting.md) + * [嵌套类型](src/chapter2/19_Nested_Types.md) + * [扩展](src/chapter2/20_Extensions.md) + * [协议](src/chapter2/21_Protocols.md) + * [泛型](src/chapter2/22_Generics.md) + * [高级操作符](src/chapter2/23_Advanced_Operators.md) +* 语言参考 + * [关于语言参考](src/chapter3/01_About_the_Language_Reference.md) + * [词法结构](src/chapter3/02_Lexical_Structure.md) + * [类型](src/chapter3/03_Types.md) + * [表达式](src/chapter3/04_Expressions.md) + * [语句](src/chapter3/05_Statements.md) + * [声明](src/chapter3/06_Declarations.md) + * [属性](src/chapter3/07_Attributes.md) + * [模式](src/chapter3/08_Patterns.md) + * [泛型参数](src/chapter3/09_Generic_Parameters_and_Arguments.md) + * [语法总结](src/chapter3/10_Summary_of_the_Grammar.md) + diff --git a/src/chapter1/02_A_Swift_Tour.md b/src/chapter1/02_A_Swift_Tour.md index e69de29..b2d2ede 100644 --- a/src/chapter1/02_A_Swift_Tour.md +++ b/src/chapter1/02_A_Swift_Tour.md @@ -0,0 +1,211 @@ +A Swift Tour + +Tradition suggests that the first program in a new language should print the words “Hello, world” on the screen. In Swift, this can be done in a single line: +通常学习一门新语言都会输出一行“Hello,world”.在swift里只需要一行代码 + +“If you have written code in C or Objective-C, this syntax looks familiar to you—in Swift, this line of code is a complete program. You don’t need to import a separate library for functionality like input/output or string handling. Code written at global scope is used as the entry point for the program, so you don’t need a main function. You also don’t need to write semicolons at the end of every statement.” +如果你之前写过C或者Objective-C的代码,你会很熟悉这种语法。在swift里,这一行代码就是一个完整的程序,你不需要再去引入一个完成类似输入输出,字符串处理等操作的类库。在全局范围内编写的代码就是程序的入口,因此你不再需要main函数了,也不需要在每一条语句末尾写分号。 + +“This tour gives you enough information to start writing code in Swift by showing you how to accomplish a variety of programming tasks. Don’t worry if you don’t understand something—everything introduced in this tour is explained in detail in the rest of this book.” +这篇文章会充分向你展示如何用swift完成各种编程任务。不用担心一些细节不太理解,在这本书之后的章节中会详细的为你介绍。 + + +“Simple Values +Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once. This means you can use constants to name a value that you determine once but use in many places.” +简单值 +使用let声明常量,var声明变量。在编译的时候不需要知道常量的值,但是你只能给常量显式地赋值一次。也就是说你可以给常量赋一次值然后在其他一些地方来使用。 + +“A constant or variable must have the same type as the value you want to assign to it. However, you don’t always have to write the type explicitly. Providing a value when you create a constant or variable lets the compiler infer its type. In the example above, the compiler infers that myVariable is an integer because its initial value is a integer.” +不管是常量还是变量必须与其所赋的值类型保持一致。你不一定要把常量或者变量的类型显式地写出来。因为编译器可以根据值来自动推断常量或者变量的类型。如上面的例子,编译器根据myVariable的初始值类型推断出它是一个整型变量。 + +“If the initial value doesn’t provide enough information (or if there is no initial value), specify the type by writing it after the variable, separated by a colon.” +如果初始值没有提供足够的信息或者没有初始值,可以在变量后面写上它的类型,变量和类型用冒号隔开。 + +“Values are never implicitly converted to another type. If you need to convert a value to a different type, explicitly make an instance of the desired type.” +值的类型不会自动转换,如果你需要把一个值转换成另外一种类型,需要强制转换。 + +“There’s an even simpler way to include values in strings: Write the value in parentheses, and write a backslash (\) before the parentheses. For example:” +有一种更简单的把值转换成字符串的方法:把值写在括号里,然后在括号前加一个反斜杠。例如: + +“Create arrays and dictionaries using brackets ([]), and access their elements by writing the index or key in brackets.” +用中括号创建数组和字典,通过中括号里的索引和键来访问集合中的元素。 + +“To create an empty array or dictionary, use the initializer syntax.” +用下面的初始化语法来创建一个空的数组或字典 + +“If type information can be inferred, you can write an empty array as [] and an empty dictionary as [:]—for example, when you set a new value for a variable or pass an argument to a function.” +如果类型可以被推断出来,你可以用[]来创建一个空数组,用[:]来创建一个空字典。例如,当你给一个变量赋值或者给一个函数传参 + +“Control Flow +Use if and switch to make conditionals, and use for-in, for, while, and do-while to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required.” +流控制 +使用if和switch来进行条件控制,使用for-in、for、while和do-while来进行循环控制。条件和循环变量外面的括号可以省略,但是语句体的大括号是必须的。 + +“In an if statement, the conditional must be a Boolean expression—this means that code such as if score { ... } is an error, not an implicit comparison to zero.” +在if语句里,条件必须是一个Boolean表达式。所以if score { ... }这种写法是错误的,score不会隐式地和0来比较。 + +“You can use if and let together to work with values that might be missing. These values are represented as optionals. An optional value either contains a value or contains nil to indicate that the value is missing. Write a question mark (?) after the type of a value to mark the value as optional.” +你可以一起使用if和let来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是nil,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。 + +“If the optional value is nil, the conditional is false and the code in braces is skipped. Otherwise, the optional value is unwrapped and assigned to the constant after let, which makes the unwrapped value available inside the block of code.” +如果变量的可选值是nil,则判断条件为false,大括号中的代码被略过。如果不为nil,可选值会赋值给let后的常量,并且该常量可以在代码块中使用。 + +“Switches support any kind of data and a wide variety of comparison operations—they aren’t limited to integers and tests for equality.” +Switch支持各种类型的数据以及大量的比较操作-它们不仅限于整数和比较大小。 + +“After executing the code inside the switch case that matched, the program exits from the switch statement. Execution doesn’t continue to the next case, so there is no need to explicitly break out of the switch at the end of each case’s code.” +与switch的case相匹配的代码执行完之后,程序不会继续执行下一个case中的代码,而是会自动退出switch语句,所以不需要在每一个case的代码后面显式的调用break。 + +“You use for-in to iterate over items in a dictionary by providing a pair of names to use for each key-value pair.” +你可以使用for-in来遍历字典,用两个变量来表示每对键值。 + +“Use while to repeat a block of code until a condition changes. The condition of a loop can be at the end instead, ensuring that the loop is run at least once.” +使用while来重复执行一段代码直到条件改变。循环条件也可也在末尾,以保证循环至少可执行一次。 + +“You can keep an index in a loop—either by using .. to make a range of indexes or by writing an explicit initialization, condition, and increment. These two loops do the same thing:” +在循环中可以使用..来表示索引范围或者使用传统的方式。两者是等价的。 + +“Use .. to make a range that omits its upper value, and use ... to make a range that includes both values.” +使用..会忽略范围中的上限值,使用...则不会忽略。 + +“Functions and Closures +Use func to declare a function. Call a function by following its name with a list of arguments in parentheses. Use -> to separate the parameter names and types from the function’s return type.” +函数和闭包 +使用func来声明一个函数。在函数名后的括号里加入参数来调用函数。使用->分隔开函数的参数和函数的返回值类型。 + +“Use a tuple to return multiple values from a function.” +使用元组来返回一组值。 + +“Functions can also take a variable number of arguments, collecting them into an array.” +函数可以有一组可变个数的参数,由array来表示。 + +“Functions can be nested. Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that is long or complex.” +函数可以嵌套,被嵌套的函数可以访问外部函数中声明的变量。你可以使用嵌套函数来重构一段很长很复杂的函数。 + +“Functions are a first-class type. This means that a function can return another function as its value.” +函数是first-class类型,因此它可以作为另一个函数的返回值。 + +“A function can take another function as one of its arguments.” +函数可以作为另一个函数的参数。 + +“Functions are actually a special case of closures. You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.” +函数实际上是一个特殊的闭包。你可以把一段代码写在大括号{}中来表示一个匿名闭包,参数和返回类型与闭包体之间用in来分隔。 + +“You have several options for writing closures more concisely. When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.” +有一些简洁表示闭包的方式。当一个闭包的类型已知,例如作为委托的回调,可以省略它的参数和返回类型。单个语句闭包会把它语句的值当做结果返回。 + +“You can refer to parameters by number instead of by name—this approach is especially useful in very short closures. A closure passed as the last argument to a function can appear immediately after the parentheses.” +你可以使用参数位置替代参数名来引用参数-这个方法特别是在很短的闭包里很好用。闭包作为函数最后一个参数的时候,可以直接跟在小括号后面。 + +“Objects and Classes +Use class followed by the class’s name to create a class. A property declaration in a class is written the same way as a constant or variable declaration, except that it is in the ” +“context of a class. Likewise, method and function declarations are written the same way.” +对象和类 +使用class和类名来创建一个类。类中的属性声明是和常量或者变量的声明方式一样的,唯一的区别是属性声明是在类的上下文之中的。同样的,方法和函数的声明也是一样的。 + +“Create an instance of a class by putting parentheses after the class name. Use dot syntax to access the properties and methods of the instance.” +类名后面跟小括号来创建一个类的实例。使用点语法来访问该实例的属性和方法。 + +“This version of the Shape class is missing something important: an initializer to set up the class when an instance is created. Use init to create one.” +这个版本的Shape类缺少了一个重要的东西:类实例化时候的一个构造器。使用init来创造一个构造器。 + +“Notice how self is used to distinguish the name property from the name argument to the initializer. The arguments to the initializer are passed like a function call when you create an instance of the class. Every property needs a value assigned—either in its declaration (as with numberOfSides) or in the initializer (as with name).” +注意用self来区分name属性和传递给构造器的name参数。像函数调用一样,把参数传递给构造器来创建一个类实例。每一个属性都需要赋值,在属性的声明中(如numberOfSides),或者在构造器中(如name) + +“Use deinit to create a deinitializer if you need to perform some cleanup before the object is deallocated.” +“Subclasses include their superclass name after their class name, separated by a colon. There is no requirement for classes to subclass any standard root class, so you can include or omit a superclass as needed.” +使用deinit来创建一个析构器来执行对象销毁前的清理工作。 +定义子类的时候是在类名后加上父类的名字,中间用冒号隔开。因为类不需要继承任何标准的基类,所以你可以省略父类。 + +“Methods on a subclass that override the superclass’s implementation are marked with override—overriding a method by accident, without override, is detected by the compiler as an error. The compiler also detects methods with override that don’t actually override any method in the superclass.” +子类方法重写父类方法的时候标记为override-这是为了防止意外的重写,如果没有override关键字,编译器会报错。编译器同样会检测在子类标记为override的方法是否真的重写了父类的某个方法。 + +“In addition to simple properties that are stored, properties can have a getter and a setter.” +属性也可以有getter和setter方法。 + +“In the setter for perimeter, the new value has the implicit name newValue. You can provide an explicit name in parentheses after set.” +在setter方法中,新的属性值被隐式地命名为newValue。你可以在set后的括号里显式地给属性值命名。 + +“Notice that the initializer for the EquilateralTriangle class has three different steps: +Setting the value of properties that the subclass declares. +Calling the superclass’s initializer. +Changing the value of properties defined by the ” +“superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.” +注意创建EquilateralTriangle类的构造器包含三个不同的步骤。 +1.在子类声明的时候设置属性值。 +2.调用父类的构造器。 +3.改变定义在父类中的属性值,还有调用方法,getter和setter方法都可以在这个步骤完成。 + +“If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet. For example, the class below ensures that the side length of its triangle is always the same as the side length of its square.” +如果你不需要计算属性,但仍需要在给属性设置新值的前后运行一些代码,使用willSet和didSet。例如,下面的类会确保三角形的边长和方形的边长相等。 + +“Methods on classes have one important difference from functions. Parameter names in functions are used only within the function, but parameters names in methods are also used when you call the method (except for the first parameter). By default, a method has the same name for its parameters when you call it and within the method itself. You can specify a second name, which is used inside the method.” +类中的方法和函数有一个很重要的区别。函数中的参数只会在函数内部使用,但是方法中的参数还可以在方法调用的时候使用(除了第一个参数)。默认情况下,方法调用时候的参数名和方法内部使用的参数名保持一致。在方法内部使用的时候你也可以定义一个不同的名字。 + +“When working with optional values, you can write ? before operations like methods, properties, and subscripting. If the value before the ? is nil, everything after the ? is ignored and the value of the whole expression is nil. Otherwise, the optional value is unwrapped, and everything after the ? acts on the unwrapped value. In both cases, the value of the whole expression is an optional value.” +处理可选值的时候,你可以在?后面跟上方法,属性,子脚本等操作。如果?之前的值是nil,?后面的代码会被忽略,然后整个表达式的值返回nil。否则,?之后的代码会执行。在这两种情况下,整个表达式就是一个可选值。 + +“Enumerations and Structures +Use enum to create an enumeration. Like classes and all other named types, enumerations can have methods associated with them.” +枚举和结构体 +使用enum来创建一个枚举。像类和其他命名类型一样,枚举可以有方法。 + +“In the example above, the raw value type of the enumeration is Int, so you only have to specify the first raw value. The rest of the raw values are assigned in order. You can also use strings or floating-point numbers as the raw type of an enumeration.” +在上面的例子中,枚举的原始值类型是Int,所以你只需要指定第一个值,其余的会按照顺序进行赋值。你同样可以使用字符串和浮点数作为枚举的原始类型。 + +“Use the toRaw and fromRaw functions to convert between the raw value and the enumeration value.” +使用toRaw和fromRaw这两个函数用于在原始值和枚举值之间转换。 + +“The member values of an enumeration are actual values, not just another way of writing their raw values. In fact, in cases where there isn’t a meaningful raw value, you don’t have to provide one.” +枚举的成员值是实际值,并不是原始值的另一种表示方式。实际上,如果原始值没有意义,你就不需要提供。 + +“Notice the two ways that the Hearts member of the ” +“enumeration is referred to above: When assigning a value to the hearts constant, the enumeration member Suit.Hearts is referred to by its full name because the constant doesn’t have an explicit type specified. Inside the switch, the enumeration is referred to by the abbreviated form .Hearts because the value of self is already known to be a suit. You can use the abbreviated form anytime the value’s type is already known.” +注意,有两种方式可以引用Hearts成员:当给hearts常量赋值时,通过Suit.Hearts全名来引用枚举成员,因为常量没有显式指定其类型。在switch语句内部,因为已知self是一个suit,所以可以通过.Hearts这种简写的方式来引用枚举。在已知值类型的情况下你都可以采用简写的方式。 + +“Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.” +使用struct来创建一个结构体。结构体支持很多和类同样的操作,包括方法和构造器。两者之间最重要的差别是结构体在代码中传递采用的是拷贝的方式,但是类采用的是传递引用的方式。 + +“An instance of an enumeration member can have values associated with the instance. Instances of the same enumeration member can have different values associated with them. You provide the associated values when you create the instance. Associated values and raw values are different: The raw value of an enumeration member is the ”“same for all of its instances, and you provide the raw value when you define the enumeration.” +一个枚举成员的实例可以有对应的实例值。相同枚举成员的实例可以有不同的实例值。当你创建一个实例的时候提供对应的实例值。实例值和原始值是不同的:枚举成员所有的实例的原始值都是相同的,而且你是在定义枚举的时候提供原始值。 + +“For example, consider the case of requesting the sunrise and sunset time from a server. The server either responds with the information or it responds with some error information.” +例如,考虑从服务端获取日出和日落的时间。服务端要么返回正常信息,要么返回错误信息。 + +“Notice how the sunrise and sunset times are extracted from the ServerResponse value as part of matching the value against the switch cases.” +注意,如何从ServerResponse中提去日出和日落时间。 + +Protocols and Extensions +“Use protocol to declare a protocol.” +协议和扩展 +使用protocol来声明一个协议 + +“Classes, enumerations, and structs can all adopt protocols.” +类,枚举,结构体都可以遵循协议 + +“Notice the use of the mutating keyword in the declaration of SimpleStructure to mark a method that modifies the structure. The declaration of SimpleClass doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.” +注意,在SimpleStructure的声明中使用关键字mutating来标记一个会修改结构体的方法。在SimpleClass的声明中不需要用关键字mutating来标记类中的方法,因为类中的方法总是可以修改类。 + +“Use extension to add functionality to an existing type, such as new methods and computed properties. You can use an extension to add protocol conformance to a type that is declared elsewhere, or even to a type that you imported from a library or framework.” +使用扩展给一个已存在的类型增加功能,例如一些新方法和计算过的属性。你可以使用扩展给一个在别处声明的类型增加协议,甚至是给一个从库和框架中引入的类型增加协议。 + +“You can use a protocol name just like any other named type—for example, to create a collection of objects that have different types but that all conform to a single protocol. When you work with values whose type is a protocol type, methods outside the protocol definition are not available.” +你可以像使用其他命名类型一样使用协议名-例如,创建一个具有不同类型但是遵循同一协议的对象集合。当你处理一些协议类型的值的时候,协议定义之外的方法是不可用的。 + +“Even though the variable protocolValue has a runtime type of SimpleClass, the compiler treats it as the given type of ExampleProtocol. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.” +虽然变量protocolValue具有一个运行时类型SimpleClass,编译器是把它当做一个协议类型来处理的。这意味着你不能够访问协议没有指定的方法和属性。 + +Generics +“Write a name inside angle brackets to make a generic function or type.” +泛型 +在尖括号中写一个名字来创建一个泛型函数或类型。 + +“You can make generic forms of functions and methods, as well as classes, enumerations, and structures.” +你可以使用泛型来创建函数,方法,类,枚举和结构体。 + +“Use where after the type name to specify a list of ” +“requirements—for example, to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass.” +在类型名之后使用where来指定一些条件-例如,要求类型遵循某个协议,要求两个类型相同,或者要求一个类有一个特定的父类。 + +“In the simple cases, you can omit where and simply write the protocol or class name after a colon. Writing is the same as writing .” +在简单情况下,你可以省略where,然后在协议和类名之间用冒号隔开。 是等价的。 diff --git a/src/chapter2/01_The_Basics.md b/src/chapter2/01_The_Basics.md index e69de29..1106ab0 100644 --- a/src/chapter2/01_The_Basics.md +++ b/src/chapter2/01_The_Basics.md @@ -0,0 +1,678 @@ +# 基础部分 + +Swift是一个用于iOS和OS X平台开发的新的编程语言。尽管如此,Swift在很多地方都会和你以往用C语言和Objective-C开发的经验类似。 + +Swift为所有C和Objective-C的基础类型提供了自己的版本,包括整数值`Int`,浮点值`Double`和`Float`,布尔值`Bool`,以及文本值`String`。Swift还提供了两个强大的常见集合类型,数组`Array`和字典`Dictionary`,见[集合类型](link)。 + +和C一样,Swift使用具有唯一名称的变量(variables)来储存和指向特定值。Swift还对那些值不能改变的变量进行了扩展,他们也被称为常量(Constants),Swift中的常量比C中的常量更加强大。在Swift中当你需要处理一些恒定不变的值的时候,使用常量(Constants)可以让代码更加安全和整洁。 + +除了这些我们熟知的类型以外,Swift还有一些在Objective-C中没有的高级类型,比如可以创建和传递一组数值的元组(tuples)。元组能在函数中以一个复合值的形式返回多个值。 + +Swift还有可选(Optionals)类型,来处理值缺失的情况。可选类型表示“值是x”或者“没有值”。可选类型类似于在Objective-C的指针中使用`nil`,但是可选类型可以用在任意类型上,而不只是类。相比于Objective-C中的`nil`,可选类型(Optionals)更加安全和高效,可选类型也是Swift的众多强大特性的核心。 + +可选类型说明了Swift是一个类型安全的语言。Swift让你清楚的知道你的代码中可用的值的类型。如果你期望这部分代码是字符串(`String`),类型安全性会阻止你错误的给它赋一个整型(`Int`)的值。这让你在开发的过程中尽早的发现和解决问题。 + +# 常量和变量 + +常量和变量将一个变量名(如`maximumNumberOfLoginAttempts`或`welcomeMessage`)和对应类型的值(如数字`10`或字符串`"Hello"`)进行关联。常量的值一旦设定就不能改变,变量的值可以。 + +# 声明常量和变量 + +常量和变量必须在使用前声明。用`let`来声明常量,用`var`来声明变量。下面是一个用常量和变量来记录用户尝试登录次数的例子: + +``` +let maximumNumberOfLoginAttempts = 10 +var currentLoginAttempt = 0 +``` + +这段代码可以解读为: + +声明一个新的常量,叫做`maximumNumberOfLoginAttempts`(最大尝试登陆次数),值为`10`。声明一个新的变量,叫做`currentLoginAttempt`(当前尝试登陆次数),初始值设为`0`。 + +在这个例子中,最大尝试登陆次数被声明为常量,因为这个最大值不会改变,当前尝试登陆次数被声明为变量,因为这个值在每次尝试登陆失败后都要增加。 + +你可以在同一行内声明多个常量或变量,用逗号分割: + +``` +var x = 0.0, y = 0.0, z = 0.0 +``` + +> 注意 + +> 当值不会改变时,永远用`let`声明常量。只在值需要改变的时候用变量。 + +# 类型批注 + +在声明常量和变量时,你可以提供一个类型批注,来表明这个常量或变量可以储存的值的类型。类型批注的格式是在常量或变量名后加一个冒号,接着是一个空格,接着是要使用的类型的名称。 + +下面是一个叫`welcomeMessage`的变量的类型批注的例子,来说明这个变量可以储存`String`(字符串)类型的值: + +``` +var welcomeMessage: String +``` + +这个声明中的冒号表示“类型是”,所以这段代码可以解读为: + +“声明一个叫`welcomeMessage`的变量,类型是`String`(字符串)。” + +“类型是`String`(字符串)”表示“可以储存任意的`String`(字符串)类型的值”。可以把它想作是它可以储存的“一个事物的类型”。 + +变量`welcomeMessage`可以被赋值为任意`String`(字符串)类型的值,而不会报错: + +``` +welcomeMessage = "Hello" +``` + +> 注意 + +> 在实践中你很少需要写类型批注。如果你在定义一个常量或变量时给了它一个初始值,Swift几乎总是可以推断出这个常量或变量使用的类型,参见[类型安全和类型推断](link)。在上面这个welcomeMessage的例子里,没有提供初始值,所以`welcomeMessage`这个变量的类型通过类型批注的形式指定,而不是从初始值推断而来。 + +# 命名常量和变量 + +你可以使用几乎所有你喜欢的符号来命名常量和变量,包括unicode字符: + +``` +let π = 3.14159 +let 你好 = "你好世界" +let 🐶🐮 = "dogcow" +``` + +常量和变量的命名不能包括数学运算符,箭头, 私有(或无效)的unicode码位, 或者线条(line-),以及制表符(box-drawing)字符。也不能以数字开头,尽管数字可以出现在变量名的其他位置。 + +一旦你声明了常量或变量为特定类型,你不能用同样的名称重新声明,不能改变它的储值类型,也不能把常量改为变量或变量改为常量。 + +> 注意 + +> 如果你需要给一个常量或变量一个相同的名字作为swift的关键字,你可以在使用这个名字的时候用重音符(`)把关键字包围起来。然而,除非你没有别的选择,你应该避免使用关键字的方式来命名。 + +你可以将一个已经存在的变量的值改变成另一个适配类型的值。在下面的例子里,变量`friendlyWelcome`的值从`"Hello!"`变成了`"Bonjour!"`: + +``` +var friendlyWelcome = "Hello!" +friendlyWelcome = "Bonjour!" +// friendlyWelcome的值现在是"Bonjour!"了。 +``` + +和变量不同,常量的值一旦设定就不能更改。尝试更改会导致代码编译时报错。 + +``` +let languageName = "Swift" +languageName = "Swift++" +// this is a compile-time error - languageName cannot be changed +``` + +# 输出常量和变量 + +你可以用`printIn`函数输出常量和变量的当前值: + +``` +println(friendlyWelcome) +// 输出 "Bonjour!" +``` + +`printIn`是一个输出值的全局函数,为了得到良好的输出结果,输出后会加上一个空行。如果你使用的是Xcode,`printIn`会将结果输出到Xcode的调试信息栏。(另一个函数,`print`,会执行几乎相同的任务,除了不会在输出结果之后加空行以外。) + +`printIn`函数会输出任何你传递给它的字符串(`String`)值: + +``` +println("This is a string") +// prints "This is a string" +``` + +`printIn`函数可以输出更复杂的记录信息,和Cocoa的`NSlog`函数类似。这些信息可以包含常量和变量的当前值。 + +Swift使用字符串插值(string interpolation)的方式将常量或变量名以占位符的形式加入字符串中,并且提示Swift用常量或变量的当前值替换对应占位符。书写格式是将名字包裹在括号中并在前面加上反斜扛来转义: + +``` +println("The current value of friendlyWelcome is \(friendlyWelcome)") +// 输出 "The current value of friendlyWelcome is Bonjour!" +``` + +> 注意 + +> 关于字符串插值的详细参数,参见[字符串插值](link)。 + +# 注释 + +将代码中不会执行的文本,笔记或者备忘用注释来表达。注释在代码编译时会被忽略。 + +Swift中的注释和C中的注释十分相似。单行的注释以两个正斜杠(```//```)开头: + +``` +// 这是一个注释 +``` + +你也可以写多行的注释,用一个正斜杠跟着一个星号开头(`/*`),用一个星号跟着一个正斜杠结束(`*/`): + +``` +/* 这还是一个注释, +但是是多行的 */ +``` + +和C中的多行注释不同,Swift的多行注释可以嵌套在另一个多行注释内部。你可以这样写嵌套的注释:开始一个多行注释块,在第一块多行注释内部跟着开始第二个多行注释。第二个多行注释块结束,然后第一个多行注释块结束。 + +``` +/* 这是第一个多行注释开始 +/* 这是第二个,嵌套的多行注释 */ +这是第一个多行注释的结束 */ +``` + +嵌套的多行注释让你可以简单快捷的注释一大段代码,即使这段代码之前已经有多行注释块。 + +# 分号 + +和其他很多语言不同,Swift并不要求你在代码的每一个语句之后写一个分号(`;`),尽管如果你愿意你可以这么做。然而,如果你想在一行里写多个独立的语句,分号是必要的。 + + let cat = "🐱"; println(cat) + // prints "🐱" + +# 整数 + +整数就是没有小数部分的完整数字,如`42`和`-23`。整数可以有符号(正数,0,或负数),也可以没有符号(正数或0)。 + +Swift提供8,16,32和64位的带符号和不带符号的整数类型。这些整数遵从和C类似的命名约定,在这个约定中,8位的无符号整数的类型为`UInt8`,而32位的有符号整数的类型是`Int32`。和Swift的所有类型一样,这些整数的类型是首字母大写的。 + +## 整数边界 + +你可以通过整数类型的`max`和`min`属性来访问它的最大值和最小值: + +``` +let minValue = UInt8.min // 最小值是 0, 类型是 UInt8 +let maxValue = UInt8.max // 最大值是 255, 类型是 UInt8 +``` + +这些属性值是相应数字类型的合适范围(比如上面例子里的`UInt8`),因此它们可以在其他拥有相同类型值的表达式中沿用。 + +## 整数 + +在大多数情况下,你不需要在代码中指定整数的范围。Swift提供了一个专门的整数类型`Int`,它和当前平台的原生长度范围相同。 + +* 在32位的平台上,`Int`和`Int32`范围相同。 +* 在64位的平台上,`Int`和`Int64`范围相同。 + +如果你不需要处理特定的整数范围,请在代码中始终使用`Int`类型。这有助于代码的一致性和可复用性。即使在32位的平台上,`Int`类型可以储存从`-2,147,483,648`到`2,147,483,647`的整数,大多数情况下这足够用了。 + +## 无符号整数 + +Swift也提供了无符号整数类型,`UInt`,它和当前平台的原生长度范围相同。 + +* 在32位的平台上,`UInt`和`UInt32`范围相同。 +* 在64位的平台上,`UInt`和`UInt64`范围相同。 + +> 注意 + +> 只在你需要特别指定无符号整数与当前平台原生长度范围相同时才使用`UInt`,否则,推荐使用`Int`,即使是你知道不会存储负值的时候。使用一致的`Int`整数类型有助于代码的复用,避免了不同数字类型之间的转换,并且和整数的类型推断结果一致,详见[类型安全和类型推断](link)。 + +# 浮点型数字 + +浮点型数字是指有小数部分的数字,比如`3.14159`,`0.1`和`-273.15`。 + +浮点类型比整数类型范围大很多,它可以储存比整数更大或者更小的数。Swift提供了两种有符号的浮点数类型: + +* `Double`代表64位浮点数。当你需要特别大和特别精确的值的时候使用`Double`类型。 +* `Float`代表32位浮点数。当不需要像64位那么精确的时候使用`Float`就够了。 + +> 注意 + +> `Double`有至少15位数字的精确度,而`Float`的精确度只有至少6位数字。根据业务需要的值的范围去选择合适的浮点类型。 + +# 类型安全和类型推断 + +Swift是一个类型安全的语言。一个类型安全的语言鼓励你声明代码中使用的值的类型。如果你期望这部分代码是`String`(字符串),你就不能错误的给它赋一个`Int`(整型)的值。 + +因为Swift是类型安全的,它会在代码编译时运行类型检查,并且将所有不匹配的类型标记为错误。这让你可以在开发过程中今早的发现和修复问题。 + +当你处理不同类型的值的时候,类型检查帮助你避免错误。然而,这并不意味着你需要给每一个声明的常量和变量指定特定的类型。如果你没有指定特定的类型,Swift会使用类型推断来推断出合适的类型。当编译代码时,类型推断特性使得编译器可以简单的通过检查你提供的值来自动推断出特定表达式的类型。 + +因为有了类型推断,Swift与类似C和Objective-C的语言相比需要更少的类型声明。常量和变量同样会有准确的类型,但是大部分关于指定类型的工作Swift已经帮你做好了。 + +当你声明一个有初始值的常量或变量时类型推断特别实用。通常是当你声明一个常量和变量的时候你就给它赋一个字面量(literal value)。(字面量是会直接出现在源码里的值,比如下面例子里的`42`和`3.14159`。) + +举个例子,如果你给一个新的常量赋了一个字面量`42`,但没有指定它的类型,Swift会推断你希望这个常量是一个`Int`(整数)类型,因为你用了一个像是整数的数字来初始化变量: + +``` +let meaningOfLife = 42 +// meaningOfLife被推断为Int(整数)类型 +``` + +类似的,如果你没有特别指定一个浮点型的字面量,Swift会推断你需要一个`Double`类型的浮点数: + +``` +let pi = 3.14159 +// pi被推断为Double类型 +``` + +在推断浮点型的数字时,Swift总是会选择`Double`类型而不是`Float`类型。 + +如果你将整数型和浮点型的字面量相加,这种情况下会推断为`Double`类型: + +``` +let anotherPi = 3 + 0.14159 +// anotherPi也被推断为Double类型 +``` + +字面量`3`本身并没有准确的类型,因此是通过相加的元素中有一个浮点型的字面量推断出合适的输出类型为`Double`。 + +# 数字字面量 + +整数字面量可以写成: + +* 十进制的数字,不需要任何前缀 +* 二进制的数字,前缀是`0b` +* 八进制的数字,前缀是`0o` +* 十六进制的数字,前缀是`0x` + +下面所有的整数字面量在十进制下的值都是`17`: + +``` +let decimalInteger = 17 +let binaryInteger = 0b10001 // 二进制声明中的17 +let octalInteger = 0o21 // 八进制声明中的17 +let hexadecimalInteger = 0x11 // 十六进制声明中的17 +``` + +浮点型的字面量可以是十进制的(没有前缀),或者十六进制(前缀是`0x`)。在小数点两边都必须有数字(或十六进制数字符号)。它们也可以有一个可选的指数,在十进制中用大写或小写的`e`来表示,在十六进制中用大写或小写的`p`来表示。 + +在十进制数字中的指数`exp`表示基数乘以10exp: + +* ```1.25e2``` 表示 1.25 × 102,或者`125.0`. +* ```1.25e-2``` 表示 1.25 × 10-2,或者`0.0125` + +在十六进制的数字中的指数`exp`表示基数乘以2exp + +* 0xFp2 表示 15 × 22,或者 60.0. +* 0xFp-2 表示 15 × 2-2,或者 3.75. + +以下这些浮点字面量在十进制下的值都是`12.1875`: + +``` +let decimalDouble = 12.1875 +let exponentDouble = 1.21875e1 +let hexadecimalDouble = 0xC.3p0 +``` + +数字字面量可以使用额外的格式让它们更易读。不论整数还是浮点数都可以添加额外的0,和下划线来增加可读性。这些格式都不会影响到这些字面量原本的值: + +``` +let paddedDouble = 000123.456 +let oneMillion = 1_000_000 +let justOverOneMillion = 1_000_000.000_000_1 +``` + +# 数字类型转换 + +在一般情况下对整数的常量或变量使用`Int`类型,即使是你知道不会存储负值的时候。总是使用默认的`Int`(整数)类型意味着你的代码中整数的常量和变量可以很好的协作,并且和整数字面量推断出的类型一致。 + +只在当前工作中有特殊需要的时候,才使用其他的整数类型,比如是从外部来源的需要指定精确度的数据,或者为了性能,内存使用量,或者其他必要的优化。在这些情况下使用指定精确度的数字类型可以帮助我们发现溢出值和暗示这个数据的特性。 + +## 整数类型转换 + +不同整数类型的常量或变量可以储存不同范围的数字。一个`Int8`的常量或变量可以储存`-128`到`127`之间的数字,然而一个`UInt8`型的常量或变量可以储存从`0`到`255`之间的数字。如果一个数字不符合常量或变量的储值范围,编译的时候会报错: + +``` +let cannotBeNegative: UInt8 = -1 +// UInt8不能储存负数,所以这里会报错 +let tooBig: Int8 = Int8.max + 1 +// Int8不能储存一个大于其最大值的数字 +// 所以这里同样会报错 +``` + +因为每一种数字类型可以储存不同范围的值,你必须根据具体的案例选择合适的数字类型转换。这种方式阻止了隐式的转换报错,并且让代码中的类型转换意图更明确。 + +要将一种特定类型转换成另一种,你需要用一个你想要的类型的新数字值来重新初始化。在下面的例子里,常量`twoThousand`是`UInt16`类型的,然而常量`one`是`UInt8`类型的。它们不能直接相加,因为它们类型不同。因此,这个例子调用了`UInt16(one)`来对常量`one`创建一个新的`UInt16`类型的初始值,然后用这个值来代替原来的值: + +``` +let twoThousand: UInt16 = 2_000 +let one: UInt8 = 1 +let twoThousandAndOne = twoThousand + UInt16(one) +``` + +因为相加的两个数现在都是`UInt16`类型了,这样相加是允许的。输出结果的常量`twoThousandAndOne`被推断为`UInt16`类型,因为它是两个`UInt16`类型的值的和。 + +`SomeType(ofInitialValue)`是Swift初始化并赋予一个初值的默认方法。在语言内部,`UInt16`类型有一个初始值设定项会接受一个`UInt8`的值,然后这个初始值设定项是用来从已知的`UInt8`中创建一个新的`UInt16`的值。你不能传递任意类型,只能传入`UInt16`提供初始化的类型。扩展已有的类型来提供更多的初始化选项,来接受新的类型(包括自定义类型),见[扩展](link)。 + +## 整数和浮点数转换 + +整数和浮点数之间的类型转换必须是显性的: + +``` +let three = 3 +let pointOneFourOneFiveNine = 0.14159 +let pi = Double(three) + pointOneFourOneFiveNine +// pi等于3.14159,它被推断为Double类型 +``` +在这里,常量`three`的值被用来创建一个新的`Double`型的值,所以相加的两个数都是同样的类型了,无须转换,这个加法是允许的。 + +相对的,由浮点数到整数的转换也是可行的,也就是说一个整数类型可以被初始化为一个`Double`或者`Float`型的值: + +``` +let integerPi = Int(pi) +// integerPi等于3,它被推断为整数(Int)类型 +``` + +当浮点型的值初始化为一个新的整数型的值时,它的数值总是被截断的。这意味着`4.75`会成为`4`,而`-3.9`会成为`-3`。 + +> 注意 + +> 数字的常量和变量相加的规则和数字字面量的规则是不一样的。字面量`3`可以和字面量`0.14159`直接相加,因为数字字面量本身并没有一个显式的类型声明。他们的类型是在编译器运行时被推断的。 + +# 类型别名 + +类型别名给已经存在的类型定义另一个名字。你用关键字`typealias`来定义类型别名。 + +当你想用一个更合适的名字来命名一个已有类型的时候,类型别名是很有用的。比如当你处理需要指定精确度的外部来源数据时: + +``` +typealias AudioSample = UInt16 +``` + +一旦你定义了类型别名,你就可以在任何你可以使用原始名字的地方使用这个别名: + +``` +var maxAmplitudeFound = AudioSample.min +// maxAmplitudeFound的值是0 +``` + +在这个例子中,`AudioSample`被定义为`UInt16`的一个别名。因为这是一个别名,调用`AudioSample.min`实际上是调用`UInt16.min`给变量`maxAmplitudeFound`赋了初始值`0`。 + +# 布尔值 + +Swift有一个基本的布尔值类型,叫做`Bool`。布尔值可以用作逻辑判断,因为它们只有“真”和“假”两个值。Swift提供两种布尔值常量,`true`和`false`: + +``` +let orangesAreOrange = true +let turnipsAreDelicious = false +``` + +因为`orangesAreOrange`和`turnipsAreDelicious`有一个布尔的字面量作为初始值,它们的类型被推断为`Bool`。如果你在创建常量或变量的时候就把它们的值设为`true`或者`false`,你不需要声明它们的类型是`Bool`。当你赋予常量或变量一个类型已知的初值时,类型推断让Swift的代码更加准确和可读。 + +布尔值在条件声明(比如:`if`语句)中特别有用: + +``` +if turnipsAreDelicious { + println("Mmm, tasty turnips!") +} else { + println("Eww, turnips are horrible.") +} +// 输出 "Eww, turnips are horrible." +``` + +关于条件声明(比如`if`语句)的更多细节会在[Control Flow](link)这一章节中详细讲解。 + +Swift的类型安全会阻止一个非布尔值替换`Bool`值。下面的例子会在编译时报错: + +``` +let i = 1 +if i { + // 这个例子不会顺利编译,会报错 +} +``` + +然而,下面这个例子是有效的: + +``` +let i = 1 +if i == 1 { + // 这个例子会成功编译 +} +``` + +`i == 1`的比较结果是一个`Bool`类型的值,因此第二个例子通过了类型检查。类似`i == 1`的比较我们会在[Basic Operators](link)进行深入讨论。 + +和其他Swift的类型安全的例子一样,这种检查避免了意外的错误并且保证了特定的某块代码的意图始终是明确的。 + +# 元组 + +元组将多个数值组合成一个单独的复合值。一个元组中的值可以是任何类型,不必是同一种类型。 + +在这个例子里, `(404, "Not Found")` 是一个用来形容HTTP状态码的元组。HTTP状态码是当你请求一个网页的时候网页服务器的返回值。当你请求的网页不存在的时候会返回`404 Not Found`的状态码。 + +``` +let http404Error = (404, "Not Found") +// http404Error的类型是(Int, String),值等于(404, "Not Found") +``` + +元组`(404, "Not Found")`将一个整数值`Int`和一个字符串`String`组合起来,给HTTP状态码赋予两个独立的值:一个机器语言的数字和一句人类语言的描述。它可以描述为”一个类型为 `(Int, String)`的元组“。 + +你可以创建一个含任意类型组合的元组,它们可以包含任意多的类型。谁也不能阻止你创建一个`(Int, Int, Int)`类型或者`(String, Bool)`类型,又或者任意你想要的类型组合的元组。 + +你可以把一个元组的内容分解成独立的常量或变量,然后你就可以像平常一样引用它们: + +``` +let (statusCode, statusMessage) = http404Error +println("状态码是 \(statusCode)") +// 输出”状态码是404“ +println("状态信息是 \(statusMessage)") +// prints "状态信息是 Not Found" +``` +当你在分解元组时,如果只需要元祖的一部分值,可以使用下划线(`_`)来忽略元组的一部分值: + +``` +let (justTheStatusCode, _) = http404Error +println("The status code is \(justTheStatusCode)") +// prints "The status code is 404" +``` + +同样的,使用从`0`开始的序列可以引用元组中的一个单独元素的值。 + +``` +println("The status code is \(http404Error.0)") +// prints "The status code is 404" +println("The status message is \(http404Error.1)") +// prints "The status message is Not Found" +``` + +定义元组的时候,你可以给元素单独命名: + +``` +let http200Status = (statusCode: 200, description: "OK") +``` + +如果你给元组中的元素命名了,你可以用这个名字来引用它们的值: + +``` +println("The status code is \(http200Status.statusCode)") +// prints "The status code is 200" +println("The status message is \(http200Status.description)") +// prints "The status message is OK" +``` + +作为函数的返回值元组是很有用的。一个试图获取网页的函数可能返回一个(Int, String)类型的元组来描述这个页面获取成功或失败的状态。通过返回一个含有两个不同类型值的元组,这个函数的结果相比只能返回一个单独类型值的函数提供了更多有用的信息。详见[Functions with Multiple Return Values]()。 + +> 注意 + +> 当你处理一些相关数据的临时组合,元组是很有用的。它们不适合用于一些复杂的数据类型的创建。如果你的数据结构超过了一个临时的范畴,用class或者structrue会比用元组tuple更合适。更多信息,参见[Classes and Structures](link)。 + +# 可选类型 + +当值可能缺失的情况下,可以用可选类型(Options)。可选类型表示: + +- 那里有一个值,它等于x + +或者 + +- 值不存在 + +> 注意 + +> 可选类型在C或者Objective-C中都不存在。在Objective-C中最接近的是返回一个对象的方法也可以返回一个`nil`值的能力,`nil`的意思是“有效对象的缺失”。然而,它只对对象有效,对`structs`,基本的C类型、或者枚举类型都是无效的。对这些类型,Objective-C通常会返回一个特殊值(比如`NSNotFound`)来表示值缺失。这样的方式假定我们调用方法时它必须知道有一个特殊值,并且要记得去检查它。Siwft的可选类型让你可以给任意类型的值声明这个值缺失,不需要任何其它的特殊常量。 + +下面是一个例子。Swift的字符串类型有一个`toInt`的方法,它会尝试将一个字符串类型转换成一个整数类型。然而,不是每一个字符串都可以转换成整数。字符串`"123"`可以转换为数字量`123`,但是字符串`"Hello, world"`并没有一个明显的数字量可以转换。 + +下面这个例子用`toInt`方法尝试转换字符串到整数: + +``` +let possibleNumber = "123" +let convertedNumber = possibleNumber.toInt() +// convertedNumber被推断为"Int?"类型, 或 "可选整数" +``` + +因为`toInt`方法可能失败,它返回的是可选类型的整数,而不是一个整数。一个可选的整数表示为`Int?`,而不是`Int`。最后的问号表示这个值是可选的,意味着它可能包含整数类型的值,或者没有值。(它不能包括其它任何值,比如布尔值或者字符串。它要么是一个整数,要么就没有值。) + +## If语句和强制解析 + +你可以使用`if`语句来判断一个可选类型是否有值。如果有值,它就等于`true`,如果没有值,就等于`false`。 + +一旦你确定可选类型是有值的,你可以在可选类型的名字后面加一个感叹号`!`来调用它的值。这个感叹号的意思是:”我知道这个可选类型的值存在值,请使用这个值。“这叫做可选值的强制解析: + +``` +if convertedNumber { + println("\(possibleNumber) has an integer value of \(convertedNumber!)") +} else { + println("\(possibleNumber) could not be converted to an integer") +} +// prints "123 has an integer value of 123" +``` + +更多`if`语句的信息,见[Control Flow](link)。 + +> 注意 + +> 如果你使用`!`来调用一个不存在的可选值,会报运行时错误。在使用`!`强制解析可选值之前请确认这个可选值包含一个非`nil`的值。 + +## 可选值绑定 + +你可以使用可选值绑定(optional binding)来找出一个可选类型是否包含值,如果包含,则可以把这个值作为一个临时常量或变量来使用。可选值绑定可以和`if`或`while`语句一起来检查可选值里面的值,并且解析到一个常量或变量中,所有这些都在一步操作中完成。更多关于`if`和`while`语句的信息,见[Control Flow](link)。 + +像这样给`if`语句写可选值绑定: + +``` +if let constantName = someOptional { + statements +} +``` + +你可以用可选值绑定来代替强制解析来重写之前那个`possibleNumber`的例子: + +``` +if let actualNumber = possibleNumber.toInt() { + println("\(possibleNumber) has an integer value of \(actualNumber)") +} else { + println("\(possibleNumber) could not be converted to an integer") +} +// prints "123 has an integer value of 123" +``` + +这段代码可以解读为: + +”如果可选整数值通过`possibleNumber.toInt`返回一个值,设定一个叫`actualNumber`的新常量来储存这个可选整数中的值。“ + +如果这个转换是成功的,那么常量`actualNumber`在`if`语句的第一个分支就可用了。它已经用可选量中的值初始化了,所以现在不需要用`!`后缀来调用这个值。在这个例子中,`actualNumber`只是简单的用于输出转换结果。 + +无论是常量还是变量你都可以使用可选值绑定。如果你希望对`if`语句第一个分支中`actualNumber`的值进行操作,你可以用`if var actualNumber`来声明,然后可选量中的值就会成为一个可用的变量而不是常量。 + +## nil + +通过给可选变量赋予特殊值`nil`来表示没有值的状态: + +``` +var serverResponseCode: Int? = 404 +// serverResponseCode contains an actual Int value of 404 +serverResponseCode = nil +// serverResponseCode now contains no value +``` + +> 注意 + +> `nil`不能被用于可选类型之外的常量和变量。如果你的代码中的常量或变量需要处理值缺失的情况,总是用一个可选的合适类型来声明它。 + +如果你定义一个可选的常量或变量,却没有赋初值,这个常量或变量会自动设置为`nil`: + +``` +var surveyAnswer: String? +// surveyAnswer is automatically set to nil +``` + +> 注意 + +> Swift的`nil`和Objective-C中的不完全一样。Objective-C中,`nil`是指向不存在对象的指针。而在Swift中,`nil`不止是一个指针——它是一个特定类型的值缺失。可选的任何类型都可以设为`nil`,不只是对象类型。 + +## 隐式解析可选值 + +像之前说的,可选值意味着一个常量或变量允许“没有值”。可以通过`if`语句来判断可选值中是否有值存在,如果值存在则用可选值绑定来调用可选量的值。 + +有时候从程序的结构可以清楚的知道一个可选值在第一次值设定之后,它总是有值的。在这些情况下,把每次调用都检查并解析可选量的值去掉是很有用的,因为它们可以安全的被推断出它们总是有值的。 + +这些类型的可选值被定义为隐式解析可选值。当你创建可选值的时候,你可以通过写感叹号(String!)而不是问号(String?)的形式来表示一个隐式解析的可选值。 + +如果可选值在第一次定义之后就肯定存在值并且之后每一刻都肯定存在,这种情况下隐式解析可选值是很有用的。Swift中的隐式解析可选值的主要用法是在类的初始化过程中,参见[Unowned References and Implicitly Unwrapped Optional Properties](link)。 + +一个隐式解析可选值其实是一个普通的可选值,但它也可以像非可选的值那样使用,无须每次调用都解析可选量的值。下面的例子显示了一个普通的可选的字符串和一个隐式解析的可选字符串的区别: + +``` +let possibleString: String? = "An optional string." +println(possibleString!) // requires an exclamation mark to access its value +// prints "An optional string." + +let assumedString: String! = "An implicitly unwrapped optional string." +println(assumedString) // no exclamation mark is needed to access its value +// prints "An implicitly unwrapped optional string." +``` + +你可以把隐式解析可选值当作一个允许可选值每次都能自动解析的授权。你只需要当你声明的时候在可选量的类型后面放一个感叹号,而不用每次使用的时候都在可选值后面放感叹号。 + +> 注意 + +> 如果你试图调用一个没有值的隐式解析可选值,你会得到一个运行时错误。这和你在一个普通的不含值的可选值后面放感叹号的结果是完全一样的。 + +你仍然可以把隐式解析可选值当作一个普通的可选值,来检查它是否含有值: + +``` +if assumedString { + println(assumedString) +} +// prints "An implicitly unwrapped optional string." +``` + +你同样可以对隐式解析可选值使用可选值绑定,在一行声明中来检查和解析它的值: + +``` +if let definiteString = assumedString { + println(definiteString) +} +// prints "An implicitly unwrapped optional string." +``` + +> 注意 + +> 当一个变量有可能是`nil`的情况下,不应当使用隐式解析可选值。如果在整个过程中检查一个变量是否有`nil`值,你应该用一个普通的可选值。 + +# 断言 + +可选值让你可以检查一个值是否存在,并且优雅的处理值缺失的情况。然而,在一些情况下,如果值不存在或者值不满足指定的条件,你的程序就无法继续执行。在这些情况下,你可以在代码中触发一个断言,来中断代码的执行来调试为什么值缺失或无效。 + +## 用断言来调试 + +断言是判断一个逻辑条件是否为真的实时检查。基本上,一个断言就是“断定”(asserts)一个条件为真。你可以使用断言来确保在运行后续代码之前一个必要的条件是已经满足了的。如果这个条件为真,代码会正常执行,如果条件为否,代码会停止执行,并且你的应用会终止。 + +如果你的代码在调试环境下触发了一个断言(比如当你在Xcode中创建并运行一个app的时候),你可以准确的看到非法语句出现的位置,也可以查询到当断言触发时app的状态。断言还允许你可以提供一个相应的调试信息。 + +通过调用全局的assert函数来写一个断言。你给断言函数传入一个会被解析为`true`或者`false`的表达式,以及在解析为`false`时的提示信息: + +``` +let age = -3 +assert(age >= 0, "A person's age cannot be less than zero") +// this causes the assertion to trigger, because age is not >= 0 +``` + +在这个例子里,只有`age >= 0`为真的时候代码才会继续执行,也就是,如果`age`的值是非负数。在上面的代码中,如果`age`的值是负数,`age >= 0`这个表达式解析为否,断言被触发,终止了这个程序。 + +断言信息不能使用字符串插值。断言信息可以省略,像下面这个例子: + +``` +assert(age >= 0) +``` + +## 何时使用断言 + +在一个条件有可能为否,但只有它为真的时候代码才能继续执行时,应该使用断言。使用断言检查的合适场景包括: + +- 一个整数类型的下标索引被传入一个自定义的下标实现,但是这个下标索引值太小或太大。 +- 一个值传入一个函数,但是如果传入不合法的值,这个函数就无法满足需求。 +- 一个可选值当前的值是`nil`,但是后续代码成功执行需要一个非`nil`值。 + +参见[Subscripts and Functions](link)。 + +> 注意 + +> 断言会造成你的app终止,它并不能代替你设计代码来保证不合法情况不出现。尽管如此,当不合法的情况可能出现时,断言可以有效的保证在你的app在正式发布之前的开发过程中这种不合法的情况就会被高亮并被注意到。 \ No newline at end of file diff --git a/src/chapter2/02_Basic_Operators.md b/src/chapter2/02_Basic_Operators.md index e69de29..8d230c2 100644 --- a/src/chapter2/02_Basic_Operators.md +++ b/src/chapter2/02_Basic_Operators.md @@ -0,0 +1,547 @@ +###基本运算符 + +An operator is a special symbol or phrase that you use to check, change, or combine values. For example, the addition operator (+) adds two numbers together (as in let i = 1 + 2). More complex examples include the logical AND operator && (as in if enteredDoorCode && passedRetinaScan) and the increment operator ++i, which is a shortcut to increase the value of i by 1. + +运算符(operator)是用来检查,改变或者合并值的一种特殊的符号或者短语。举个例子,加操作符(+)将2个数字相加(i=1+2).更复杂的如逻辑与运算符`&&`,以及缩写形式的,表达i的值加1的自增运算符`i++`。 + +Swift supports most standard C operators and improves several capabilities to eliminate common coding errors. The assignment operator (=) does not return a value, to prevent it from being mistakenly used when the equal to operator (==) is intended. Arithmetic operators (+, -, *, /, % and so forth) detect and disallow value overflow, to avoid unexpected results when working with numbers that become larger or smaller than the allowed value range of the type that stores them. You can opt in to value overflow behavior by using Swift’s overflow operators, as described in Overflow Operators. + + +Swift 支持大部分标准 C 语言运算符,且改进了许多特性来避免常规编码错误。赋值操作符(=)不再返回一个值,从而避免当应该使用相等运算符(==)的地方,错写成赋值符而导致的错误。算术运算符(+,-,*,/,%等)会检测值溢出并且不允许溢出,以此来避免当保存变量时,变量大于或小于其类型所能承载的范围时所导致的异常结果。当然你可以使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_37)。 + +Unlike C, Swift lets you perform remainder (%) calculations on floating-point numbers. Swift also provides two range operators (a..b and a...b) not found in C, as a shortcut for expressing a range of values. + +与C语言不同,Swift允许你对浮点数进行求余(%)运算,同时还提供C语言没有的两种区间运算符(a..b 和a...b),以方便我们表达一定区间内的数值。 + +This chapter describes the common operators in Swift. Advanced Operators covers Swift’s advanced operators, and describes how to define your own custom operators and implement the standard operators for your own custom types. + + +本章节只描述了 Swift 中的基本运算符,[高级运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_28)包含了高级运算符相关内容,以及如何自定义运算符与通过扩充标准运算符来生成自定义运算符。 + +###术语 + +Operators are unary, binary, or ternary: + +Unary operators operate on a single target (such as -a). Unary prefix operators appear immediately before their target (such as !b), and unary postfix operators appear immediately after their target (such as i++). +Binary operators operate on two targets (such as 2 + 3) and are infix because they appear in between their two targets. +Ternary operators operate on three targets. Like C, Swift has only one ternary operator, the ternary conditional operator (a ? b : c). +The values that operators affect are operands. In the expression 1 + 2, the + symbol is a binary operator and its two operands are the values 1 and 2. + + + +运算符包括一元运算符,二元运算符,三元运算符。 + +* 一元运算符是对单一操作对象的操作(如-a).一元运算符中,前置运算符出现在操作对象的前面(如!b),后置运算符则出现在操作对象的后面(如i++). +* 二元运算符是对二个操作对象的操作,位于二个操作对象之间(如2+3) +* 三元运算符就是对三个对象的操作,和C语言一样,Swift 只有一个三元运算符,即三元条件运算符(a ? b : c)。 + +受运算符影响的值叫操作数,在表达式1 + 2中,加符号`+`是二元运算符,它的两个操作数是两个值1和2。 + +###赋值运算符 + +The assignment operator (a = b) initializes or updates the value of a with the value of b: + +赋值运算符(a = b),表示通过b来初始化或更新a的值; + + let b = 10 + var a = 5 + a = b + // a 现在等于 10 + +If the right side of the assignment is a tuple with multiple values, its elements can be decomposed into multiple constants or variables at once: + +如果赋值运算符右边是一个多元组,它的元素可以马上被拆分为多个常量或值: + + let (x, y) = (1, 2) + // 此时 x 等于 1, y 等于 2 + +Unlike the assignment operator in C and Objective-C, the assignment operator in Swift does not itself return a value. The following statement is not valid: + +与 C 语言和 Objective-C 不同,Swift 的赋值操作符并不返回任何值。所以以下代码是错误的: + + if x = y { + // 这里是错误的, 因为 x = y 并不返回任何值 + } + +This feature prevents the assignment operator (=) from being used by accident when the equal to operator (==) is actually intended. By making if x = y invalid, Swift helps you to avoid these kinds of errors in your code. + + +这个特性使你无法把(==)错写成(=),由于if x = y是错误代码,Swift 从代码层面帮你避免了这些错误的产生。 + +Arithmetic Operators + +Swift supports the four standard arithmetic operators for all number types: + +Addition (+) +Subtraction (-) +Multiplication (*) +Division (/) + +###算术运算符 + +Swift 支持所有number类型间的标准四则运算: +* 加法(+) +* 减法(-) +* 乘法(*) +* 除法(/) + + 1 + 2 // 等于 3 + 5 - 3 // 等于 2 + 2 * 3 // 等于 6 + 10.0 / 2.5 // 等于 4.0 + +Unlike the arithmetic operators in C and Objective-C, the Swift arithmetic operators do not allow values to overflow by default. You can opt in to value overflow behavior by using Swift’s overflow operators (such as a &+ b). See Overflow Operators. + +The addition operator is also supported for String concatenation: + +和C、Object-C不同的是,Swift 默认不允许在数值运算中出现溢出情况。但你可以使用 Swift 的溢出运算符来达到有目的的溢出(如a &+ b)。详情参见溢出运算符。 + +加运算符也可用于String的拼接: + + "hello, " + "world" // 等于 "hello, world" + +两个`Character`类型的值或一个`String`类型和一个`Character`类型的值,相加会生成一个新的`String`类型的值: + + let dog: Character = "d" + let cow: Character = "c" + let dogCow = dog + cow + + // dogCow 现在是 "dc" + +详情参见字符,字符串的拼接。 + + +###求余运算符 + +The remainder operator (a % b) works out how many multiples of b will fit inside a and returns the value that is left over (known as the remainder). + +求余运算(a % b)来计算b的多少倍刚刚好可以容入a,之后返回多出来的那部分(余数)。 + +>NOTE + +>The remainder operator (%) is also known as a modulo operator in other languages. However, its behavior in +>Swift for negative numbers means that it is, strictly speaking, a remainder rather than a modulo operation. + + +>注意: +求余运算(%)在其他语言也叫取模运算。然而严格说来,Swift对于该运算符对负数的操作结果,"求余"比"取模"更合适些。 + +Here’s how the remainder operator works. To calculate 9 % 4, you first work out how many 4s will fit inside 9: + +这里展示了求余运算符是怎样计算的。若计算9 % 4,你首先要计算出4的多少倍刚好可以融入9中: + +![求余](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderInteger_2x.png) + +You can fit two 4s inside 9, and the remainder is 1 (shown in orange). + +两个4正好装到9里,余数是1(用橙色标出) + +在 Swift 中这么来表达: + + 9 % 4 // 等于 1 + +To determine the answer for a % b, the % operator calculates the following equation and returns remainder as its output: + +为了得到a % b的结果,%计算了以下等式,并输出余数作为结果: + +a = (b × 倍数) + 余数 + +where some multiplier is the largest number of multiples of b that will fit inside a. + +Inserting 9 and 4 into this equation yields: + +当倍数取最大值的时候,就会刚好可以容入a中。 + +The same method is applied when calculating the remainder for a negative value of a: + +把9和4代入等式中,我们得1: + + 9 = (4 × 2) + 1 + +Inserting -9 and 4 into the equation yields: + +同样的方法,我来们计算 -9 % 4: + + -9 % 4 // 等于 -1 +把-9和4代入等式,-2是取到的最大整数: + + -9 = (4 × -2) + -1 + +余数是-1。 + +The sign of b is ignored for negative values of b. This means that a % b and a % -b always give the same answer. + + +在对负数b求余时,b的符号会被忽略。这意味着 a % b 和 a % -b的结果是相同的。 + + +###浮点数求余 + +Unlike the remainder operator in C and Objective-C, Swift’s remainder operator can also operate on floating-point numbers: + +与 C 语言和 Objective-C不同,Swift中可以对浮点数进行求余运算。 + + 8 % 2.5 // 等于 0.5 + +In this example, 8 divided by 2.5 equals 3, with a remainder of 0.5, so the remainder operator returns a Double value of 0.5. + +这个例子中,8除于2.5等于3余0.5,所以结果是一个Double类型的值0.5。 + +![浮点数求余](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderFloat_2x.png) + +Increment and Decrement Operators + +Like C, Swift provides an increment operator (++) and a decrement operator (--) as a shortcut to increase or decrease the value of a numeric variable by 1. You can use these operators with variables of any integer or floating-point type. + +###自增和自减运算符 +和 C 语言一样,Swift 也提供了对变量本身加1或减1这类运算的缩写:自增(++)和自减(--)的运算符。其操作对象可以是整形和浮点类型的变量。 + + var i = 0 + ++i // 现在 i = 1 + +Each time you call ++i, the value of i is increased by 1. Essentially, ++i is shorthand for saying i = i + 1. Likewise, --i can be used as shorthand for i = i - 1. + +每调用一次++i,i的值就会加1。实际上,++i是i = i + 1的简写,而--i是i = i - 1的简写。 + +The ++ and -- symbols can be used as prefix operators or as postfix operators. ++i and i++ are both valid ways to increase the value of i by 1. Similarly, --i and i-- are both valid ways to decrease the value of i by 1. + +++和--既是前置又是后置运算。++i,i++,--i和i--都是有效的写法。 + +Note that these operators modify i and also return a value. If you only want to increment or decrement the value stored in i, you can ignore the returned value. However, if you do use the returned value, it will be different based on whether you used the prefix or postfix version of the operator, according to the following rules: + +需要注意的是,这些运算符修改了i后有一个返回值。如果你只想修改i的值,那你就可以忽略这个返回值。但如果你想使用返回值,你需要留意前置和后置操作符的返回值是不同的。 + +If the operator is written before the variable, it increments the variable before returning its value. +If the operator is written after the variable, it increments the variable after returning its value. + +当++前置的时候,先自増再返回。 +当++后置的时候,先返回再自增。 + + var a = 0 + let b = ++a // a 和 b 现在都是 1 + let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1 + +In the example above, let b = ++a increments a before returning its value. This is why both a and b are equal to to the new value of 1. + +However, let c = a++ increments a after returning its value. This means that c gets the old value of 1, and a is then updated to equal 2. + +Unless you need the specific behavior of i++, it is recommended that you use ++i and --i in all cases, because they have the typical expected behavior of modifying i and returning the result. + + + +上述例子中,`let b = ++a`首先使a自增1,再返回a的值。所以a和b都是新值1。 + +而let c = a++,先返回了a的值,然后a才自增1。所以c得到了a的旧值1,而a加1后变成2。 + +除非你需要使用i++的特性,不然推荐你使用++i和--i,因为先修改后返回这样的行为更符合我们的逻辑。 + +The sign of a numeric value can be toggled using a prefixed -, known as the unary minus operator: + +###一元减号运算符 + +数值的正负号可以使用前缀-(即一元减号运算符)来切换: + + let three = 3 + let minusThree = -three // minusThree 等于 -3 + let plusThree = -minusThree // plusThree 等于 3, 或 "负负3" + +The unary minus operator (-) is prepended directly before the value it operates on, without any white space. + + +一元减号运算符(-)写在操作数之前,中间没有空格。 + +Unary Plus Operator + +The unary plus operator (+) simply returns the value it operates on, without any change: + +###一元加号操作符 + +一元加号操作符(+)不做任何改变地返回操作数的值。 + + let minusSix = -6 + let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6 +虽然一元+做无用功,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。 + +##复合赋值 +和C 语言一样,Swift 也提供把其他运算符和赋值运算(=)组合的复合赋值运算符,加赋运算(+=)是其中一个例子: + + var a = 1 + a += 2 // a 现在是 3 + +表达式a += 2是a = a + 2的简写,一个加赋运算就把加法和赋值两件事完成了。 +>注意: +复合赋值运算没有返回值,let b = a += 2这类代码是错误。这不同于上面提到的自增和自减运算符。 + +在表达式章节里有复合运算符的完整列表。 ‌ + +##比较运算符 +swift支持所有c语言中的比较运算 + +* 等于(a == b) +* 不等于(a != b) +* 大于(a > b) +* 小于(a < b) +* 大于等于(a >= b) +* 小于等于(a <= b) + + +NOTE + +Swift also provides two identity operators (=== and !==), which you use to test whether two object references both refer to the same object instance. For more information, see Classes and Structures. + + +>注意: +Swift 也提供身份操作符(`===`和`!==`)来判断两个对象是否是同一个对象实例的引用。更多细节可以参考类与结构体。 + +Each of the comparison operators returns a Bool value to indicate whether or not the statement is true: + +每个比较运算符都返回一个布尔值,用来表示一个表达式是否成立: + + 1 == 1 // true, 因为 1 等于 1 + 2 != 1 // true, 因为 2 不等于 1 + 2 > 1 // true, 因为 2 大于 1 + 1 < 2 // true, 因为 1 小于2 + 1 >= 1 // true, 因为 1 大于等于 1 + 2 <= 1 // false, 因为 2 并不小于等于 1 + +Comparison operators are often used in conditional statements, such as the if statement: + +比较运算多用于条件语句,如if条件: + + let name = "world" + if name == "world" { + println("hello, world") + } else { + println("I'm sorry \(name), but I don't recognize you") + } + // 输出 "hello, world", 因为 `name` 就是等于 "world" + +For more on the if statement, see Control Flow. + + +关于if语句,请参考[控制流](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-XID_153)。 + +Ternary Conditional Operator + +The ternary conditional operator is a special operator with three parts, which takes the form question ? answer1 : answer2. It is a shortcut for evaluating one of two expressions based on whether question is true or false. If question is true, it evaluates answer1 and returns its value; otherwise, it evaluates answer2 and returns its value. + +The ternary conditional operator is shorthand for the code below: + +##三元条件运算符 +三元条件运算符的特殊之处,在于它是一个包含三个部分的运算符,它的形式是 问题 ? 答案1 :答案2。它简洁地表达了两个表达式:如果问题成立,返回答案1的结果; 如果不成立,返回答案2的结果。 + + +使用三元条件运算简化了以下代码 + + if question: { + answer1 + } else { + answer2 + } + +Here’s an example, which calculates the pixel height for a table row. The row height should be 50 pixels taller than the content height if the row has a header, and 20 pixels taller if the row doesn’t have a header: + + +这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。 + + let contentHeight = 40 + let hasHeader = true + let rowHeight = contentHeight + (hasHeader ? 50 : 20) + // rowHeight 现在是 90 + + let contentHeight = 40 + let hasHeader = true + var rowHeight = contentHeight + +The preceding example is shorthand for the code below: + +这样写会比下面的代码简洁: + + if hasHeader { + rowHeight = rowHeight + 50 + } else { + rowHeight = rowHeight + 20 + } + // rowHeight 现在是 90 + +The first example’s use of the ternary conditional operator means that rowHeight can be set to the correct value on a single line of code. This is more concise than the second example, and removes the need for rowHeight to be a variable, because its value does not need to be modified within an if statement. + +The ternary conditional operator provides an efficient shorthand for deciding which of two expressions to consider. Use the ternary conditional operator with care, however. Its conciseness can lead to hard-to-read code if overused. Avoid combining multiple instances of the ternary conditional operator into one compound statement. + + +第一段代码中使用了三元条件运算符,所以一行代码就能让我们得到正确的值。这比第二段代码简洁得多,无需将rowHeight定义成变量,因为它的值无需在if语句中改变。 + +三元条件运算符提供高效且便捷的方式来表达二选一的选择。需要注意的是,过度使用三元条件运算符,就会使代码变的难以阅读。我们应避免在一个组合语句中使用多个三元条件运算符。 + +##区间运算符 +Swift 提供了两个方便表达一定区间的值的运算符。 + +Closed Range Operator + +The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. + +The closed range operator is useful when iterating over a range in which you want all of the values to be used, such as with a for-in loop: + +###闭区间运算符 +闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间。 ‌ 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在for-in循环中: + + for index in 1...5 { + println("\(index) * 5 = \(index * 5)") + } + // 1 * 5 = 5 + // 2 * 5 = 10 + // 3 * 5 = 15 + // 4 * 5 = 20 + // 5 * 5 = 25 + +关于for-in,请参考[控制流](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-XID_153)。 + +Half-Closed Range Operator + +The half-closed range operator (a..b) defines a range that runs from a to b, but does not include b. It is said to be half-closed because it contains its first value, but not its final value. + +Half-closed ranges are particularly useful when you work with zero-based lists such as arrays, where it is useful to count up to (but not including) the length of the list: + +###半闭区间运算符 +半闭区间(a..b)定义了一个从a到b但不包括b的区间。 之所以称为半闭区间,是因为该区间包含第一个值而不包含最后的值。 + +半闭区间的实用性在于,当你使用一个初始元素序号为0的列表(如数组)时,可以非常方便地从0枚举到列表的长度。 + + let names = ["Anna", "Alex", "Brian", "Jack"] + let count = names.count + for i in 0..count { + println("第 \(i + 1) 个人叫 \(names[i])") + } + // 第 1 个人叫 Anna + // 第 2 个人叫 Alex + // 第 3 个人叫 Brian + // 第 4 个人叫 Jack + +Note that the array contains four items, but 0..count only counts as far as 3 (the index of the last item in the array), because it is a half-closed range. For more on arrays, see Arrays. + +Logical Operators + +Logical operators modify or combine the Boolean logic values true and false. Swift supports the three standard logical operators found in C-based languages: + +数组有4个元素,但0..count只数到3(最后一个元素的序号),因为它是半闭区间。关于数组,请查阅[数组章节]()。 + +##逻辑运算符 +逻辑运算符的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。 + +* 逻辑非(!a) +* 逻辑与(a && b) +* 逻辑或(a || b) + +Logical NOT Operator + +The logical NOT operator (!a) inverts a Boolean value so that true becomes false, and false becomes true. + +The logical NOT operator is a prefix operator, and appears immediately before the value it operates on, without any white space. It can be read as “not a”, as seen in the following example: + + +###逻辑非运算符 +逻辑非运算符(!a)对一个布尔值取反,使得true变false,false变true。 + +它是一个前置运算符,需要出现在操作数之前,之间不需要加空格。可以读作非 a。我们看以下的例子: + + let allowedEntry = false + if !allowedEntry { + println("ACCESS DENIED") + } + // 输出 "ACCESS DENIED" + +The phrase if !allowedEntry can be read as “if not allowed entry.” The subsequent line is only executed if “not allowed entry” is true; that is, if allowedEntry is false. + +As in this example, careful choice of Boolean constant and variable names can help to keep code readable and concise, while avoiding double negatives or confusing logic statements. + + + +`if !allowedEntry`语句可以读作 "如果 非 alowed entry。",下一行代码只有在如果 "非 allow entry" 为`true`,即`allowEntry`为`false`时被执行。 +在示例代码中,谨慎地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或使用混乱的逻辑语句。 + +Logical AND Operator + +The logical AND operator (a && b) creates logical expressions where both values must be true for the overall expression to also be true. + +If either value is false, the overall expression will also be false. In fact, if the first value is false, the second value won’t even be evaluated, because it can’t possibly make the overall expression equate to true. This is known as short-circuit evaluation. + +This example considers two Bool values and only allows access if both values are true: + +###逻辑与运算符 +逻辑与运算符`(a && b)`表达了只有`a`和`b`的值都为`true`时,整个表达式的值才会是`true`。 +只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不会去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。 +以下例子,只有两个`Bool`值都为`true`值的时候才允许进入: + + let enteredDoorCode = true + let passedRetinaScan = false + if enteredDoorCode && passedRetinaScan { + println("Welcome!") + } else { + println("ACCESS DENIED") + } + // 输出 "ACCESS DENIED" + +Logical OR Operator + +The logical OR operator (a || b) is an infix operator made from two adjacent pipe characters. You use it to create logical expressions in which only one of the two values has to be true for the overall expression to be true. + +Like the Logical AND operator above, the Logical OR operator uses short-circuit evaluation to consider its expressions. If the left side of a Logical OR expression is true, the right side is not evaluated, because it cannot change the outcome of the overall expression. + +In the example below, the first Bool value (hasDoorKey) is false, but the second value (knowsOverridePassword) is true. Because one value is true, the overall expression also evaluates to true, and access is allowed: + + + +###逻辑或运算符 +逻辑或运算符(a || b)是一个由两个连续的`|`组成的中置运算符。它表示两个逻辑表达式中,其中一个为`true`,整个表达式就为`true`。 + +同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。 +以下示例代码中,第一个布尔值(hasDoorKey)为`false`,但第二个值(knowsOverridePassword)为`true`,所以整个表达是`true`,于是允许进入: + + let hasDoorKey = false + let knowsOverridePassword = true + if hasDoorKey || knowsOverridePassword { + println("Welcome!") + } else { + println("ACCESS DENIED") + } + // 输出 "Welcome!" + +###组合逻辑 +我们可以组合多个逻辑运算来表达一个复合逻辑: + + if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { + println("Welcome!") + } else { + println("ACCESS DENIED") + } + // 输出 "Welcome!" + +This example uses multiple && and || operators to create a longer compound expression. However, the && and || operators still operate on only two values, so this is actually three smaller expressions chained together. It can be read as: + +If we’ve entered the correct door code and passed the retina scan; or if we have a valid door key; or if we know the emergency override password, then allow access. + +Based on the values of enteredDoorCode, passedRetinaScan, and hasDoorKey, the first two mini-expressions are false. However, the emergency override password is known, so the overall compound expression still evaluates to true. + +Explicit Parentheses + +It is sometimes useful to include parentheses when they are not strictly needed, to make the intention of a complex expression easier to read. In the door access example above, it is useful to add parentheses around the first part of the compound expression to make its intent explicit: + +这个例子使用了含多个&&和||运算符的复合逻辑。但无论怎样,&&和||始终只能操作两个值。所以这实际是三个子表达式连续操作的结果。我们来解读一下: + +如果我们输入了正确的密码(enteredDoorCode)并通过了视网膜扫描(passedRetinaScan); 或者我们有一把有效的钥匙(hasDoorKey); 又或者我们知道紧急情况下重置的密码(knowsOverridePassword),我们就能把门打开。 + +若前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们若知道紧急情况下重置的密码,整个复杂表达式的值还是`true`。 + +###使用括号来明确优先级 +为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要。在上个关于门的权限的例子中,我们给第一个部分加个括号,使它看起来逻辑更明确: + + if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { + println("Welcome!") + } else { + println("ACCESS DENIED") + } + // 输出 "Welcome!" + +这个括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说,有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧! + + diff --git a/src/chapter2/03_Strings_and_Characters.md b/src/chapter2/03_Strings_and_Characters.md index e69de29..6dff1b9 100644 --- a/src/chapter2/03_Strings_and_Characters.md +++ b/src/chapter2/03_Strings_and_Characters.md @@ -0,0 +1,607 @@ +# 字符和字符串 + + +A string is an ordered collection of characters, such as "hello, world" or "albatross". Swift strings are represented by the String type, which in turn represents a collection of values of Character type. + +字符串指的是一组字符的有序集合[to: 字符串指的是一组有序的字符集合],比如「hello, world」或者「albatoross」。在 Swift 里,字符串通过 `String` 类型表示,或者从另外一个角度来说,它是一组 `Character` 对象的值的集合 [to: Swift 中的字符串用 `String` 类型来表示,反过来说,它是一组 `Character` 类型的值集]。 + + + +Swift’s String and Character types provide a fast, Unicode-compliant way to work with text in your code. The syntax for string creation and manipulation is lightweight and readable, with a similar syntax to C strings. String concatenation is as simple as adding together two strings with the + operator, and string mutability is managed by choosing between a constant or a variable, just like any other value in Swift. + +Swift 的 `String` 和 `Character` 类型为代码里的字符操作提供了一种快速的、兼容 Unicode 的途径。跟 C 语言类似,在 Swift 里面新建和操作字符的语法十分简单易读。字符串的合并只需要在两个字符串中间见天一个 `+` 运算符,同时就像其它 Swift 值一样,可以通过定义字符串为常量或是变量来决定它是否可被修改。 [to:字符串合并就像用`+`操作符相加两个字符串那么简单,另外,就像Swift里的其他值,可以通过定义字符串为常量还是变量来设置它是否可被修改。] + + +Despite this simplicity of syntax, Swift’s String type is a fast, modern string implementation. Every string is composed of encoding-independent Unicode characters, and provides support for accessing those characters in various Unicode representations. + +尽量语法十分简单,Swift 的 `String` 类型是一个快速且现代化的字符串实现。每个字符串都由一组独立编码的 Unicode 字符组成,并且对这些字符提供了多种可访问的 Unicode 编码形式。 [to:每个字符串由一组独立编码的 Unicode 字符组成,并提供了这些字符的多种可访问 Unicode 编码形式。] + +Strings can also be used to insert constants, variables, literals, and expressions into longer strings, in a process known as string interpolation. This makes it easy to create custom string values for display, storage, and printing. + +[to:字符串还可以通过字符串插值这种方式插入常量、变量、字面量或表达式来变得更长。这使得创建用来展示、存储和打印的自定义字符串值变得简单。] + +> ### NOTE +> +> Swift’s String type is bridged seamlessly to Foundation’s NSString class. If you are working with the Foundation framework in Cocoa or Cocoa Touch, the entire NSString API is available to call on any String value you create, in addition to the String features described in this chapter. You can also use a String value with any API that requires an NSString instance. +> +> For more information about using String with Foundation and Cocoa, see Using Swift with Cocoa and Objective-C. + +> ### 需要注意 + +> Swift 的 `String` 类型无缝接入了 Foundation 的 `NSString` 类。如果你之前在 Cocoa 或 Cocoa Touch 中使用了 Foundation 框架,所有 `NSString` 的 API 都可以用在新的 `String` 实例对象上,而且 `String` 对象还额外添加了下文中提到的特性。同时也可以把 `String` 对象用在任何需要传入 `NSString` 实例为参数的 API 中。 + +> 更多关于 `String` 和 Foundation 或 Coca 使用方法的信息,请查看 [Using Swift with Cocoa and Objective-C]()。 + +## String Literals + +## 字符串字面量 + +You can include predefined String values within your code as string literals. A string literal is a fixed sequence of textual characters surrounded by a pair of double quotes (""). + +我们可以在代码的前面先定义一些字符串字面量。 [to: 我们可以在代码中预定义`Sting`值为*字符串字面量*。] 字符串字面量是由一对引号("")包裹的含有相对顺序的字符集合。[to: 字符串字面量是由一对引号(`""`)包围的固定文本字符序列。] + +A string literal can be used to provide an initial value for a constant or variable: + +字符串字面量可以给常量或者变量提供一个初始值[to:提供初始值]: + + let someString = "Some string literal value" + +Note that Swift infers a type of String for the someString constant, because it is initialized with a string literal value. + +需要注意的是,Swift 会自动把 `someString` 定义为 `String` 类型,因为它的初始值是一个字符串字面量。 + + +String literals can include the following special characters: + +字符串字面量可以包含以下特殊字符: + +* The escaped special characters \0 (null character), \\ (backslash), \t (horizontal tab), \n (line feed), \r (carriage return), \" (double quote) and \' (single quote) +* Single-byte Unicode scalars, written as \xnn, where nn is two hexadecimal digits +* Two-byte Unicode scalars, written as \unnnn, where nnnn is four hexadecimal digits +* Four-byte Unicode scalars, written as \Unnnnnnnn, where nnnnnnnn is eight hexadecimal digits + +* 转义后的特殊字符 `\0`(空字符),`\\`(斜杠),`\t`(水平 Tab),`\n`(换行),`\r`(回车),`\"`(双引号),`\'`(单引号); +* 单字节 Unicode 字符,一般写为 `\xnn`,其中 `nn` 是两位十六进制数值; +* 双字节 Unicode 字符,一般写为 `\unnnn`,其中 `nnnn` 是四位十六进制数值; +* 四字节 Unicode 字符,一般写为 `\Unnnnnnnn`,其中 `nnnnnnnn` 是八个十六进制数值; + +The code below shows an example of each kind of special character. The wiseWords constant contains two escaped double quote characters. The dollarSign, blackHeart, and sparklingHeart constants demonstrate the three different Unicode scalar character formats: + +下面的代码中,对各种特殊字符举了一些例子[to:给每种特殊字符各举了一个例子]。`wiseWords` 常量包含了两个被转义的引号字符。`dollarSign`、`blackHeart` 和 `sparklingHeart` 常量展示了三种不同类型的 Unicode 字符表示方式。 + + let wiseWords = "\"Imagination is more important than knowledge\" - Einstein" + // "Imagination is more important than knowledge" - Einstein + let dollarSign = "\x24" // $, Unicode 字符 U+0024 + let blackHeart = "\u2665" // ♥, Unicode 字符 U+2665 + let sparklingHeart = "\U0001F496" // 💖, Unicode 字符 U+1F496 + +## Initializing an Empty String + +## 初始化字符串 + +To create an empty String value as the starting point for building a longer string, either assign an empty string literal to a variable, or initialize a new String instance with initializer syntax: + + var emptyString = "" // empty string literal + var anotherEmptyString = String() // initializer syntax + // these two strings are both empty, and are equivalent to each other + +一般我们会初始化一个空白的字符串[to:空字符串]作为后面组装长字符串的基础,Swift 提供了两种初始化字符串的方式,第一种是给变量赋予一个空白的字符串字面量[to:空字符串字面量],第二种是通过新建实例的语法: + + + var emptyString = "" // 空白的字符串字面量 + var anotherEmptyString = String() // 新建 String 实例的语法 + // 这两个字符串都是空白的,而且相互等价。 + +You can find out whether a String value is empty by checking its Boolean isEmpty property: + +可以通过 `String` 对象的 `isEmpty` 属性获取一个字符串是否为空,该属性返回的是布尔值: + + if emptyString.isEmpty { + println("Nothing to see here") + } + // prints "Nothing to see here" + + + if emptyString.isEmpty { + println("你看不到我") + } + // 会输出 "你看不到我" + + +## String Mutability + +## 字符串操作 + +You indicate whether a particular String can be modified (or mutated) by assigning it to a variable (in which case it can be modified), or to a constant (in which case it cannot be modified): + + var variableString = "Horse" + variableString += " and carriage" + // variableString is now "Horse and carriage" + + let constantString = "Highlander" + constantString += " and another Highlander" + // this reports a compile-time error - a constant string cannot be modified + + +一个字符串是否能被操作取决于它被定义为常量(不能修改)还是变量(允许修改): + + var variableString = "Horse" + variableString += " and carriage" + // 运行后 variableString 将会是 "Horse and carriage" + + let constantString = "Highlander" + constantString += " and another Highlander" + // 这段代码会在编译时报错,因为常量不能被修改 + +> ### NOTE + +> This approach is different from string mutation in Objective-C and Cocoa, where you choose between two classes (NSString and NSMutableString) to indicate whether a string can be mutated. + +> ### 需要注意 + +> 这个地方跟 Objective-C 和 Cocoa 不一致,与前者不同,后者可以使用两种类型的字符串 `NSString` 和 `NSMutableString` 来区分该字符是否能被修改[to:这种方式与 Objective-C 和 Cocoa 有所不同,这两者通过设置字符串为 `NSString` 还是 `NSMutableString` 来区分该字符是否能被修改]。 + + +## Strings Are Value Types + +## 字符串是值类型 + +Swift’s String type is a value type. If you create a new String value, that String value is copied when it is passed to a function or method, or when it is assigned to a constant or variable. In each case, a new copy of the existing String value is created, and the new copy is passed or assigned, not the original version. Value types are described in Structures and Enumerations Are Value Types. + + +Swift 中的 `String` 类型属于*值类型*,也就是说你一旦把一个字符串传进一个方法或者函数里面[to:传给一个方法或函数],或者重新赋值[to:给]一个新的常量或变量时,字符串的值都会被复制一遍。详细来说就是[to:在以上每种情况中,],String 类型会把原来的字符串复制为一个新的拷贝,再进行传递和分配,传递后的字符串已经不是原来的那个[to:传递的字符串不是最初的那个]。关于值类型的详细介绍会在[to:请参见] [Structures and Enumerations Are Value Types]()。 + + +> ### NOTE + +> This behavior differs from that of NSString in Cocoa. When you create an NSString instance in Cocoa, and pass it to a function or method or assign it to a variable, you are always passing or assigning a reference to the same single NSString. No copying of the string takes place, unless you specifically request it. + +> ## 需要注意 + +> 这个行为和 Cocoa 里的 `NSString` 有所 区别[to:不同]。 当实例化一个 `NSString` 对象之后,无论怎么在函数和方法之间传递,它都只是指向原来的值的引用[to:无论是把它传递给一个函数或方法,还是把它赋值给一个变量,我们传递或者赋值的是一个指向同一个 `NSString` 的引用]。并不会每次复制这个字符串对象,除非你主动要求这么做[to:除非我们特别要求,否则不会复制这个字符串对象]。 + + +Swift’s copy-by-default String behavior ensures that when a function or method passes you a String value, it is clear that you own that exact String value, regardless of where it came from. You can be confident that the string you are passed will not be modified unless you modify it yourself. + +Swift 这种默认复制的行为确保了每个方法或者函数里面接收到的字符串都完全属于这个方法或函数的,并不需要考虑它从哪来,这样就保证了字符串不会变动,除非主动修改它[to:这样就使得我们能够确保传递的字符串不会被修改,除非我们主动去修改它]。 + +Behind the scenes, Swift’s compiler optimizes string usage so that actual copying takes place only when absolutely necessary. This means you always get great performance when working with strings as value types. + +实际上,Swift 的编译器优化了字符串的使用 过程,只有绝对需要时才会产生复制的实际操作。这样就意味着 Swift 可以在字符串操作上和值类型保持一样的高性能。 + + +## Working with Characters + +## 字符处理 + +Swift’s String type represents a collection of Character values in a specified order. Each Character value represents a single Unicode character. You can access the individual Character values in a string by iterating over that string with a for-in loop: + +Swift 的 `String` 类型是由一系列 `Character` 对象根据一定的顺序排列组成的[to:是一组有着特定顺序的 `Character` 序列]。每个 `Character` 对象都对应着[to:代表了]一个 Unicode 字符。我们可以用一个 `for-in` 循环逐个读取字符串中的 `Character` 对象[to:值]: + + for character in "Dog!🐶" { + println(character) + } + // D + // o + // g + // ! + // 🐶 + + +The for-in loop is described in For Loops. + +`for-in` 循环的介绍在[to:介绍请参见] [For Loops]()。 + +Alternatively, create a stand-alone Character constant or variable from a single-character string literal by providing a Character type annotation: + +此外,可以通过为一个单独的字符指定 `Character` 类型去定义一个独立的 `Character` 常量或者变量[to:可以通过 `Character`类型声明单字符变量或常量]: + + let yenSign: Character = "¥" + + +## Counting Characters + +## 计算字符个数 + +To retrieve a count of the characters in a string, call the global countElements function and pass in a string as the function’s sole parameter: + + let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" + println("unusualMenagerie has \(countElements(unusualMenagerie)) characters") + // prints "unusualMenagerie has 40 characters" + +如果需要计算一个字符串中字符的数量,可以 通过 调用全局方法 `countElements` 并把该字符串作为唯一的参数: + + let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" + println("unusualMenagerie 拥有 \(countElements(unusualMenagerie)) 个字符") + // hui输出 "unusualMenagerie 拥有 40 个字符" + + +> ### NOTE + +> Different Unicode characters and different representations of the same Unicode character can require different amounts of memory to store. Because of this, characters in Swift do not each take up the same amount of memory within a string’s representation. As a result, the length of a string cannot be calculated without iterating through the string to consider each of its characters in turn. If you are working with particularly long string values, be aware that the countElements function must iterate over the characters within a string in order to calculate an accurate character count for that string. + +> Note also that the character count returned by countElements is not always the same as the length property of an NSString that contains the same characters. The length of an NSString is based on the number of 16-bit code units within the string’s UTF-16 representation and not the number of Unicode characters within the string. To reflect this fact, the length property from NSString is called utf16count when it is accessed on a Swift String value. + + +> ### 需要注意 + +> 不同的 Unicdoe 字符或者同一个 Unicode 字符的不同表示方式可能需要不同的内存大小进行存储。因此,在一个字符串中每个字符[to:一个字符串中的各个字符]可能占用不同的内存大小。结果导致[to:因此,]如果需要知道一个字符串的长度,必须通过迭代[to:遍历]整个字符串中的字符才能计算出来。如果我们在处理一个超长字符串,必须要注意的是 `countElements` 方法会迭代[to:遍历]整个字符串来精确计算该字符串的长度。 + +> 另外需要注意的是,对于同一个字符串,通过执行 `countElements` 方法返回的字符串数量不一定和 `NSString` 使用 `length` 属性得到的值一样。因为 `NSString` 的 `length` 值是基于 `UTF-16` 十六位编码方式[to:中的16位编码单元的数量来]统计的,并未直接统计字符串里 Unicode 字符的数量[to:而不是字符串里的 Unicode 字符的数量]。为了反映这个事实,Swift 里 `String` 对象的 `utf16count` 属性对应 `NSString` 类的 `length` 值。 + + +## Concatenating Strings and Characters + +## 字符和字符串的拼接 + +String and Character values can be added together (or concatenated) with the addition operator (+) to create a new String value: + + let string1 = "hello" + let string2 = " there" + let character1: Character = "!" + let character2: Character = "?" + + let stringPlusCharacter = string1 + character1 // equals "hello!" + let stringPlusString = string1 + string2 // equals "hello there" + let characterPlusString = character1 + string1 // equals "!hello" let characterPlusCharacter = character1 + character2 // equals "!?" + +`String` 和 `Character` 对象可以通过 `+` 运算符进行组合(或者拼接),从而产生一个新的 `String` 对象: + + + let string1 = "hello" + let string2 = " there" + let character1: Character = "!" + let character2: Character = "?" + + let stringPlusCharacter = string1 + character1 // 等于 "hello!" + let stringPlusString = string1 + string2 // 等于 "hello there" + let characterPlusString = character1 + string1 // 等于 "!hello" let characterPlusCharacter = character1 + character2 // 等于 "!?" + + +You can also append a String or Character value to an existing String variable with the addition assignment operator (+=): + + var instruction = "look over" + instruction += string2 + // instruction now equals "look over there" + + var welcome = "good morning" + welcome += character1 + // welcome now equals "good morning!" + +也可以通过加法赋值运算符 `+=` 在一个已有的 `String` 变量后面追加 `String` 或 `Character` 对象: + + var instruction = "look over" + instruction += string2 + // instruction 现在是 "look over there" + + var welcome = "good morning" + welcome += character1 + // welcome now 现在是 "good morning!" + +> ### NOTE + +> You can’t append a String or Character to an existing Character variable, because a Character value must contain a single character only. + +> ### 需要注意 + +> 无法在 `Character` 对象后面追加任何 `String` 或 `Character` 字符,因为 `Character` 对象必须只有一个字符。 + + +## String Interpolation + +## 字符串插值 + +String interpolation is a way to construct a new String value from a mix of constants, variables, literals, and expressions by including their values inside a string literal. Each item that you insert into the string literal is wrapped in a pair of parentheses, prefixed by a backslash: + + let multiplier = 3 + let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" + // message is "3 times 2.5 is 7.5" + + +字符串插值是一种新的创建 `String` 对象的方式,它可以在字符串中插入一系列的常量、变量、字面量和包含值运算的表达式。每种插值在字符串中必须用以斜线 `\` 打头并用圆括号 `()` 包裹起来: + + let multiplier = 3 + let message = "\(multiplier) 的 2.5 倍是 \(Double(multiplier) * 2.5)" + // 运行后输出 "3 的 2.5 倍是 7.5" + +In the example above, the value of multiplier is inserted into a string literal as \(multiplier). This placeholder is replaced with the actual value of multiplier when the string interpolation is evaluated to create an actual string. + +在上面的例子中,`multiplier` 的值以 `\(multiplier)` 的方式插入到字符串中,这个占位符会在字符串进行插值运算时自动替换成 `multiplier` 实际的值。 + +The value of multiplier is also part of a larger expression later in the string. This expression calculates the value of Double(multiplier) * 2.5 and inserts the result (7.5) into the string. In this case, the expression is written as \(Double(multiplier) * 2.5) when it is included inside the string literal. + +`multiplier` 同时也在字符串后面的表达式中出现,这个表达式会被执行并把计算结果(7.5)插入到字符串中。在这个案例里面,该表达式被写成了 `\(Double(multiplier) * 2.5)` 并插在了字符串里面[to: 当被包含在字符串字面量中时,该表达式被写为 `\(Double(multiplier) * 2.5)`]。 + + +## Comparing Strings + +## 字符串比较 + +Swift provides three ways to compare String values: string equality, prefix equality, and suffix equality. + +Swift 提供了三种对比字符串的方式:全值比较、前缀比较和后缀比较。 + +### String Equality + +### 全值比较 + +Two String values are considered equal if they contain exactly the same characters in the same order: + + let quotation = "We're a lot alike, you and I." + let sameQuotation = "We're a lot alike, you and I." + if quotation == sameQuotation { + println("These two strings are considered equal") + } + // prints "These two strings are considered equal" + + + +如果两个字符串由相同的字符和一致的顺序组成[to:包含的字符完全相同,并且这些字符的排列顺序也相同],那就认为这两个字符串是完全相等的: + + let quotation = "We're a lot alike, you and I." + let sameQuotation = "We're a lot alike, you and I." + if quotation == sameQuotation { + println("这两个字符串是相等的") + } + // 运行后会输出 "这两个字符串是相等的" + +### Prefix and Suffix Equality + +### 前缀比较和后缀比较 + +To check whether a string has a particular string prefix or suffix, call the string’s hasPrefix and hasSuffix methods, both of which take a single argument of type String and return a Boolean value. Both methods perform a character-by-character comparison between the base string and the prefix or suffix string. + +通过调用字符串的 `hasPrefix` 或 `hasSuffix` [to:方法]来判断该字符串是否含有特定的前缀或后缀,这两个方法分别都接收一个字符串参数,结果[to:并]返回布尔值。这两个方法会在字符串和需要对比的前缀或后缀进行逐字对比。 + +The examples below consider an array of strings representing the scene locations from the first two acts of Shakespeare’s Romeo and Juliet: + + let romeoAndJuliet = [ + "Act 1 Scene 1: Verona, A public place", + "Act 1 Scene 2: Capulet's mansion", + "Act 1 Scene 3: A room in Capulet's mansion", + "Act 1 Scene 4: A street outside Capulet's mansion", + "Act 1 Scene 5: The Great Hall in Capulet's mansion", + "Act 2 Scene 1: Outside Capulet's mansion", + "Act 2 Scene 2: Capulet's orchard", + "Act 2 Scene 3: Outside Friar Lawrence's cell", + "Act 2 Scene 4: A street in Verona", + "Act 2 Scene 5: Capulet's mansion", + "Act 2 Scene 6: Friar Lawrence's cell" + ] + +下面的例子来自莎士比亚著作的《罗密欧与茱丽叶》,我们用前两场戏每一幕对应的地点组成一个数组: + + let romeoAndJuliet = [ + "Act 1 Scene 1: Verona, A public place", + "Act 1 Scene 2: Capulet's mansion", + "Act 1 Scene 3: A room in Capulet's mansion", + "Act 1 Scene 4: A street outside Capulet's mansion", + "Act 1 Scene 5: The Great Hall in Capulet's mansion", + "Act 2 Scene 1: Outside Capulet's mansion", + "Act 2 Scene 2: Capulet's orchard", + "Act 2 Scene 3: Outside Friar Lawrence's cell", + "Act 2 Scene 4: A street in Verona", + "Act 2 Scene 5: Capulet's mansion", + "Act 2 Scene 6: Friar Lawrence's cell" + ] + +You can use the hasPrefix method with the romeoAndJuliet array to count the number of scenes in Act 1 of the play: + + var act1SceneCount = 0 + for scene in romeoAndJuliet { + if scene.hasPrefix("Act 1 ") { + ++act1SceneCount + } + } + println("There are \(act1SceneCount) scenes in Act 1") + // prints "There are 5 scenes in Act 1" + +我们可以用 `hasPrefix` 方法统计在 `romeoAndJuliet` 数组中第一场戏一共有几幕: + + var act1SceneCount = 0 + for scene in romeoAndJuliet { + if scene.hasPrefix("Act 1 ") { + ++act1SceneCount + } + } + println("第一场戏一共有 \(act1SceneCount) 幕") + // 运行后输出: "第一场戏一共有 5 幕" + +Similarly, use the hasSuffix method to count the number of scenes that take place in or around Capulet’s mansion and Friar Lawrence’s cell: + + var mansionCount = 0 + var cellCount = 0 + for scene in romeoAndJuliet { + if scene.hasSuffix("Capulet's mansion") { + ++mansionCount + } else if scene.hasSuffix("Friar Lawrence's cell") { + ++cellCount + } + } + println("\(mansionCount) mansion scenes; \(cellCount) cell scenes") + // prints "6 mansion scenes; 2 cell scenes" + + +类似地,用 `hasSuffix` 方法来统计发生在 `Capulet's mansion` 和 `Friar Lawrence's cell` 场景的数量: + + var mansionCount = 0 + var cellCount = 0 + for scene in romeoAndJuliet { + if scene.hasSuffix("Capulet's mansion") { + ++mansionCount + } else if scene.hasSuffix("Friar Lawrence's cell") { + ++cellCount + } + } + println("\(mansionCount) mansion scenes; \(cellCount) cell scenes") + // 运行后输出: "6 mansion scenes; 2 cell scenes" + + +## Uppercase and Lowercase Strings + +## 字符串的大小写转换 + +You can access an uppercase or lowercase version of a string with its uppercaseString and lowercaseString properties: + +``` +let normal = "Could you help me, please?" +let shouty = normal.uppercaseString +// shouty is equal to "COULD YOU HELP ME, PLEASE?" +let whispered = normal.lowercaseString +// whispered is equal to "could you help me, please?" +``` + +[to:我们]可以通过字符串本身的 `uppercaseString` 或 `lowercaseString` 属性取得它的全大些或全小写版本[to:某个字符串的大写或小写版本]: + + +``` +let normal = "Could you help me, please?" +let shouty = normal.uppercaseString +// shouty 等价于 "COULD YOU HELP ME, PLEASE?" +let whispered = normal.lowercaseString +// whispered 等价于 "could you help me, please?" +``` + + +## Unicode + +## Unicode 字符 + +Unicode is an international standard for encoding and representing text. It enables you to represent almost any character from any language in a standardized form, and to read and write those characters to and from an external source such as a text file or web page. + +`Unicode` 是一个用来编码和表示文本的国际标准。它允许我们用 [to: 使得我们可以用] 一个标准格式来表示世界上几乎所有的字符,并且提供了在类似文本文件或网页中读取和写入这些字符的能力。 + +Swift’s String and Character types are fully Unicode-compliant. They support a number of different Unicode encodings, as described below. + +Swift 的 `String` 和 `Character` 类型完全兼容 Unicode ,并且提供了大量 Unicode 编码的支持,下面我们会提到这点。 + +### Unicode Terminology + +### Unicode 的术语 + +Every character in Unicode can be represented by one or more unicode scalars. A unicode scalar is a unique 21-bit number (and name) for a character or modifier, such as U+0061 for LOWERCASE LATIN LETTER A ("a"), or U+1F425 for FRONT-FACING BABY CHICK ("🐥"). + +Unicode 里的每个字符都对应这一个或多个 uincode 标量。每个 unicode 标量是一个 21 位的数值(包括名称),代表着一个字符或者修饰符。比如 `U+0061` 是 `LOWERCASE LATIN LETTER A ("a")`, `U+1F425` 是 `FRONT-FACING BABY CHICK ("🐥")`。 + +When a Unicode string is written to a text file or some other storage, these unicode scalars are encoded in one of several Unicode-defined formats. Each format encodes the string in small chunks known as code units. These include the UTF-8 format (which encodes a string as 8-bit code units) and the UTF-16 format (which encodes a string as 16-bit code units). + +当一个 Unicode 的字符串被写进文本文件或者其它存储形式时,这些 unicode 标量就会从几种 Unicode 制定的编码方式中选取其中一种把该字符串编码成小块的编码单元。这些编码方式包括了 `UTF-8` 格式(会把字符串编码成 8 位编码单元),`UTF-16` 格式(把字符串编码成 16 位编码单元)。 + +### Unicode Representations of Strings + +### Unicode 字符串的表示方式 + +Swift provides several different ways to access Unicode representations of strings. + +Swift 对 Unicode 字符提供了几种不同的访问方式。 +[to: Swift 提供了不同的方法来访问字符的Unicode形式。] + +You can iterate over the string with a for-in statement, to access its individual Character values as Unicode characters. This process is described in Working with Characters. + +首先可以从字符串的逐字迭代中取得每个 `Character` 中的 Unicode 字符。[to: 我们可以用 `for-in`语句遍历字符串来获取到字符串中每个`Character` 相应的 Unicode 字符。] 这个过程在上面的 [字符处理]() 章节中有提到。 + + +Alternatively, access a String value in one of three other Unicode-compliant representations: + +``` +A collection of UTF-8 code units (accessed with the string’s utf8 property) +A collection of UTF-16 code units (accessed with the string’s utf16 property) +A collection of 21-bit Unicode scalar values (accessed with the string’s unicodeScalars property) +``` + +另外,还可以通过以下三种 方式中的一种访问字符串中兼容 Unicode 的字符数据[to:统一编码方式来访问一个 `String` 的值]: + +``` +一组以 UTF-8 编码的单元 (通过字符串的 utf8 属性访问) +一组以 UTF-16 编码的单元 (通过字符串的 utf16 属性访问) +一组以 21 位编码的单元 (通过字符串的 unicodeScalars 属性访问) +``` + +Each example below shows a different representation of the following string, which is made up of the characters D, o, g, !, and the 🐶 character (DOG FACE, or Unicode scalar U+1F436): + +下面的每个示例 描述了[to: 给出了] 以下字符串不同的几种表示方法[to:的几种不同表示方法],这个字符串由这几个字符组成:`D`、`o`、`g`、`!` 和 `🐶` 字符(`DOG FACE` 或者 Unicode 标量 `U+1F436`) + +``` +let dogString = "Dog!🐶" +``` + + +### UTF-8 + +You can access a UTF-8 representation of a String by iterating over its utf8 property. This property is of type UTF8View, which is a collection of unsigned 8-bit (UInt8) values, one for each byte in the string’s UTF-8 representation: + +我们可以通过 迭代访问[to:遍历] 字符串的 `utf8` 属性来获得该字符串的 `UTF-8` 编码形式。该属性的类型是 `UTF8View`,由一组无符号的 8 位数值(`UInt8`) [to: 8 位(`UInt8`)数值]组成,每个数值代表了字符串用 `UTF-8` 编码后的字节[to:每个字节都是字符串的 UTF-8 编码形式]。 + +``` +for codeUnit in dogString.utf8 { + print("\(codeUnit) ") +} +print("\n") +// 68 111 103 33 240 159 144 182 +``` + +In the example above, the first four decimal codeUnit values (68, 111, 103, 33) represent the characters D, o, g, and !, whose UTF-8 representation is the same as their ASCII representation. The last four codeUnit values (240, 159, 144, 182) are a four-byte UTF-8 representation of the DOG FACE character. + +上面的例子中,前四位十进制的编码单元[to: `(68, 111, 103, 33)`]分别代表了 `D`、`o`、`g`、`!`,这几个字符的编码形式跟 ASCII 是一致的[to:相同的]。最后四位编码单元(240,159,144,182)是一个 4 位的 UTF-8 编码表示形式,用来表示 `DOG FACE` 字符[to: 字符 `DOG FACE` 的 4 位 UTF-8 编码形式]。 + +### UTF-16 + +You can access a UTF-16 representation of a String by iterating over its utf16 property. This property is of type UTF16View, which is a collection of unsigned 16-bit (UInt16) values, one for each 16-bit code unit in the string’s UTF-16 representation: + +通过迭代访问[to:遍历]字符串的 `utf16` 属性可以取得该字符串的 `UTF-16` 编码形式,该属性的类型是 `UTF16View`,即一组无符号 16 位数值(UInt16)[to:16 位(UInt16)数值]的集合,每个数值代表了字符串用 `UTF-16` 编码后的字节[to:每个字节都是字符串的 UTF-16 编码形式]。 + +``` +for codeUnit in dogString.utf16 { + print("\(codeUnit) ") +} +print("\n") +// 68 111 103 33 55357 56374 +``` + +Again, the first four codeUnit values (68, 111, 103, 33) represent the characters D, o, g, and !, whose UTF-16 code units have the same values as in the string’s UTF-8 representation. + +类似的,前四个编码单元(`68,111,103,33`)代表了前四个字符 `D`、`o`、`g` 和 `!`,这几个字符 `UTF-16` 和 `UTF-8` 拥有相同的表示方式[to: 的`UTF-16` 表示方式同 `UTF-8` 的一样]。 + +The fifth and sixth codeUnit values (55357 and 56374) are a UTF-16 surrogate pair representation of the DOG FACE character. These values are a lead surrogate value of U+D83D (decimal value 55357) and a trail surrogate value of U+DC36 (decimal value 56374). + +第五个和第六个`编码单元`(`55357` 和 `56374`)则是 `DOG FACE` 字符的代理项。这两个数值由第一部分的 `U+D83D`(十进制 55357) 和 `U+DC36`(十进制 56374)组成。 + +### Unicode Scalars + +### Unicode 标量 + +You can access a Unicode scalar representation of a String value by iterating over its unicodeScalars property. This property is of type UnicodeScalarView, which is a collection of values of type UnicodeScalar. A Unicode scalar is any 21-bit Unicode code point that is not a lead surrogate or trail surrogate code point. + +通过迭代[to:遍历]字符串的 `unicodeScalars` 属性可以获得该字符串的 Unicode 标量表示形式。该属性的类型是 `UnicodeScalarView`,由一组 `UnicodeScalar` 类型的值组成。一个 Unicode 标量是一个完整的 21 位编码,既没有首码位,也没有尾码位。 + +Each UnicodeScalar has a value property that returns the scalar’s 21-bit value, represented within a UInt32 value: + +每个 `UnicodeScalar` 都有一个 `value` 属性可以获得它的 21 位(UInt32)标量值: + +``` +for scalar in dogString.unicodeScalars { + print("\(scalar.value) ") +} +print("\n") +// 68 111 103 33 128054 +``` + +The value properties for the first four UnicodeScalar values (68, 111, 103, 33) once again represent the characters D, o, g, and !. The value property of the fifth and final UnicodeScalar, 128054, is a decimal equivalent of the hexadecimal value 1F436, which is equivalent to the Unicode scalar U+1F436 for the DOG FACE character. + +前四个 `UnicodeScalar` 对象的 `value` 属性值(`68,111,103,33`)按照惯例还是代表着 `D`、`o`、`g` 和 `!` 四个字符。而第五个也就是最后一个 `UnicodeScalar` 数值 128054 的十六进制是 1F436,刚好等价于 `DOG FACE` 字符的 Unicode 标量 `U+1F436` 。 + +As an alternative to querying their value properties, each UnicodeScalar value can also be used to construct a new String value, such as with string interpolation: + +除了使用它们的 `value` 属性,每个 `UnicodeScalar` 对象也可以用来直接构造新的 `String` 对象,比如这里用的字符串插值方法: + +``` +for scalar in dogString.unicodeScalars { + println("\(scalar) ") +} +// D +// o +// g +// ! +// 🐶 +``` + diff --git a/src/chapter2/04_Collection_Types.md b/src/chapter2/04_Collection_Types.md index e69de29..7e2c8d2 100644 --- a/src/chapter2/04_Collection_Types.md +++ b/src/chapter2/04_Collection_Types.md @@ -0,0 +1,424 @@ +# 集合类型 + +Swift 提供了两种*集合类型* 来存储数据集合,分别是数组(arrays)和字典(dictionaries)。数组用来存储有序的相同类型数据的列表。字典用来存储无序的相同类型数据的集合,这些数据可以通过唯一的标识符(称为*键*)引用和查找。 + +在Swift中,数组和字典可以存储的键值类型总是明确的。这意味着不会把错误类型的值意外地插入到数组或字典中。这也意味着我们可以清楚的知道从数组或字典中获取的数据类型。Swift 使用显示类型集合,这确保了代码中可以处理的数据类型是明确的,并让我们可以提前发现开发中的任何类型错误。 + + +>注意 + +>Swift的`Array`类型在赋值给常量、变量或传入函数和方法时与其他类型的行为稍有不同。获取更多信息请参见[Mutability of Collections](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/04_Collection_Types.md#mutability-of-collections)and[AssignmentCopy Behavior for Collection Types]()这两个章节。 + + +## 数组 + +*数组*在一个有序列表中存储了多个相同类型的数据。相同的数据可以多次出现在数组的不同位置。 + +Swift 数组可以存储的数据类型是确定的。这与 Objective-C 的类 `NSArray` 和 `NSMutableArray` 不同,这两个类可以存储任何类型的对象,并且不会提供它们返回的对象的任何信息。在 Swift 中,某个数组可以存储的数据类型是确定的,可以使用显式的类型声明,也可以使用类型推断,且不必一定是 class 类型。假如你创建了一个 `Int` 型的数组,那么就不能在这个数组中插入其他任何非 `Int` 型的值。Swift 数组是类型安全的,并且它们可以包含的类型是明确的。 +‌ +### 数组类型简写语法 + +Swift 数组类型的完整写法是 `Array`, 这里的 `SomeType` 指的是数组可以存储的类型。我们也可以把它简写为 `SomeType[]`。尽管这两种形式在功能上完全相同,但更推荐简写形式,且本书中提到数组类型时都会使用这种形式。 +‌ +### 数组字面量 + +我们可以用*数组字面量*来初始化数组,这是一个或多个值作为数组集合的简写方式。数组字面量写作一个数值列表,由逗号分隔,被一对方括号括起来: + +``` + [value 1, value 2, value 3] +``` + +下面这个例子创建了一个名为 `shoppingList` 的数组来存储 `String` 类型的数据。 + +``` +var shoppingList: String[] = ["Eggs", "Milk"] +// 使用两个初始化项,初始化了shoppingList +``` + +变量shoppingList声明为“`String` 类型的数组”,写为 `String[]`。由于这个特定数组指定了 `String` 的数据类型,因此它*只能*存储 `String` 类型的数据。这里的 `shoppingList` 数组由两个`String` 值(`"Eggs" `和 `"Milk"`)以数组字面量来初始化。 + +>注意 + +>数组 `shoppingList` 声明为变量(通过关键字 `var` )而不是常量(通过关键字 `let`),因为在下面的例子中,会有更多的数组项添加到这个购物列表中。 + +在这个例子中,该数组字面量只包含了两个 `String` 值。这和 `shoppingList` 变量的声明(只能包含 `String` 值的数组)是一致的,因此数组字面量的赋值作为一种方式,可以使用两个初始项初始化`shoppingList`。 + +多亏了 Swift 的类型推断,如果使用包含相同类型数据的数组字面量,我们不需要写出数组的类型。`shopplingList`的初始化可以简写为: + +``` +var shoppingList = ["Eggs", "Milk"] +``` + +因为数组字面量中所有的值的类型是相同的,Swift 可以推断出 `String[]` 是 `shoppingList` 变量使用的正确类型。 +‌ +### 访问和修改数组 + +我们可以通过数组的方法、属性或使用下标语法来访问和修改数组。 + +可以通过数组的只读属性 `count` 来获得数组包含的数组项个数。 + +``` +println("The shopping list contains \(shoppingList.count) items.") +// 打印出 "The shopping list contains 2 items." +``` + +用布尔值 `isEmpty` 属性来作为检查 `count` 属性是否等于 `0` 的快捷方式。 + +``` +if shoppingList.isEmpty { + println("The shopping list is empty.") +} else { + println("The shopping list is not empty.") +} +// 打印出 "The shopping list is not empty.” + +``` + +可以通过调用数组的 `append` 方法,在数组的末尾插入一个新的数据项。 + +``` +shoppingList.append("Flour") + +// shoppingList 现在包含3项, 有人在做煎饼 +``` + +或者,可以通过加法赋值运算符(`+=`)在数组的末尾添加新的数据项。 + +``` +shoppingList += "Baking Powder" +// shoppingList 现在包含4项 +``` + +你还可以通过加法赋值运算符(`+=`)插入一个同类型的数组。 + +``` +shoppingList += ["Chocolate Spread", "Cheese", "Butter"] +// shoppingList 现在包含7项 +``` + +通过*下标语法*从数组中获取某个值,即在数组名后紧跟的方括号中传入想要获取的值的索引: + +``` +var firstItem = shoppingList[0] +// firstItem 等于 "Eggs" +``` + +注意,数组中第一个数组项的索引为`0`,而不是`1`。Swift数组的索引是从零开始的。 + +使用下标语法可以在给定的索引处改变已存在的值。 + +``` +shoppingList[0] = "Six eggs" +// 列表中第一项现在等于 "Six eggs" ,而不是"Eggs" +``` + +我们还可以通过下标语法一次改变一个范围内的值,即使替换的值的集合与被替换的范围的长度不一致。下面的例子用 `"Bananas"` and `"Apples"`替换了 `"Chocolate Spread"`, `"Cheese"`, and `"Butter"`: + +``` +shoppingList[4...6] = ["Bananas", "Apples"] +// shoppingList现在包含6项 +``` + +>注意 + +>不能使用下标语法在数组的末尾添加新的数据项。如果我们试图用一个超过数组边界的下标访问或设置某个数值时,会导致运行时错误。不过,我们可以通过比较索引和数组的 `count` 属性来判断该索引是否有效。除了 count 为`0`时(意味着这个数组是空的),数组中最大的有效索引为 `count-1`,因为数组的索引是从0开始的。 + +要在数组的指定索引处插入一个数组项,可以调用数组的 `insert(atIndex:)` 方法: + +``` +shoppingList.insert("Maple Syrup", atIndex: 0) +// shoppingList 现在包含7项 +// "Maple Syrup" 现在是列表的第一项 +``` + +这个例子调用 `insert` 方法指定索引为 `0`,在 shoppingList 的最前面插入了值为 `“Maple Syrup”` 的新数据项。 + +类似地,`removeAtIndex` 方法可以从数组中移除一个数组项。该方法移除指定索引处的数组项,并返回被移除的数组项(如果不需要,我们可以忽略返回值): + +``` +let mapleSyrup = shoppingList.removeAtIndex(0) +// 索引为0的数据项被移除 +// shoppingList 现在包含6项, 不包括 Maple Syrup +// mapleSyrup 现在等于被移除的 " Maple Syrup" 字符串 +``` + +当一个数组项被移除后数组中的空缺项会被自动填补,因此索引`0`对应的值再次等于`“Six eggs”`: + +``` +firstItem = shoppingList[0] +// firstItem 现在等于"Six eggs" +``` + +如果我们想要从数组中移除最后一个数组项,最好使用 `removeLast` 方法,而不是 `removeAtIndex` 方法,这样可以避免查询数组的 `count` 属性。像 `removeAtIndex` 方法那样,`removeLast` 方法也会返回被移除的数组项: + +``` +let apples = shoppingList.removeLast() +// 数组中最后一项被移除 +// shoppingList 现在包含5项, 不包括cheese +// apples 现在等于被移除的"Apples" 字符串 +``` + +### 遍历数组 +我们可以使用 `for-in` 循环来遍历数组中的所有数组项: + +``` +for item in shoppingList { + println(item) +} +// Six eggs +// Milk +// Flour +// Baking Powder +// Bananas +``` +如果需要每一项的整型索引及对应的值,那么可以使用全局函数 `enumerate` 来遍历数组。函数 `enumerate` 返回由每个数组项的索引和对应的值组成的元组。我们可以把元组分解为临时的常量或者变量来进行遍历。 + +``` +for (index, value) in enumerate(shoppingList) { + println("Item \(index + 1): \(value)") +} +// Item 1: Six eggs +// Item 2: Milk +// Item 3: Flour +// Item 4: Baking Powder +// Item 5: Bananas +``` +获取更多关于 `for-in` 循环的介绍,请参见 [For循环](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/05_Control_Flow.md#for%E5%BE%AA%E7%8E%AF)。 + + +### 创建和初始化数组 + +我们可以使用初始化语法来创建某一特定类型的空数组(没有设置任何初始值): + +``` +var someInts = Int[]() +println("someInts is of type Int[] with \(someInts.count) items.") +// 打印出 "someInts is of type Int[] with 0 items." +``` +注意,变量 `someInts` 的类型被认为是 `Int[]`,因为它设置为`Int[]`初始化器的输出结果。 + +或者,如果上下文已经提供了类型信息,例如一个函数参数,或一个已经指定类型的变量或常量,我们可以用空数组字面量来创建空数组,写为`[]`(一对空的方括号): + +``` +someInts.append(3) +// someInts 现在包含 1 个 Int 值 + +someInts = [] +// someInts 现在是一个空数组, 但仍然是Int[]类型 +``` + +Swift `数组`类型还提供了一个初始化器来创建指定大小的数组,该数组的数据项设置为提供的默认值。我们把新加的数组项的大小(称为`count`)和相应类型的默认值(称为`repeatedValue`)传递给初始化器。 + +``` +var threeDoubles = Double[](count: 3, repeatedValue: 0.0) +// threeDoubles 是 Double[] 类型,等于[0.0, 0.0, 0.0] +``` +多亏有类型推导,使用初始化器时,可以不需要指定数组存储的类型,因为可以从默认值推导类型: + +``` +var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) +// anotherThreeDoubles 推导为 Double[] 类型,等于 [2.5, 2.5, 2.5] +``` +最后,我们可以使用加法操作符(`+`)把两个已经存在的相同类型的数组相加来创建一个新的数组。这个新数组的类型是从两个相加的数组的类型推导出来的: + +``` +var sixDoubles = threeDoubles + anotherThreeDoubles +// sixDoubles 推导为Double[]类型, 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] +``` + + +## 字典 + +*字典*是存储多个相同类型数值的容器。每个值和字典中作为该值的唯一标识符的*键* (key) 相关联。和数组中的数据项不同,字典中的数据项没有顺序。我们可以使用字典根据标识符来查找数据,就像在现实世界中可以用字典来查找某个特定字词的定义。 + +Swift 字典存储的键和值的类型是确定的。他们和 Objective-C 的类 `NSDictionary` 和 `NSMutableDictionary` 有所不同,这些类可以用任意一种对象作为他们的键和值,且不提供这些对象本质相关的任何信息。在 Swift 中,某个字典可以存储的键和值的类型是确定的,可以使用显式类型声明,也可以使用类型推断。 + +Swift 字典类型写为 `Dictionary`,这里的 `KeyType` 是可以作为字典键的数据类型,`ValueType` 是字典中存储的与键相对应的值的类型。 + +`KeyType` 的唯一限制是它必须是*可哈希的*——也就是说,它必须提供一个方法可以唯一代表自己。所有的 Swift 基础类型(如 `String`、`Int`、 `Double` 和 `Bool`)默认都是可哈希的,所有的这些类型都可以被用来作为字典的键。没有对应值的枚举成员数据(参见 [Enumerations](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/08_Enumerations.md))默认也是可哈希的。 + +‌ +### 字典字面量 + +你可以用*字典字面量*来初始化一个字典,这和之前的数组字面量有着相似的语法。字典字面量是一个或多个键-值对作为`字典`集合的简写方式。 + +*键-值对*是键和值的组合。在字典字面量中,每个键-值对中的键和值由冒号分隔。键值对的列表,用逗号分隔,由方括号括起来: + +``` + [key 1: value 1, key 2: value 2, key 3: value 3] + ``` +下面的例子创建了一个字典来储存国际机场的名字。在这个字典中,键是由三个字母组成的国际航空运输协会代码,值是机场的名称: + +``` +var airports: Dictionary = ["TYO": "Tokyo", "DUB": "Dublin"] +``` +`airports`字典声明为`Dictionary`类型,即“一个键和值的类型都是`String`类型的字典”。 + +>注意 + +>`airports`字典声明为变量(用关键字`var`),而不是常量(用关键字`let`),因为在下面的例子中,会有更多的机场被添加到这个字典里。 + +`airports` 字典由包含两对键值对的字典字面量初始化。第一对有键`“TYO”` 和值 `“Tokyo”`。第二对有键 `“DUB”` 和值 `“Dublin”`。 + +这个字典字面量包含两个 `String`:`String` 类型的键值对。这和变量 `airports` 声明(只有 `String` 键和 `String` 值的字典)的类型一致,因此字典字面量的赋值作为一种方法,可以用两个初始项来初始化这个 `airports` 字典。 + +和数组一样,如用我们用键值类型相同的字典字面量来初始化字典,那么不需要写出它的类型。`airports` 的初始化可以简写为以下形式: + +``` +var airports = ["TYO": "Tokyo", "DUB": "Dublin"] +``` + +由于字面量中所有的键和值都是相同类型的,因此 Swift 可以推断出 `Dictionary` 是字典 `airports` 使用的正确类型。 +‌ +### 访问和修改字典 + +我们可以通过字典的方法和属性,或下标语法来访问和修改字典。像数组那样,我们可以通过检查只读属性 `count` 来获取`字典`中数据项的个数。 + +``` +println("The dictionary of airports contains \(airports.count) items.") +// 打印出 "The dictionary of airports contains 2 items." + +``` + +我们可以通过下标语法给字典添加新的数据项。使用相应类型的键作为下标索引,并赋予相应类型的值: + +``` +airports["LHR"] = "London" +// airports 字典现在包含3项 +``` + +我们还可以使用下标语法改变某个键对应的值: + +``` +airports["LHR"] = "London Heathrow" +// "LHR" 的值修改为 "London Heathrow" +``` + +字典的 `updateValue(forKey:)` 方法可作为下标的替代来设置或更新某个特定键对应的值。像上面下标的例子,`updateValue(forKey:)` 方法可以设置某个不存在的键的值或者更新某个已有的键的值。和下标不同的是,`updateValue(forKey:)` 方法在更新数据后会返回更新前的数值。这使得我们可以检查更新是否成功。 + +`updateValue(forKey:)` 方法返回字典值类型的可选值。举个例子,对于一个储存了 `String` 数据的字典,这个方法返回了一个 `String?类型` 或“可选的 `String`”类型的数据。如果在更新之前这个键是存在的,则这个可选数据返回更新之前的值,否则返回 `nil`。 + +``` +if let oldValue = airports.updateValue("Dublin International", forKey: "DUB") { + println("The old value for DUB was \(oldValue).") +} +// 打印出 "The old value for DUB was Dublin." +``` + +我们也可以使用下标语法来访问字典中某个特定键对应的值。因为有可能会查询一个没有对应值的键,字典的下标会返回字典数据类型的可选值。如果字典包含了与要查找的键相对应的值,下标返回的可选值包含那个键对应的值,否则下标返回 `nil`。 + +``` +if let airportName = airports["DUB"] { + println("The name of the airport is \(airportName).") +} else { + println("That airport is not in the airports dictionary.") +} +// 打印出 "The name of the airport is Dublin International." +``` + +我们可以使用下标语法设置某个指定键的值为 `nil` 来从字典中移除该键值对。 + +``` +airports["APL"] = "Apple International" +// "Apple International" 不是APL的真实机场,所以删除它 +airports["APL"] = nil +// APL 已经从字典中移除了 +``` + +我们还可以使用 `removeValueForKey` 方法从字典中移除一个键值对。如果要移除的键对应的值对存在,该方法会移除它们并返回被移除的值,如果对应的值不存在,则返回 `nil`。 + +``` +if let removedValue = airports.removeValueForKey("DUB") { + println("The removed airport's name is \(removedValue).") +} else { + println("The airports dictionary does not contain a value for DUB.") +} + +//打印出 "The removed airport's name is Dublin International." +``` + +### 字典遍历 +我们可以用 `for-in` 循环遍历一个字典中的键值对。字典中的每个数据项返回相应的(`键, 值`)元组,我们可以把元组分解为临时常量或变量用于遍历: + +``` +for (airportCode, airportName) in airports { + println("\(airportCode): \(airportName)") +} +// TYO: Tokyo +// LHR: London Heathrow +``` +更多关于 `for-in` 循环的介绍,请参见 [For循环](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/05_Control_Flow.md#for%E5%BE%AA%E7%8E%AF)。 + +可以使用字典的 `keys` 或 `values` 属性得到字典的键和值的遍历集合。 + +``` +for airportCode in airports.keys { + println("Airport code: \(airportCode)") +} +// Airport code: TYO +// Airport code: LHR + +for airportName in airports.values { + println("Airport name: \(airportName)") +} +// Airport name: Tokyo +// Airport name: London Heathrow +``` + +如果需要使用字典的API 生成键和值的 `Array` 实例,可以使用 `keys` 或 `values` 属性初始化新数组。 + +``` +let airportCodes = Array(airports.keys) +// airportCodes is ["TYO", "LHR"] + +let airportNames = Array(airports.values) +// airportNames is ["Tokyo", "London Heathrow"] +``` + +>注意 + +>Swift 字典类型是无序的集合。遍历字典时键、值以及键值对的访问顺序是不确定的。 +‌ + +### 创建空字典 + +和数组一样,我们可以使用初始化语法创建一个特定类型的空 `字典`。 + +``` +var namesOfIntegers = Dictionary() +// namesOfIntegers is an empty Dictionary + +// namesOfIntegers 是一个空字典 +``` + +这个例子创建了 `Int`,`String` 类型的空字典来存储整型的可读性名称。它的键的类型是 `Int`,值的类型是 `String`。 + +如果上下文已经提供了类型信息,可以用空字典字面量来创建空字典,写为[`:`] (中括号中放一个冒号): + +``` +namesOfIntegers[16] = "sixteen" +// namesOfIntegers 现在包含1个键-值对 + +namesOfIntegers = [:] +// namesOfIntegers再一次成为 Int, String 类型的空字典 +``` + +>注意 + +>在后台,Swift 数组和字典类型都是由*泛型集合*实现的。更多关于泛型类型和集合的介绍,请参见 [Generics](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/22_Generics.md)。 +‌ + +## 集合的易变性 + +数组和字典在单个集合中存储了多个数据。如果我们创建一个数组或字典并把它声明为变量,这个创建出来的集合是*易变的*。这意味着我们可以改变集合的大小,在集合中添加更多的数据项,或从中移除已有的数据项。相反的,如果把一个数组或字典声明为常量,则这个数组或字典是*不可变*的,它的大小是不会变化的。 + +对字典来说,不可变还意味着我们不能替换字典中某个已有的键对应的值。一个不可变字典的内容一旦设置就不能再改变。 + +对数组来说,不可变的意义稍有不同。我们仍不允许改变不可变数组的大小,但是,我们*被*允许改变数组中某个已有索引对应的值。这使得 Swift 的 `Array` 类型可以在操作固定大小的数组时提供最佳性能。 + +Swift 的 `Array` 类型的可变性还会影响数组实例如何赋值和修改。更多介绍请参见 [集合类型的声明和复制](https://github.com/trans4fun/The-Swift-Programming-Language/blob/master/src/chapter2/09_Classes_and_Structures.md#%E9%9B%86%E5%90%88%E7%9A%84%E8%B5%8B%E5%80%BC%E4%B8%8E%E6%8B%B7%E8%B4%9D)。 + +>注意 +>当集合的大小不需要改变时,创建一个不可变的集合是比较好的做法。这样做,Swift 编译器会优化创建的集合性能。 + + diff --git a/src/chapter2/05_Control_Flow.md b/src/chapter2/05_Control_Flow.md index e69de29..4130472 100644 --- a/src/chapter2/05_Control_Flow.md +++ b/src/chapter2/05_Control_Flow.md @@ -0,0 +1,961 @@ +# 控制流(Control Flow) + +Swift provides all the familiar control flow constructs of C-like languages. These include for and while loops to perform a task multiple times; if and switch statements to execute different branches of code based on certain conditions; and statements such as break and continue to transfer the flow of execution to another point in your code. + +Swift提供类似C语言的控制流语法,比如```for```和```while```循环;```if```和```switch```的条件判断语句语句以及``` break```,```continue```这种跳出循环的语句。 + +In addition to the traditional for-condition-increment loop found in C, Swift adds a for-in loop that makes it easy to iterate over arrays, dictionaries, ranges, strings, and other sequences. + +Swift除了支持类似C语言这种```for-condition-increment```的语句外,还支持```for-in```这种循环语句,可以轻松的遍历数组,字典,字符串等序列。 + +Swift’s switch statement is also considerably more powerful than its counterpart in C. The cases of a switch statement do not “fall through” to the next case in Swift, avoiding common C errors caused by missing break statements. Cases can match many different types of pattern, including range matches, tuples, and casts to a specific type. Matched values in a switch case can be bound to temporary constants or variables for use within the case’s body, and complex matching conditions can be expressed with a where clause for each case. + +Swift的```switch```语句和C语言相比很强大,它不会像C语言那样,因为在case的分支中忘记写break而执行了下一条case的语句。同时case语句可以进行不同类型的模式匹配,包括区间匹配,元组匹配和特定类型描述。```switch```语句中待匹配的值可以绑定到临时变量或常量上,便于```case```中得代码访问,同时,一些复杂的匹配条件可已通过```where```关键字来补充。 + + +#For循环 + +A for loop performs a set of statements a certain number of times. Swift provides two kinds of for loop: + +`for`循环可以将一段代码执行若干次。Swift提供两种`for`循环: + +for-in performs a set of statements for each item in a range, sequence, collection, or progression. +for-condition-increment performs a set of statements until a specific condition is met, typically by incrementing a counter each time the loop ends. + +* ```for-in```遍历并返回序列或集合中的每一个元素,执行一段代码 +* ```for-condition-increment```在符合某种条件下(通常是在每次循环结束时对计数器+1),返回序列或集合中的每一个元素,执行一段代码。 + +##For-In + +You use the for-in loop to iterate over collections of items, such as ranges of numbers, items in an array, or characters in a string. + +你可以使用```for-in```来遍历集合里的元素;一定范围内的数字;数组中的元素或者字符串中的字符。 + +This example prints the first few entries in the five-times-table: + +下面这个例子可以打印出一个5次循环的结果: + +```Java +for index in 1...5{ + println("\(index) times 5 is \(index * 5)") +} +// 1 times 5 is 5 +// 2 times 5 is 10 +// 3 times 5 is 15 +// 4 times 5 is 20 +// 5 times 5 is 25 +``` + +The collection of items being iterated is a closed range of numbers from 1 to 5 inclusive, as indicated by the use of the closed range operator (...). The value of index is set to the first number in the range (1), and the statements inside the loop are executed. In this case, the loop contains only one statement, which prints an entry from the five-times-table for the current value of index. After the statement is executed, the value of index is updated to contain the second value in the range (2), and the println function is called again. This process continues until the end of the range is reached. + +我们首先通过(```...```)语句创建了一个数字集合,然后枚举集合中的每个元素,```index```值首先被赋值为range(```1```),然后执行循环体内的代码,上面这个例子中循环体内只有一行代码,作用是打印出```index```的值。当这行代码执行完后,```index```值会被更新,被赋值为range(```2```),```println```再次被调用,如此反复,直到循环结束。 + +In the example above, index is a constant whose value is automatically set at the start of each iteration of the loop. As such, it does not have to be declared before it is used. It is implicitly declared simply by its inclusion in the loop declaration, without the need for a let declaration keyword. + +上面例子中`index`是个常量,在每次循环开始的时候会被自动赋值,因此它不需要在使用前通过`let`声明,因为它会被`for-in`隐式声明。 + +>注意 + +>常量`index`的作用域只存在于循环体内。如果你想在循环体外查看`index`的值或者使用它,你需要在循环开始前声明它。 + +If you don’t need each value from the range, you can ignore the values by using an underscore in place of a variable name: + +如果你在遍历某个集合时并不关心集合中的元素,你可以通过下划线来代替集合中的元素变量名: + +```Java +let base = 3 +let power = 10 +var answer = 1 +for _ in 1...power { + answer *= base +} +println("\(base) to the power of \(power) is \(answer)") +// prints "3 to the power of 10 is 59049” +``` + +This example calculates the value of one number to the power of another (in this case, 3 to the power of 10). It multiplies a starting value of 1 (that is, 3 to the power of 0) by 3, ten times, using a half-closed loop that starts with 0 and ends with 9. This calculation doesn’t need to know the individual counter values each time through the loop—it simply needs to execute the loop the correct number of times. The underscore character _ (used in place of a loop variable) causes the individual values to be ignored and does not provide access to the current value during each iteration of the loop. + +这个例子用来计算某个数字的某次方(这里是计算3的10次方)。它用起始值1乘以3,循环10次。这种计算并不需要知道当前是第几次循环(循环的计数器),只需要保证它执行次数是正确的即可。 + +Use the for-in loop with an array to iterate over its items:我们还可以 + +使用`for-in`遍历数组中的元素: + +```Java +let names = ["Anna", "Alex", "Brian", "Jack"] +for name in names { + println("Hello, \(name)!") +} +// Hello, Anna! +// Hello, Alex! +// Hello, Brian! +// Hello, Jack!” + +``` + +You can also iterate over a dictionary to access its key-value pairs. Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within in the body of the for-in loop. Here, the dictionary’s keys are decomposed into a constant called animalName, and the dictionary’s values are decomposed into a constant called legCount: + +你也同样可以遍历字典中的key-value。遍历时,字典中的每个元素都以`(key,value)`元组结构返回,你也可以在`for-in`中将元组中的key,value当做常量显式的分解出来。这里,key被分解为为常量`animalName`,value被分解为`legCount`: + +```Java +let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] +for (animalName, legCount) in numberOfLegs { + println("\(animalName)s have \(legCount) legs") +} +// spiders have 8 legs +// ants have 6 legs +// cats have 4 legs” + +``` + +Items in a Dictionary may not necessarily be iterated in the same order as they were inserted. The contents of a Dictionary are inherently unordered, and iterating over them does not guarantee the order in which they will be retrieved. For more on arrays and dictionaries, see Collection Types.) + +`Dictionary`中的元素的顺序在被遍历时可能和我们插入它的顺序不同,这是由于`Dictionary`自身就是无序的,它在被遍历时无法保证元素的顺序。想了解更多关于数组和字典的内容,请查阅[集合类型](link)一章。 + +In addition to arrays and dictionaries, you can also use the for-in loop to iterate over the Character values in a string: + +除了数组和字典,你同样可以使用`for-in`来遍历字符串中的`Character`: + +```Java +for character in "Hello" { + println(character) +} +// H +// e +// l +// l +// o + +``` +## For-Condition-Increment + +In addition to for-in loops, Swift supports traditional C-style for loops with a condition and an incrementer: + +除了使用`for-in`这种循环语句,Swift还提供了传统的C风格的`for`循环语句,通常它需要一个循环执行条件和一个累加器: + +```Java +for var index = 0; index < 3; ++index { + println("index is \(index)") +} +// index is 0 +// index is 1 +// index is 2 +``` + +Here’s the general form of this loop format: + +下面是这种语法通用的语句: + +for `initialization`;`condition`;`increment`{ +`statements` +} + +Semicolons separate the three parts of the loop’s definition, as in C. However, unlike C, Swift doesn’t need parentheses around the entire “initialization; condition; increment” block. + +和C语言一样,循环语句被分号分割为3部分。但是和C语言不同的是,Swift不需要使用括号将"initialization;condition;increment"包围起来。 + +The loop is executed as follows: + +1,When the loop is first entered, the initialization expression is evaluated once, to set up any constants or variables that are needed for the loop. +2,The condition expression is evaluated. If it evaluates to false, the loop ends, and code execution continues after the for loop’s closing brace (}). If the expression evaluates to true, code execution continues by executing the statements inside the braces. +3,After all statements are executed, the increment expression is evaluated. It might increase or decrease the value of a counter, or set one of the initialized variables to a new value based on the outcome of the statements. After the increment expression has been evaluated, execution returns to step 2, and the condition expression is evaluated again. + +循环执行顺序如下: + +1,循环开始时执行一次initialization语句,初始化循环使用的常量或变量。 + +2,condition语句被执行,如果结果为`false`循环结束,执行循环体后面的代码。如果condition语句结果为`true`,继续执行循环体内的代码 + +3,当循环体内的代码被执行完时,increment语句被执行。通常情况是增加或减少计数器的值,然后执行第2步,condition语句会被再次执行。 + +The loop format and execution process described above is shorthand for (and equivalent to) the outline below: + +上面的for循环结构也可以如下表示: + +`initialization` +while `condition` { +`statements` +`increment` +} + +Constants and variables declared within the initialization expression (such as var index = 0) are only valid within the scope of the for loop itself. To retrieve the final value of index after the loop ends, you must declare index before the loop’s scope begins: + +在for循环初始化时创建的变量或者常量只在循环体内有效,如果想获取循环结束时的`index`值,你必须将`index`显式的声明到循环语句前: + +```Java +var index: Int +for index = 0; index < 3; ++index { + println("index is \(index)") +} +// index is 0 +// index is 1 +// index is 2 +println("The loop statements were executed \(index) times") +// prints "The loop statements were executed 3 times” +``` +Note that the final value of index after this loop is completed is 3, not 2. The last time the increment statement ++index is called, it sets index to 3, which causes index < 3 to equate to false, ending the loop. + +注意循环结束时,`index`值是`3`而不是`2`。当`++index`被最后一次执行时,`index`被赋值为`3`,这回导致`index<3`返回`false`,结束循环。 + +##While循环 + +A while loop performs a set of statements until a condition becomes false. These kinds of loops are best used when the number of iterations is not known before the first iteration “begins. Swift provides two kinds of while loop: + +while evaluates its condition at the start of each pass through the loop. +do-while evaluates its condition at the end of each pass through the loop. + +一个`while`语句会循环执行一段代码直到条件变为`false`。这种循环语句在处理循环次数不确定的情况时非常有效。Swift提供两种`while`循环: + +* `while`语句会在执行循环提前判断执行条件。 +* `do-while`语句会在执行完循环体内代码后判断执行条件。 + +###while + +A while loop starts by evaluating a single condition. If the condition is true, a set of statements is repeated until the condition becomes false. + +Here’s the general form of a while loop: + +一个`while`循环从判断执行条件开始。如果条件为`true`,则循环执行循环体内的代码,知道条件变为`false`。下面是通用的`while`语法格式: + +```Java +while condition { + statements +} +``` + +This example plays a simple game of Snakes and Ladders (also known as Chutes and Ladders): + +下面是一个简单的Snakes and Ladders游戏(也叫Chutes and Ladders) + +![](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/snakesAndLadders_2x.png) + +The rules of the game are as follows: + +The board has 25 squares, and the aim is to land on or beyond square 25. +Each turn, you roll a six-sided dice and move by that number of squares, following the horizontal path indicated by the dotted arrow above. +If your turn ends at the bottom of a ladder, you move up that ladder. +If your turn ends at the head of a snake, you move down that snake. + +游戏规则如下: + +* 板子上有25个方块,你的目标是到达或超过第25个方格。 +* 每次你需要通过掷骰子来决定前进几个放格,前进的方向由上图虚线的箭头所示。 +* 如果在你的回合结束时,你恰好到了梯子的尾部,你可以沿着梯子爬上去。 +* 如果在你的回合结束时,你恰好到了蛇的头部,则你会沿着蛇身滑下去。 + +The game board is represented by an array of Int values. Its size is based on a constant called finalSquare, which is used to initialize the array and also to check for a win condition later in the example. The board is initialized with 26 zero Int values, not 25 (one each at indices 0 through 25 inclusive): + +游戏的板子可以看做是一个`Int`型的数组。大小由常量`finalSquare`决定,这个常量也会用来初始化数组和判断获胜条件。数组被初始化为26个0,注意不是25(数组范围从0到25): + +```Java +let finalSquare = 25 +var board = Int[](count: finalSquare + 1, repeatedValue: 0)” +``` + +Some squares are then set to have more specific values for the snakes and ladders. Squares with a ladder base have a positive number to move you up the board, whereas squares with a snake head have a negative number to move you back down the board: + +板子上的许多方格用来被指定为梯子或者蛇。如果是梯子,那么这个方格的值为正数,如果是蛇,则方格的值为负数: + +```Java +board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 +board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 + +``` +Square 3 contains the bottom of a ladder that moves you up to square 11. To represent this, board[03] is equal to +08, which is equivalent to an integer value of 8 (the difference between 3 and 11). The unary plus operator (+i) balances with the unary minus operator (-i), and numbers lower than 10 are padded with zeros so that all board definitions align. (Neither stylistic tweak is strictly necessary, but they lead to neater code.) + +第三个方格被认定为是梯子的底部,它会让你走到第11个方格。为了表达这个意思,`board[03]`被复制为`+08`,它等价于整数8,考虑到所有的数字均为2位数,为了对齐和美观,(`+i`)用来和(`-i`)保持一致。 + +The player’s starting square is “square zero”, which is just off the bottom left corner of the board. The first dice roll always moves the player on to the board: + +玩家从板子左下角的第0号方块开始,第一次掷骰子会将玩家移动到: + +```Java +var square = 0 +var diceRoll = 0 +while square < finalSquare { + // roll the dice + if ++diceRoll == 7 { diceRoll = 1 } + // move by the rolled amount + square += diceRoll + if square < board.count { + // if we're still on the board, move up or down for a snake or a ladder + square += board[square] + } +} +println("Game over!") +``` + +This example uses a very simple approach to dice rolling. Instead of a random number generator, it starts with a diceRoll value of 0. Each time through the while loop, diceRoll is incremented with the prefix increment operator (++i), and is then checked to see if it has become too large. The return value of ++diceRoll is equal to the value of diceRoll after it is incremented. Whenever this return value equals 7, the dice roll has become too large, and is reset to a value of 1. This gives a sequence of diceRoll values that is always 1, 2, 3, 4, 5, 6, 1, 2 and so on. + +这个例子用了一个非常简单的方式来掷骰子。我们没有采用每次产生一个随机数的方法,取而代之的是我们先给`diceRoll`赋值为0,然后在每次while循环的过程中对`diceRoll`加1,然后检测它的值是否等于7,让它等于7的时候,再将它重置为1。这样,产生的一系列`diceRoll`的值为1,2,3,4,5,6,1,2...。 + +After rolling the dice, the player moves forward by diceRoll squares. It’s possible that the dice roll may have moved the player beyond square 25, in which case the game is over. To cope with this scenario, the code checks that square is less than the board array’s count property before adding the value stored in board[square] onto the current square value to move the player up or down any ladders or snakes. + +在掷完骰子后,玩家前进`diceRoll`个方格。玩家在前进的过程中很可能超过25,这种情况下游戏会结束。为了解决这种情况,当`square`在累加`board[quare]`的值前,会检查`square`的值是否小于`board.count`的值,同样它也会规避由数组越界带来的错误。 + +Had this check not been performed, board[square] might try to access a value outside the bounds of the board array, which would trigger an error. If square is now equal to 26, the code would try to check the value of board[26], which is larger than the size of the array. + +由于做了这种校验,当board[square]发生数组越界的情况时,会引发错误。例如,如果当前方格的值为26,代码会试图访问board[26],显然这个访问会引发数组越界。 + +The current while loop execution then ends, and the loop’s condition is checked to see if the loop should be executed again. If the player has moved on or beyond square number 25, the loop’s condition evaluates to false, and the game ends. + +然后`while`循环体结束,这时会再次判断循环执行条件,如果玩家已经前进到了25或者越过了25,那么循环条件会返回`false`,游戏结束。 + +A while loop is appropriate in this case because the length of the game is not clear at the start of the while loop. Instead, the loop is executed until a particular condition is satisfied + +显然在上面这情况下,使用`while`循环是非常合适的,因为循环次数不确定,只能根据循环条件来判断是否要终止循环。 + +###Do-While + +The other variation of the while loop, known as the do-while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false. + +Here’s the general form of a do-while loop: +另一个`while`循环的变种是`do-while`语句,它首先执行一次循环,然后在判断循环执行条件。如此反复,直到循环条件返回`false`。 + +下面是`do-while`语句的格式: + +```Java +do { + statements +} while condition + +``` +Here’s the Snakes and Ladders example again, written as a do-while loop rather than a while loop. The values of finalSquare, board, square, and diceRoll are initialized in exactly the same way as with a while loop: + +还是上面的蛇和梯子的例子,我们来使用`do-while`实现,初始化变量的过程同`while`循环: + +```Java +let finalSquare = 25 +var board = Int[](count: finalSquare + 1, repeatedValue: 0) +board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 +board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 +var square = 0 +var diceRoll = 0 + +``` + +In this version of the game, the first action in the loop is to check for a ladder or a snake. No ladder on the board takes the player straight to square 25, and so it is not possible to win the game by moving up a ladder. Therefore, it is safe to check for a snake or a ladder as the first action in the loop. + +At the start of the game, the player is on “square zero”. board[0] always equals 0, and has no effect: + +这次,我们首先来判断第一次遇到的是梯子还是蛇。因为没有梯子会把玩家直接送到25,所以这个判断是安全的。开始时,玩家的位置在"0",`board[0]`的值永远为0: + +```Java +do { + // move up or down for a snake or ladder + square += board[square] + // roll the dice + if ++diceRoll == 7 { diceRoll = 1 } + // move by the rolled amount + square += diceRoll +} while square < finalSquare +println("Game over!") +``` +After the code checks for snakes and ladders, the dice is rolled, and the player is moved forward by diceRoll squares. The current loop execution then ends. + +在判断遇到的时梯子还是蛇之后,我们掷骰子,玩家前进`diceRoll`个格子。当前循环结束。 + +The loop’s condition (while square < finalSquare) is the same as before, but this time it is not evaluated until the end of the first run through the loop. The structure of the do-while loop is better suited to this game than the while loop in the previous example. In the do-while loop above, square += board[square] is always executed immediately after the loop’s while condition confirms that square is still on the board. This behavior removes the need for the array bounds check seen in the earlier version of the game. + +这里的循环条件(`while square < finalSquare`)和前面相同,但它会在循环结束后被执行。`do-while`循环结构比前面的`while`更适合这个游戏。使用`do-while`时,`square+=board[square]`会立刻被执行,它省去了上个例子中对数组越界的判断。 + +##条件语句 + +It is often useful to execute different pieces of code based on certain conditions. You might want to run an extra piece of code when an error occurs, or to display a message when a value becomes too high or too low. To do this, you make parts of your code conditional. + +通常情况下我们需要在满足某种条件时执行一段代码或者在出错时执行另一段代码,也可能需要在某个值过大或过小时打印输出一些提示信息。这都需要你在代码中加入判断条件。 + +Swift provides two ways to add conditional branches to your code, known as the if statement and the switch statement. Typically, you use the if statement to evaluate simple conditions with only a few possible outcomes. The switch statement is better suited to more complex conditions with multiple possible permutations, and is useful in situations where pattern-matching can help select an appropriate code branch to execute. + +Swift提供了两种条件判断的语句,它们是`if`和`switch`。通常情况下,在条件分支比较少时,你使用`if`语句,`switch`语句用来处理复杂的判断条件和过多的条件分支,此外`switch`在模式匹配上非常有用。 + +###If + +In its simplest form, the if statement has a single if condition. It executes a set of statements only if that condition is true: + +`If`语句很简单,当判断条件为`true`时,执行一段代码: + +```Java +var temperatureInFahrenheit = 30 +if temperatureInFahrenheit <= 32 { + println("It's very cold. Consider wearing a scarf.") +} +// prints "It's very cold. Consider wearing a scarf. +``` + +The preceding example checks whether the temperature is less than or equal to 32 degrees Fahrenheit (the freezing point of water). If it is, a message is printed. Otherwise, no message is printed, and code execution continues after the if statement’s closing brace. + +上面的例子会判断温度是否小于等于32华氏度。如果是,打印一条消息,否则,则没有信息输出,执行if代码段后面的代码。 + +The if statement can provide an alternative set of statements, known as an else clause, for when the if condition is false. These statements are indicated by the else keyword: + +`if`语句通常和`else`搭配使用,当`if`语句返回`false`时,`else`分支的代码会被执行: + +```Java +temperatureInFahrenheit = 40 +if temperatureInFahrenheit <= 32 { + println("It's very cold. Consider wearing a scarf.") +} else { + println("It's not that cold. Wear a t-shirt.") +} +// prints "It's not that cold. Wear a t-shirt. + +``` +One of these two branches is always executed. Because the temperature has increased to 40 degrees Fahrenheit, it is no longer cold enough to advise wearing a scarf, and so the else branch is triggered instead. + +这种情况下`if`和`else`两个分支中的一个一定会被执行到。因为温度已经升高到40华氏度,不需要再建议带围巾了,因此else语句的代码段被执行。 + +You can chain multiple if statements together, to consider additional clauses: + +你也可以将多个`if`语句连起来使用: + +```Java +temperatureInFahrenheit = 90 +if temperatureInFahrenheit <= 32 { + println("It's very cold. Consider wearing a scarf.") +} else if temperatureInFahrenheit >= 86 { + println("It's really warm. Don't forget to wear sunscreen.") +} else { + println("It's not that cold. Wear a t-shirt.") +} +// prints "It's really warm. Don't forget to wear sunscreen. +``` + +Here, an additional if statement is added to respond to particularly warm temperatures. The final else clause remains, and prints a response for any temperatures that are neither too warm nor too cold. + +这里,多了一个`if`语句用来判断特定的温度范围。仍然有一个`else`在最后面,当温度既不高又不低得情况下打印出相应的信息: + +最后的`else`语句是可选的,如果它所在的分支不不需要执行,则可以将它排除: + +```Java +temperatureInFahrenheit = 72 +if temperatureInFahrenheit <= 32 { + println("It's very cold. Consider wearing a scarf.") +} else if temperatureInFahrenheit >= 86 { + println("It's really warm. Don't forget to wear sunscreen.") +} +``` +In this example, the temperature is neither too cold nor too warm to trigger the if or else if conditions, and so no message is printed. + +在这个例子中,温度不满足`if`和`else if`的执行条件,因此没有信息会被打印出来。 + +###Switch + +A switch statement considers a value and compares it against several possible matching patterns. It then executes an appropriate block of code, based on the first pattern that matches successfully. A switch statement provides an alternative to the if statement for responding to multiple potential states. + +一条`switch`语句会将一个值和多种模式进行匹配。匹配成功则执行该条件所对应的代码。`switch`是一种可以替换`if`进行条件判断的语句。 + +In its simplest form, a switch statement compares a value against one or more values of the same type: + +最简单的`switch`的语法是用来比较一个或多个类型相同的值: + +```Java +switch some value to consider { +case value 1: + respond to value 1 +case value 2, +value 3: + respond to value 2 or 3 +default: + otherwise, do something else +} +``` + +Every switch statement consists of multiple possible cases, each of which begins with the case keyword. In addition to comparing against specific values, Swift provides several ways for each case to specify more complex matching patterns. These options are described later in this section. + +每个`switch`语句都对应多个`case`语句。除了可以用`case`语句来比较具体的值以外,Swift提供了许多复杂的模式匹配,下一小节会详细介绍。 + +The body of each switch case is a separate branch of code execution, in a similar manner to the branches of an if statement. The switch statement determines which branch should be selected. This is known as switching on the value that is being considered. + +`switch`语句内包含多个代码执行分支,这点和`if`语句很像。`switch`语句决定了那个分支会被执行,通常是根据变量值来决定的。 + +Every switch statement must be exhaustive. That is, every possible value of the type being considered must be matched by one of the switch cases. If it is not appropriate to provide a switch case for every possible value, you can define a default catch-all case to cover any values that are not addressed explicitly. This catch-all case is indicated by the keyword default, and must always appear last. + + +每个`switch`语句必须是完整的,意思是它提供的分支条件必须能涵盖所有情况,你可以定义一个默认的分支条件用来处理一些意外的条件。这种分支通常用关键字`default`表示,并且它必须是最后一个分支条件。 + +This example uses a switch statement to consider a single lowercase character called someCharacter: + +这个例子使用`switch`语句类匹配一个小写字母变量`someCharacter`: + +```Java +let someCharacter: Character = "e" +switch someCharacter { +case "a", "e", "i", "o", "u": + println("\(someCharacter) is a vowel") +case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", +"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": + println("\(someCharacter) is a consonant") +default: + println("\(someCharacter) is not a vowel or a consonant") +} +// prints "e is a vowel” +``` +The switch statement’s first case matches all five lowercase vowels in the English language. Similarly, its second case matches all lowercase English consonants. + +`switch`语句的第一个`case`分支是匹配五个元音字母。同理,第二个`case`分支用来匹配所有的字符常量。 + +It is not practical to write all other possible characters as part of a switch case, and so this switch statement provides a default case to match all other characters that are not vowels or consonants. This provision ensures that the switch statement is exhaustive. + +通常来说列举出所有的字符不太现实,因此`switch`语句提供了`default`分支用来匹配所有其它的字符,这确保了`switch`语句的完整性。 + + +###没有隐式贯穿 + +In contrast with switch statements in C and Objective-C, switch statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicit break statement. This makes the switch statement safer and easier to use than in C, and avoids executing more than one switch case by mistake. + +和C语言或Objective-C相比,Swift中的`switch`语句在执行完一条`case`分支的代码后,则认为`switch`语句执行完毕。在某个`case`分支没有`break`的情况下,不会继续执行下一条`case`分支的代码。这比C语言更简单和安全。 + +>注意: +>你也可以在`case`分支代码执行前使用`break`语句来跳出这个分支,详见后面的[switch语句中break一节](link)。 + +The body of each case must contain at least one executable statement. It is not valid to write the following code, because the first case is empty: + +每条`case`分支的函数体必须至少包含一行可执行代码。下面的代码是不合法的,因为第一条case分支的函数体为空: + +```Java +let anotherCharacter: Character = "a" +switch anotherCharacter { +case "a": +case "A": + println("The letter A") +default: + println("Not the letter A") +} +// this will report a compile-time error +``` + +Unlike a switch statement in C, this switch statement does not match both "a" and "A". Rather, it reports a compile-time error that case "a": does not contain any executable statements. This approach avoids accidental fallthrough from one case to another, and makes for safer code that is clearer in its intent. + +和C语言不同,`switch`语句不会既匹配`"a"`又匹配`"A"`。遇到这种情况,编译器会给出错误提示。这条规则会避免意外的执行了其它分支的代码。 + +Multiple matches for a single switch case can be separated by commas, and can be written over multiple lines if the list is long: + +一条`case`分支也可以匹配多个判断条件,用逗号隔开: + +```Java +switch some value to consider { +case value 1, +value 2: + statements +} +``` +>注意 +>对于`switch`语句的某个case分支,你可以通过使用`fallthrough`关键字来强制贯穿改`case`分支。 +>更对细节在[Fallthrough](link)一节有所介绍 + +###区间匹配 + +Values in switch cases can be checked for their inclusion in a range. This example uses number ranges to provide a natural-language count for numbers of any size: + +`switch`语句的`case`分支可以是一个值区间同样支持范围匹配。下面这个例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式: + +```Java +let count = 3_000_000_000_000 +let countedThings = "stars in the Milky Way" +var naturalCount: String +switch count { +case 0: + naturalCount = "no" +case 1...3: + naturalCount = "a few" +case 4...9: + naturalCount = "several" +case 10...99: + naturalCount = "tens of" +case 100...999: + naturalCount = "hundreds of" +case 1000...999_999: + naturalCount = "thousands of" +default: + naturalCount = "millions and millions of" +} +println("There are \(naturalCount) \(countedThings).") +// prints "There are millions and millions of stars in the Milky Way. +``` + +###元组 + +You can use tuples to test multiple values in the same switch statement. Each element of the tuple can be tested against a different value or range of values. Alternatively, use the underscore (_) identifier to match any possible value. + +你可以使用元组在同一个`switch`语句中测试多个值。元组中的每个元素可以是一个区间,也可以是不同的值。我们使用(_)来匹配任何可能的值。 + +The example below takes an (x, y) point, expressed as a simple tuple of type (Int, Int), and categorizes it on the graph that follows the example: + +下面的例子使用一个元组类型(`Int`,`Int`)的点`(x,y)`并用图标来描述它的分布: + +```Java +let somePoint = (1, 1) +switch somePoint { +case (0, 0): + println("(0, 0) is at the origin") +case (_, 0): + println("(\(somePoint.0), 0) is on the x-axis") +case (0, _): + println("(0, \(somePoint.1)) is on the y-axis") +case (-2...2, -2...2): + println("(\(somePoint.0), \(somePoint.1)) is inside the box") +default: + println("(\(somePoint.0), \(somePoint.1)) is outside of the box") +} +// prints "(1, 1) is inside the box + +``` + +![](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/coordinateGraphSimple_2x.png) + +The switch statement determines if the point is at the origin (0, 0); on the red x-axis; on the orange y-axis; inside the blue 4-by-4 box centered on the origin; or outside of the box. + +`switch`语句用来判断这个点是否在原点;红色的X轴上;橘黄色的Y轴上;在蓝色的4x4的盒子内,还是在盒子外面。 + +Unlike C, Swift allows multiple switch cases to consider the same value or values. In fact, the point (0, 0) could match all four of the cases in this example. However, if multiple matches are possible, the first matching case is always used. The point (0, 0) would match case (0, 0) first, and so all other matching cases would be ignored. + +不像C语言,Swift支持不同的`case`分支匹配同一个结果,在这个例子中,点(0,0)满足所有4个分之条件。这种情况下只有第一条`case`语句被执行。点(0,0)会首先匹配第一条`case(0,0)`,后面的`case`语句会被忽略掉。 + +###值绑定(Value Bindings) + +A switch case can bind the value or values it matches to temporary constants or variables, for use in the body of the case. This is known as value binding, because the values are “bound” to temporary constants or variables within the case’s body. + +`switch`语句允许在一个`case`分支中,将待匹配的值绑定到一些常量或临时变量上。这样就可以在case分支的代码段中使用这些变量或常量,这就是值绑定。 + +The example below takes an `(x, y)` point, expressed as a tuple of type` (Int, Int) `and categorizes it on the graph that follows: + +下面的例子将定义一个`(Int,Int)`型的元组变量`(x,y)`,并归类它在图中的位置: + +```Java +let anotherPoint = (2, 0) +switch anotherPoint { +case (let x, 0): + println("on the x-axis with an x value of \(x)") +case (0, let y): + println("on the y-axis with a y value of \(y)") +case let (x, y): + println("somewhere else at (\(x), \(y))") +} +// prints "on the x-axis with an x value of 2 + +``` +![](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/coordinateGraphMedium_2x.png) + +The switch statement determines if the point is on the red x-axis, on the orange y-axis, or elsewhere, on neither axis. + +上面我们通过`switch`语句判断了`anotherPoint`这个点是否在红色的X,y轴上或者在非轴上的任意位置。 + +The three switch cases declare placeholder constants x and y, which temporarily take on one or both tuple values from anotherPoint. The first case, case (let x, 0), matches any point with a y value of 0 and assigns the point’s x value to the temporary constant x. Similarly, the second case, case (0, let y), matches any point with an x value of 0 and assigns the point’s y value to the temporary constant y. + +上面三条`case`语句使用临时变量`x`,`y`来匹配`anotherPoint`中的值。第一条`case(let x, 0)`语句用来匹配所有`y`值为0的点,并把该点的`x`值赋值给常量`x`。同理,`case(0,let y)`匹配所有`x`值为0的点,并把该点得```y```值赋给常量`y`。 + +Once the temporary constants are declared, they can be used within the case’s code block. Here, they are used as shorthand for printing the values with the println function. + +临时变量被声明后,它就可以用在`case`分支的代码中。本例中`x`,`y`在`println`方法中被打印出来。 + +Note that this switch statement does not have a default case. The final case, case let (x, y), declares a tuple of two placeholder constants that can match any value. As a result, it matches all possible remaining values, and a default case is not needed to make the switch statement exhaustive. + +注意到上面的`switch`语句并没有`default`分支,原因是最后一条`case let(x,y)`涵盖了所有其他情况,因此我们不需要再写一个`default`分支了。 + +In the example above, x and y are declared as constants with the let keyword, because there is no need to modify their values within the body of the case. However, they could have been declared as variables instead, with the var keyword. If this had been done, a temporary variable would have been created and initialized with the appropriate value. Any changes to that variable would only have an effect within the body of the case. + +在上面的例子中,我们通过`let`关键字将`x`,`y`声明为常量,原因是我们不需要修改他们的值。同样我们也可以用`var`关键字声明他们为变量,如果把它们声明为变量则需要为他们指定合适的初始值。任何对于它们值的修改也仅仅局限在`case`分支的代码中有效。 + +###Where + +A switch case can use a where clause to check for additional conditions. + +在`case`语句的条件分支中可以使用`where`关键字增加附加条件的判断。 + +The example below categorizes an (x, y) point on the following graph: + +还是上面的例子,用来归类`(x,y)`点所在图中得位置: + +```Java +let yetAnotherPoint = (1, -1) +switch yetAnotherPoint { +case let (x, y) where x == y: + println("(\(x), \(y)) is on the line x == y") +case let (x, y) where x == -y: + println("(\(x), \(y)) is on the line x == -y") +case let (x, y): + println("(\(x), \(y)) is just some arbitrary point") +} +// prints "(1, -1) is on the line x == -y +``` +![](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/coordinateGraphComplex_2x.png) + +The switch statement determines if the point is on the green diagonal line where x == y, on the purple diagonal line where x == -y, or neither. + +上面的`switch`语句用来检测`yetAnotherPoint`这个点是否在绿色(x==y)或紫色(x==-y)的对角线上。 + +The three switch cases declare placeholder constants x and y, which temporarily take on the two tuple values from point. These constants are used as part of a where clause, to create a dynamic filter. The switch case matches the current value of point only if the where clause’s condition evaluates to true for that value. + +上面的三条`switch`语句声明了常量`x`和`y`用来临时保存`yetAnotherPoint`中的坐标值。这两个常量同时也被`where`用来创建一个过滤条件。只有当`where`的过滤条件返回`true`时,`case`分支的代码才会被执行。 + +As in the previous example, the final case matches all possible remaining values, and so a default case is not needed to make the switch statement exhaustive. + +和上面的例子一样`default`分支在这个例子中也可以被忽略。 + +###控转移语句 + +Control transfer statements change the order in which your code is executed, by transferring control from one piece of code to another. Swift has four control transfer statements: + +控制转移语句会改变代码执行的顺序。Swift提供了4个控制转移语句: + +* continue +* break +* fallthrough +* return + +The control, break and fallthrough statements are described below. The return statement is described in Functions. + +我们下面将会讨论`control`,`break`,`return`语句,`return`将会在[函数](link)一章讨论。 + + + +###continue + +The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop. It says “I am done with the current loop iteration” without leaving the loop altogether. + +`continue`用来终止当前循环并开始执行下一次循环。它会说“本次循环结束了”,但并不会跳出循环。 + +>注意: +>在`for-condition-increment`一节,即使执行了`continue`,循环的累加器仍会自动加1。 +>循环会继续,仅仅是循环体内的代码不会被执行。 + +The following example removes all vowels and spaces from a lowercase string to create a cryptic puzzle phrase: + +下面的例子会移除一个小写字符串中所有的原因字母: + +```Java +let puzzleInput = "great minds think alike" +var puzzleOutput = "" +for character in puzzleInput { + switch character { + case "a", "e", "i", "o", "u", " ": + continue + default: + puzzleOutput += character + } +} +println(puzzleOutput) +// prints "grtmndsthnklk +``` +The code above calls the continue keyword whenever it matches a vowel or a space, causing the current iteration of the loop to end immediately and to jump straight to the start of the next iteration. This behavior enables the switch block to match (and ignore) only the vowel and space characters, rather than requiring the block to match every character that should get printed. + +在上面的代码中,一旦出现元音字母,当前循环就会被终止,并重新开始下一次循环。这会让`switch`语句去匹配元音字符或空字符时不做处理,而不是让每一个匹配到的字符都被打印出来。 + +###break + +The break statement ends execution of an entire control flow statement immediately. The break statement can be used inside a switch statement or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case. + +`break`语句会立刻终止当前执行的整个控制流。`break`可被用在`switch`语句里面或循环中用来提前结束控制流。 + +###循环体内的break + +When used inside a loop statement, break ends the loop’s execution immediately, and transfers control to the first line of code after the loop’s closing brace (}). No further code from the current iteration of the loop is executed, and no further iterations of the loop are started. + +当在循环体内执行`break`时,会退出整个循环,代码从循环体后面的第一行开始执行,break语句后面的代码将不会执行,下一次循环也不会开始。 + +###Switch语句中得break + +When used inside a switch statement, break causes the switch statement to end its execution immediately, and to transfer control to the first line of code after the switch statement’s closing brace (}). + +当在`switch`中使用`break`时,会立即终止改`switch`代码块的执行,并且跳转到`switch`代码块结束的大括号(`}`)后的第一行代码。 + +This behavior can be used to match and ignore one or more cases in a switch statement. Because Swift’s switch statement is exhaustive and does not allow empty cases, it is sometimes necessary to deliberately match and ignore a case in order to make your intentions explicit. You do this by writing the break statement as the entire body of the case you want to ignore. When that case is matched by the switch statement, the break statement inside the case ends the switch + +这种行为可被用来忽略或跳过`switch`中的某个`case`。由于Swift要求`switch`的分支条件必须完整并且不允许空的`case`分支,因此在有些情况下使用`break`特意跳出某个分支会很必要。你可以通过在`case`的代码快中加入`break`来达到这个效果。当`case`分支的代码块被执行时,`break`会终止`switch`语句的执行。 + +>注意: +>当`switch`的一个分支仅仅包含注释时,编译器会给出错误提示。 +>注释并不是真正的代码,他不能达到忽略这个`case`分支的作用。有哪次还是需要使用`break`。 + +The following example switches on a Character value and determines whether it represents a number symbol in one of four languages. Multiple values are covered in a single switch case for brevity: + +下面这个例子用来判断一个字符的值是否是数字符号。为了简便,多个值被包含到一个分支中: + +```Java +let numberSymbol: Character = "三" // Simplified Chinese for the number 3 +var possibleIntegerValue: Int? +switch numberSymbol { +case "1", "١", "一", "๑": + possibleIntegerValue = 1 +case "2", "٢", "二", "๒": + possibleIntegerValue = 2 +case "3", "٣", "三", "๓": + possibleIntegerValue = 3 +case "4", "٤", "四", "๔": + possibleIntegerValue = 4 +default: + break +} +if let integerValue = possibleIntegerValue { + println("The integer value of \(numberSymbol) is \(integerValue).") +} else { + println("An integer value could not be found for \(numberSymbol).") +} +// prints "The integer value of 三 is 3." + +``` + +This example checks numberSymbol to determine whether it is a Latin, Arabic, Chinese, or Thai symbol for the numbers 1 to 4. If a match is found, one of the switch statement’s cases sets an optional Int? variable called possibleIntegerValue to an appropriate integer value. + +这个例子用来判断`numberSymbol`是否是拉丁文,阿拉伯文,中文或泰语中的1到4之一。如果被匹配到,该`switch`分支语句给`Int?`类型变量`possibleIntegerValue`设置一个整数值。 + +After the switch statement completes its execution, the example uses optional binding to determine whether a value was found. The possibleIntegerValue variable has an implicit initial value of nil by virtue of being an optional type, and so the optional binding will succeed only if possibleIntegerValue was set to an actual value by one of the switch statement’s first four cases. + +当switch代码块执行完后,接下来的代码先判断`possibleIntegerValue`是否被绑定成功。因为是可选类型的缘故,`possibleIntegerValue`有一个隐式的初始值nil,所以仅仅当`possibleIntegerValue`曾被switch代码块的前四个分支中的某个设置过一个值时,可选的绑定将会被判定为成功。 + +It is not practical to list every possible Character value in the example above, so a default case provides a catchall for any characters that are not matched. This default case does not need to perform any action, and so it is written with a single break statement as its body. As soon as the default statement is matched, the break statement ends the switch statement’s execution, and code execution continues from the if let statement. + +在上面的例子中,想要把`Character`所有的的可能性都枚举出来是不现实的,所以使用`default`分支来包含所有上面没有匹配到字符的情况。由于这个`default`分支不需要执行任何动作,所以它只写了一条`break`语句。一旦落入到`default`分支中后,`break`语句就完成了该分支的所有代码操作,代码继续向下,开始执行`if let`语句。 + +###Fallthrough + +Switch statements in Swift do not fall through the bottom of each case and into the next one. Instead, the entire switch statement completes its execution as soon as the first matching case is completed. By contrast, C requires you to insert an explicit break statement at the end of every switch case to prevent fallthrough. Avoiding default fallthrough means that Swift switch statements are much more concise and predictable than their counterparts in C, and thus they avoid executing multiple switch cases by mistake. + +Swift中的Switch语句不会从上到下进入每一个case分支。相反,一旦有一个case分支被匹配成功,整个state语句就结束执行了。相比之下,在C语言中,为了防止switch语句会贯穿执行每一个case分支,你需要在每个case分支的末尾插入`break`语句。和C语言相比,Swift支持这种避免贯穿行为会让`switch`语句更简洁和更安全也能规避错误执行多个case分支的情况。 + +If you really need C-style fallthrough behavior, you can opt in to this behavior on a case-by-case basis with the fallthrough keyword. The example below uses fallthrough to create a textual description of a number: + +如果你一定要使用C风格的贯穿(fallthrough)机制,你可以在每个需要支持该特性的case分支中使用`fallthrough`关键字。下面这个例子展示了如何使用`fallthrough`来实现对数字的文本描述: + +```Java +let integerToDescribe = 5 +var description = "The number \(integerToDescribe) is" +switch integerToDescribe { +case 2, 3, 5, 7, 11, 13, 17, 19: + description += " a prime number, and also" + fallthrough +default: + description += " an integer." +} +println(description) +// prints "The number 5 is a prime number, and also an integer. +``` + +This example declares a new String variable called description and assigns it an initial value. The function then considers the value of integerToDescribe using a switch statement. If the value of integerToDescribe is one of the prime numbers in the list, the function appends text to the end of description, to note that the number is prime. It then uses the fallthrough keyword to “fall into” the default case as well. The default case adds some extra text to the end of the description, and the switch statement is complete. + +这个例子中声明了一个叫`description`的`string`类型的变量,并且为其赋了初值。然后这个函数通过`switch`语句来判断`integerToDescribe`的值。如果`integerToDescribe`的值为数组中的一个素数,则该函数会为`decription`后面追加一段文本用来提示改数字式质数。然后会使用`fallthrough`关键字来执行`default`中的代码。在`default`分支中会为`description`后面继续追加一段文本。至此,`switch`语句才算执行完成。 + +If the value of integerToDescribe is not in the list of known prime numbers, it is not matched by the first switch case at all. There are no other specific cases, and so integerToDescribe is matched by the catchall default case. + +如果`integerToDescribe`的值不在这组质数当中,则它不会和第一条`case`语句匹配。由于没有其它的分支存在,所以`integerToDescribe`会落入`default`分支。 + +After the switch statement has finished executing, the number’s description is printed using the println function. In this example, the number 5 is correctly identified as a prime number. + +当`switch`语句执行完成后,关于这个数字的描述会通过`println`函数打印出来。在这个例子中,数字`5`被正确的识别为一个质数。 + +>注意: +>和C语言的`switch`语句一样,`fallthrough`不会检查它落入执行的`case`分支的条件是否匹配, +>它只是简单的执行下一条`cast(或`default`)中的代码。 + +###Labeled语句 + +You can nest loops and switch statements inside other loops and switch statements in Swift to create complex control flow structures. However, loops and switch statements can both use the break statement to end their execution prematurely. Therefore, it is sometimes useful to be explicit about which loop or switch statement you want a break statement to terminate. Similarly, if you have multiple nested loops, it can be useful to be explicit about which loop the continue statement should affect. + +在`swift`中,可以在循环或`switch`函数中嵌套循环或`switch`函数来实现比较复杂的控制流结构。但是,在循环或`switch`函数中可以使用`break`语句提前终止其执行过程。因此,有些时候显示的调用`break`来标识终止循环或`switch`是非常有好处的。类似的,如果一个循环中嵌套了多个循环,使用`continue`来标识其影响的循环体也是很有用的。 + +To achieve these aims, you can mark a loop statement or switch statement with a statement label, and use this label with the break statement or continue statement to end or continue the execution of the labeled statement. + +为了实现上面的目标,你可以通过标签来标识某个循环或`switch`语句。使用`break`或`continue`时,带上这个标签,这个标签可以用来结束或执行被标记的代码段。 + +A labeled statement is indicated by placing a label on the same line as the statement’s introducer keyword, followed by a colon. Here’s an example of this syntax for a while loop, although the principle is the same for all loops and switch statements + + +标签语句通常被放到一些关键字的前面,通过分号隔开。下面是一个通过标签来标记`while`语句的例子,循环或`switch`语句和它类似: + +```Java +label name: while condition { + statements +} + +``` + +The following example uses the break and continue statements with a labeled while loop for an adapted version of the Snakes and Ladders game that you saw earlier in this chapter. This time around, the game has an extra rule: + +下面这个例子将使用`break`和`continue`,配合带标签的`while`循环,该循环和前面的梯子和蛇的例子一直。这次,该游戏新增加了一条规则 + +* 为了胜利,你必须恰好到达第25个格子中 + +If a particular dice roll would take you beyond square 25, you must roll again until you roll the exact number needed to land on square 25. + +如果某一次掷骰子会把你带到超过25的地方,必须重新掷骰子,直到你恰好到达第25号格子的位子 + +The game board is the same as before:” +游戏的棋盘和前面的一样: + +![](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/snakesAndLadders_2x.png) + +The values of finalSquare, board, square, and diceRoll are initialized in the same way as before: + +`finalSquare`,`board`,`square`和`diceRoll`的值和之前初始化的值相同: + +```Java +let finalSquare = 25 +var board = Int[](count: finalSquare + 1, repeatedValue: 0) +board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 +board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 +var square = 0 +var diceRoll = 0 + +``` +This version of the game uses a while loop and a switch statement to implement the game’s logic. The while loop has a statement label called gameLoop, to indicate that it is the main game loop for the Snakes and Ladders game. + +这个版本会使用`while`循环和`switch`语句来实现游戏的逻辑。`while`循环有个标签叫做`gameLoop`,用来表示这个循环是这个游戏的主循环。 + +The while loop’s condition is while square != finalSquare, to reflect that you must land exactly on square 25: + +`while`循环的条件是`while square != finalSquare`用来表示你必须正好落在第25个格子中: + +```Java +gameLoop: while square != finalSquare { + if ++diceRoll == 7 { diceRoll = 1 } + switch square + diceRoll { + case finalSquare: + // diceRoll will move us to the final square, so the game is over + break gameLoop + case let newSquare where newSquare > finalSquare: + // diceRoll will move us beyond the final square, so roll again + continue gameLoop + default: + // this is a valid move, so find out its effect + square += diceRoll + square += board[square] + } +} +println("Game over!") + +``` +The dice is rolled at the start of each loop. Rather than moving the player immediately, a switch statement is used to consider the result of the move, and to work out if the move is allowed: + +在每个循环开始前都需要掷骰子,与之前立刻移动玩家不同,这次利用`switch`语句计算每次掷骰子产生的结果,从而决 +定玩家是否可以移动: + +If the dice roll will move the player onto the final square, the game is over. The break gameLoop statement transfers control to the first line of code outside of the while loop, which ends the game. + +* 如果掷骰子的结果恰好将玩家移动到最后的方格中,那么游戏结束。`break gameLoop`会将代码跳到循环后的第一条语句,继续执行后面的代码。 + +If the dice roll will move the player beyond the final square, the move is invalid, and the player needs to roll again. The continue gameLoop statement ends the current while loop iteration and begins the next iteration of the loop. + +* 如果掷骰子的结果将玩家移动到超过最后一个方格的位置,那么这次结果是无效的,玩家需要重新掷骰子。`continue gameLoop`会将当前循环终止并重新开始下一次循环。 + +In all other cases, the dice roll is a valid move. The player moves forward by diceRoll squares, and the game logic checks for any snakes and ladders. The loop then ends, and control returns to the while condition to decide whether another turn is required + +* 在所有其余的情况中,掷骰子的结果是有效的,玩家会前进`diceRoll`个格子,游戏的逻辑会检测是否遇到梯子或者蛇。循环结束时,代码将回到`while`条件判定检查是否要进行下一次循环。 + +NOTE + +If the break statement above did not use the gameLoop label, it would break out of the switch statement, not the while statement. Using the gameLoop label makes it clear which control statement should be terminated. + +Note also that it is not strictly necessary to use the gameLoop label when calling continue gameLoop to jump to the next iteration of the loop. There is only one loop in the game, and so there is no ambiguity as to which loop the continue statement will affect. However, there is no harm in using the gameLoop label with the continue statement. Doing so is consistent with the label’s use alongside the break statement, and helps make the game’s logic clearer to read and understand. + +>注意: + +>如果`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`。 +>使用`gameLoop`标签可以更直观的体现循环在哪里被终止的。 + +>我们还注意到,跳到下一次循环的语句:`continue gameLoop`并不一定要使用`gameLoop`标签。由于代码中只有一个循环,因此`continue`语句是没有歧义的,但是在这里使用`gameLoop`标签也是没有任何坏处的。在`break`旁边加上标签可以保证代码的一致性,是代码逻辑更清楚,更容易被人读懂和理解。 + + + + + diff --git a/src/chapter2/06_Functions.md b/src/chapter2/06_Functions.md index e69de29..d76f541 100644 --- a/src/chapter2/06_Functions.md +++ b/src/chapter2/06_Functions.md @@ -0,0 +1,781 @@ +# Functions 函数 + +Functions are self-contained chunks of code that perform a specific task. You give a function a name that identifies what it does, and this name is used to “call” the function to perform its task when needed. + +函数是执行特定任务的独立代码块。可以给函数起一个名字,标识它是做什么的,并在需要时使用这个名字来“调用”函数执行任务。 + +Swift’s unified function syntax is flexible enough to express anything from a simple C-style function with no parameter names to a complex Objective-C-style method with local and external parameter names for each parameter. Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution. + +Swift统一标准的函数语法十分灵活,可以表达任何函数。从简单的无参数名的C风格函数,到复杂的每个参数都带有局部和外部参数名的Objective-C风格函数。可以给参数提供默认值,以简化函数调用;也可以把参数当做in-out参数传值,函数执行结束时,可以改变传入变量的值。 + +Every function in Swift has a type, consisting of the function’s parameter types and return type. You can use this type like any other type in Swift, which makes it easy to pass functions as parameters to other functions, and to return functions from functions. Functions can also be written within other functions to encapsulate useful functionality within a nested function scope. + +在Swift中,每个函数都有类型,由函数参数类型和返回类型组成。函数类型可以像任何其他类型那样使用,可以把函数作为参数传递给其他函数,也可以从函数中返回函数,函数类型让这些变得简单。函数也可以定义在另一个函数中, 在嵌套函数范围内,封装特定功能。 + + +# Defining and Calling Functions 函数定义和调用 + +When you define a function, you can optionally define one or more named, typed values that the function takes as input (known as parameters), and/or a type of value that the function will pass back as output when it is done (known as its return type). + +定义函数时,你可以定义一个或多个具有名称、类型的值作为函数输入(称为参数parameters),和(或者)定义在函数执行完毕后传回的一个有类型的值作为输出(称为返回值)。 + +Every function has a function name, which describes the task that the function performs. To use a function, you “call” that function with its name and pass it input values (known as arguments) that match the types of the function’s parameters. A function’s arguments must always be provided in the same order as the function’s parameter list. + +每个函数都有一个函数名,用来描述此函数执行的任务。使用函数时,通过函数名“调用”函数,并传入符合函数参数类型的输入值(称为参数,arguments)。提供给函数的参数必须与函数参数列表的顺序一致。 + +The function in the example below is called greetingForPerson, because that’s what it does—it takes a person’s name as input and returns a greeting for that person. To accomplish this, you define one input parameter—a String value called personName—and a return type of String, which will contain a greeting for that person: + +下面例子中的函数叫做sayHello,名字描述了它的用途,用人名作为输入,并返回对这个人的问候(greeting)。为了实现这些,需要定义一个输入参数——一个名为personName的String类型值,和一个带有对此人问候的String类型的返回值。 + +```js +func sayHello(personName: String) -> String { + let greeting = "Hello, " + personName + "!" + return greeting +} +``` + +All of this information is rolled up into the function’s definition, which is prefixed with the func keyword. You indicate the function’s return type with the return arrow -> (a hyphen followed by a right angle bracket), which is followed by the name of the type to return. + +所有这些信息构成了一个函数的定义,并以关键字 func 作为前缀。使用返回箭头->(连字符后跟一个右尖括号)和紧接着的返回类型名来声明函数的返回类型。 + +The definition describes what the function does, what it expects to receive, and what it returns when it is done. The definition makes it easy for the function to be called elsewhere in your code in a clear and unambiguous way: + +函数定义描述了函数是做什么的,期望接收什么和执行结束后返回什么。这样在代码中的任何地方都可以清楚明确地调用函数,而这种函数定义使这些变得更简单。 + +```js +println(sayHello("Anna")) +// prints "Hello, Anna!" +println(sayHello("Brian")) +// prints "Hello, Brian!” +``` + +You call the sayHello function by passing it a String argument value in parentheses, such as sayHello("Anna"). Because the function returns a String value, sayHello can be wrapped in a call to the println function to print that string and see its return value, as shown above. + +调用sayHello函数时,在圆括号中传入一个String类型参数值,例如sayHello(“Anna”)。由于这个函数返回了一个String类型的值,因此sayHello可以包装在 println 函数中,用来输出这个字符串并查看它的返回值,如上所示。 + +The body of the sayHello function starts by defining a new String constant called greeting and setting it to a simple greeting message for personName. This greeting is then passed back out of the function using the return keyword. As soon as return greeting is called, the function finishes its execution and returns the current value of greeting. + +sayHello 的函数体先定义一个名为 greeting 的String 常量,并给它设置了对personName的简单问候信息。然后通过 return 关键字把 greeting 传出函数。一旦执行了 return greeting,函数就结束执行并返回greeting的当前值。 + +You can call the sayHello function multiple times with different input values. The example above shows what happens if it is called with an input value of "Anna", and an input value of "Brian". The function returns a tailored greeting in each case. + +你可以传入不同的输入值多次调用sayHello 函数。上述例子展示了输入值为“Anna”和“Brian”时调用函数的结果。函数在每种情况下返回了各自定制的greeting。 + +To simplify the body of this function, combine the message creation and the return statement into one line: + +为了简化函数体,可以把消息创建和返回语句合并为一行: + +```js +func sayHelloAgain(personName: String) -> String { + return "Hello again, " + personName + "!" +} +println(sayHelloAgain("Anna")) +// prints "Hello again, Anna!” +``` + +# Function Parameters and Return Values 函数的参数和返回值 + + +Function parameters and return values are extremely flexible in Swift. You can define anything from a simple utility function with a single unnamed parameter to a complex function with expressive parameter names and different parameter options. + +Swift 中的函数参数和返回值十分灵活。你可以定义各种类型的函数,从只有一个未命名参数的简单功能函数到 具有表达性参数名和不同参数选项的复杂函数。 + +## Multiple Input Parameters 多输入参数 + +Functions can have multiple input parameters, which are written within the function’s parentheses, separated by commas. + +函数可以有多个输入参数,写在函数的圆括号内,用逗号分隔。 + +This function takes a start and an end index for a half-open range, and works out how many elements the range contains: + +下面这个函数接收半开区间的开始和结束索引,并计算出这个区间范围包含多少个元素: + +```js +func halfOpenRangeLength(start: Int, end: Int) -> Int { + return end - start +} +println(halfOpenRangeLength(1, 10)) +// prints “9" +``` + +## Functions Without Parameters 无参数函数 + +Functions are not required to define input parameters. Here’s a function with no input parameters, which always returns the same String message whenever it is called: + +函数可以不定义输入参数。下面是一个无输入参数的函数,不管何时调用总是返回相同的字符串信息: + +```js +func sayHelloWorld() -> String { + return "hello, world" +} +println(sayHelloWorld()) +// prints "hello, world” +``` + +The function definition still needs parentheses after the function’s name, even though it does not take any parameters. The function name is also followed by an empty pair of parentheses when the function is called. + +虽然定义这种函数,并不需要任何参数,但在函数名后仍需要一对括号。所以调用函数时,函数名后总是紧跟一对空括号。 + +## Functions Without Return Values 无返回值的函数 + +Functions are not required to define a return type. Here’s a version of the sayHello function, called waveGoodbye, which prints its own String value rather than returning it: + +函数可以不定义返回类型。下面是sayHello 函数的另一个版本sayGoodbye,这个函数只输出String 值而不返回。 + +```js +func sayGoodbye(personName: String) { + println("Goodbye, \(personName)!") +} +sayGoodbye("Dave") +// prints "Goodbye, Dave!” +``` + +Because it does not need to return a value, the function’s definition does not include the return arrow (->) or a return type. + +由于这个函数不需要返回值,定义函数时不需要返回箭头(->)和返回类型。 + +> NOTE + +> 注意 + +> Strictly speaking, the sayGoodbye function does still return a value, even though no return value is defined. Functions without a defined return type return a special value of type Void. This is simply an empty tuple, in effect a tuple with zero elements, which can be written as (). + +> 严格来说,即使函数sayGoodbye没有定义返回值,它也会返回一个值。没有定义返回类型的函数会返回一个Void类型的特殊值。它仅仅是一个空元组(tuple),实际是零个元素的元组,可以写成()。 + +The return value of a function can be ignored when it is called: + +调用函数时,函数的返回值可以忽略。 + +```js +func printAndCount(stringToPrint: String) -> Int { + println(stringToPrint) + return countElements(stringToPrint) +} +func printWithoutCounting(stringToPrint: String) { + printAndCount(stringToPrint) +} +printAndCount("hello, world") +// prints "hello, world" and returns a value of 12 +printWithoutCounting("hello, world") +// prints "hello, world" but does not return a value +``` + +The first function, printAndCount, prints a string, and then returns its character count as an Int. The second function, printWithoutCounting, calls the first function, but ignores its return value. When the second function is called, the message is still printed by the first function, but the returned value is not used. + +第一个函数printAndCount 输出一个字符串并返回Int类型的字符个数。第二个函数 printWithoutCounting 调用第一个函数,但忽略它的返回值。调用第二个函数时,信息仍由第一个函数输出,但不使用它的返回值。 + +> NOTE + +> 注意 + +> Return values can be ignored, but a function that says it will return a value must always do so. A function with a defined return type cannot allow control to fall out of the bottom of the function without returning a value, and attempting to do so will result in a compile-time error. + +> 返回值虽然可以被忽略,但定义了返回类型的函数必须总是返回一个值。定义返回类型的函数不允许控制函数的最后没有返回值,试图这样做会导致编译时错误。 + +## Functions with Multiple Return Values 多返回值函数 + +You can use a tuple type as the return type for a function to return multiple values as part of one compound return value. + +可以使用元组类型作为函数的返回类型,这个函数可以返回多个值作为复合返回值的一部分。 + +The example below defines a function called count, which counts the number of vowels, consonants, and other characters in a string, based on the standard set of vowels and consonants used in American English: + +下面的例子定义了count的函数,它基于美式英语中使用的元音和辅音的标准集合,来计算字符串中的元音、辅音和其他字符的个数: + +```js +func count(string: String) -> (vowels: Int, consonants: Int, others: Int) { + var vowels = 0, consonants = 0, others = 0 + for character in string { + switch String(character).lowercaseString { + case "a", "e", "i", "o", "u": + ++vowels + case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", + "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": + ++consonants + default: + ++others + } + } + return (vowels, consonants, others) +} +``` + +You can use this count function to count the characters in an arbitrary string, and to retrieve the counted totals as a tuple of three named Int values: + +你可以使用这个 count 函数来计算任意字符串中的字符数,然后得到所有字符总数——含有3个整型值的元组(译者注:元音,辅音,其他)。 + +```js +let total = count("some arbitrary string!") +println("\(total.vowels) vowels and \(total.consonants) consonants") +// prints "6 vowels and 13 consonants” +``` + +Note that the tuple’s members do not need to be named at the point that the tuple is returned from the function, because their names are already specified as part of the function’s return type. + +注意当函数返回时,元组的成员不需要命名,因为它们的名字已经在函数返回类型中定义了,作为函数返回类型的一部分。 + +# Function Parameter Names 函数参数名 + +All of the above functions define parameter names for their parameters: + +以上所有的函数都给它们的参数定义了参数名: + +```js +func someFunction(parameterName: Int) { + // function body goes here, and can use parameterName + // to refer to the argument value for that parameter +} +``` + +However, these parameter names are only used within the body of the function itself, and cannot be used when calling the function. These kinds of parameter names are known as local parameter names, because they are only available for use within the function’s body. + +然而,这些参数名只能在这个函数体内部使用,不能在调用函数时使用。这种参数名被称为局部参数名,因为它们只能在函数体内部获得和使用。 + +## External Parameter Names 外部参数名 + +Sometimes it’s useful to name each parameter when you call a function, to indicate the purpose of each argument you pass to the function. + +有时,在调用函数时为每个参数命名十分有用,表明传给函数的每个参数的用途。 + +If you want users of your function to provide parameter names when they call your function, define an external parameter name for each parameter, in addition to the local parameter name. You write an external parameter name before the local parameter name it supports, separated by a space: + +如果希望用户在调用函数时提供参数名,除了定义局部参数名,还要给每个参数定义外部参数名。把外部参数名写在支持的局部参数名前,用空格分隔: + +```js +func someFunction(externalParameterName localParameterName: Int) { + // function body goes here, and can use localParameterName + // to refer to the argument value for that parameter +} +``` + +> NOTE 注意 + +> If you provide an external parameter name for a parameter, that external name must always be used when calling the function. + +> 如果给参数提供了外部参数名,那么在调用函数时必须总是带上外部参数名。 + +As an example, consider the following function, which joins two strings by inserting a third “joiner” string between them: + +例如下面的函数,把第三个名为“joiner”的字符串插入到另外两个字符串之间,进行字符串连接: + +```js +func join(s1: String, s2: String, joiner: String) -> String { + return s1 + joiner + s2 +} +``` + +When you call this function, the purpose of the three strings that you pass to the function is unclear: + +不过,在调用这个函数时,传给函数的三个字符串的用意并不明确: + +```js +join("hello", "world", ", ") +// returns "hello, world” +``` + +To make the purpose of these String values clearer, define external parameter names for each join function parameter: + +为了明确这些字符串值的用途,需要给 join 函数的每个参数定义外部参数名: + +``` +func join(string s1: String, toString s2: String, withJoiner joiner: String) + -> String { + return s1 + joiner + s2 +} +``` + +In this version of the join function, the first parameter has an external name of string and a local name of s1; the second parameter has an external name of toString and a local name of s2; and the third parameter has an external name of withJoiner and a local name of joiner. + +在这个版本的 join 函数中,第一个参数具有外部参数名 string 和 局部参数名 s1;第二个参数具有外部参数名 toString 和 局部参数名 s2;第三个参数具有外部参数名 withJoiner 和 局部参数名 joiner; + +You can now use these external parameter names to call the function in a clear and unambiguous way: + +现在你就可以明确清楚地使用这些外部参数名来调用函数: + +```js +join(string: "hello", toString: "world", withJoiner: ", ") +// returns "hello, world” +``` + +The use of external parameter names enables this second version of the join function to be called in an expressive, sentence-like manner by users of the function, while still providing a function body that is readable and clear in intent. + +利用外部参数使调用第二个版本的join函数更具表达性和语义性,同时也使函数体更可读、更清晰。 + +> NOTE 注意 + +> Consider using external parameter names whenever the purpose of a function’s arguments would be unclear to someone reading your code for the first time. You do not need to specify external parameter names if the purpose of each parameter is clear and unambiguous when the function is called. + +> 初次阅读这些代码的人,可能不清楚函数参数的用途,这时需要考虑使用外部参数名。如果调用函数时,每个参数的用意都很清楚、明确,那就不需要定义外部参数名。 + +## Shorthand External Parameter Names 简写外部参数名 + +If you want to provide an external parameter name for a function parameter, and the local parameter name is already an appropriate name to use, you do not need to write the same name twice for that parameter. Instead, write the name once, and prefix the name with a hash symbol (#). This tells Swift to use that name as both the local parameter name and the external parameter name. + +如果想给函数参数提供外部参数名,而局部参数名已经很适合了,那么你不需要给参数写两个相同的名字。而只用写一次,然后在名字前加上符号(#)。这就告诉Swift使用这个名字同时作为本地参数名和外部参数名。 + +This example defines a function called containsCharacter, which defines external parameter names for both of its parameters by placing a hash symbol before their local parameter names: + +下面的例子定义了containsCharacter的函数,这个函数在局部参数名前加上#标识从而给它的两个参数定义外部参数名。 + +```js +func containsCharacter(#string: String, #characterToFind: Character) -> Bool { + for character in string { + if character == characterToFind { + return true + } + } + return false +} +``` + +This function’s choice of parameter names makes for a clear, readable function body, while also enabling the function to be called without ambiguity: + +这个函数选择的参数名使函数体清晰、可读,同时在调用函数时也没有歧义: + +```js +let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v") +// containsAVee equals true, because "aardvark" contains a “v" +``` + +## Default Parameter Values 默认参数值 + +You can define a default value for any parameter as part of a function’s definition. If a default value is defined, you can omit that parameter when calling the function. + +在函数定义中,你可以给任何参数定义默认值。如果定义了默认值,在调用函数时可以忽略这个参数。 + +> NOTE 注意 + +> Place parameters with default values at the end of a function’s parameter list. This ensures that all calls to the function use the same order for their non-default arguments, and makes it clear that the same function is being called in each case. + +> 把带有默认值的参数放在函数参数列表的最后。这样可以确保,所有的函数调用使用的非默认参数顺序相同,同时可以明确每种情况都会调用相同的函数。 + +Here’s a version of the join function from earlier, which provides a default value for its joiner parameter: + +下面是之前版本的 join 函数,这个函数为它的joiner 参数提供了默认值: + +```js +func join(string s1: String, toString s2: String, + withJoiner joiner: String = " ") -> String { + return s1 + joiner + s2 +} +``` + +If a string value for joiner is provided when the join function is called, that string value is used to join the two strings together, as before: + +调用 join 函数时,如果给 joiner 提供了字符串类型的值,这个字符串值会像之前一样,连接 另外两个字符串: + +```js +join(string: "hello", toString: "world", withJoiner: "-") +// returns "hello-world” +``` + +However, if no value of joiner is provided when the function is called, the default value of a single space (" ") is used instead: + +然而,如果调用函数时没有提供 joiner 值,会使用默认值空格(“ ”)替代: + +```js +join(string: "hello", toString: "world") +// returns "hello world” +``` + +## External Names for Parameters with Default Values 带默认值参数的外部参数名 + +In most cases, it is useful to provide (and therefore require) an external name for any parameter with a default value. This ensures that the argument for that parameter is clear in purpose if a value is provided when the function is called. + +在大多数情况下,给任何带有默认值的参数提供外部参数名十分有用(而且非常必要)。这可以确保在给函数调用提供值时,参数的用途是明确的。 + +To make this process easier, Swift provides an automatic external name for any defaulted parameter you define, if you do not provide an external name yourself. The automatic external name is the same as the local name, as if you had written a hash symbol before the local name in your code. + +为了使定义外部参数名更简单,如果你没有提供自定义的外部参数名,Swift 会为你定义的任何默认参数自动提供外部参数名。在代码中,如果你已经在本地参数名前加上 # 标识,那么自动外部参数名和本地参数名相同。 + +Here’s a version of the join function from earlier, which does not provide external names for any of its parameters, but still provides a default value for its joiner parameter: + +下面是 join函数之前的版本,这个函数没有给它的任何参数提供外部参数名,但仍然为它的 joiner 参数提供了默认值。 + +```js +func join(s1: String, s2: String, joiner: String = " ") -> String { + return s1 + joiner + s2 +} +``` + +In this case, Swift automatically provides an external parameter name of joiner for the defaulted parameter. The external name must therefore be provided when calling the function, making the parameter’s purpose clear and unambiguous: + +在这个例子中,Swift自动为默认参数joiner提供了外部参数名。因此,在调用该函数时,必须使用外部参数名,使这个参数的用途明确清晰。 + +```js +join("hello", "world", joiner: "-") +// returns "hello-world” +``` + +> NOTE 注意 + +> You can opt out of this behavior by writing an underscore (_) instead of an explicit external name when you define the parameter. However, external names for defaulted parameters are always preferred where appropriate. + +> 定义参数时,可以使用下划线(_)来避免显式使用参数外部名。不过,在大多情况下,都推荐为有默认值的参数提供外部参数名。 + +## Parameters 可变参数 + +A variadic parameter accepts zero or more values of a specified type. You use a variadic parameter to specify that the parameter can be passed a varying number of input values when the function is called. Write variadic parameters by inserting three period characters (...) after the parameter’s type name. + +可变参数接收指定类型的零个或多个值。调用函数时,参数可以传入不定个数的输入值。这种参数定义为可变参数。通过在参数类型名后插入3个点字符(…)来定义可变参数。 + +The values passed to a variadic parameter are made available within the function’s body as an array of the appropriate type. For example, a variadic parameter with a name of numbers and a type of Double... is made available within the function’s body as a constant array called numbers of type Double[]. + +传给可变参数的值,在函数体内部可以当做相应类型的数组使用。例如,名为 numbers 的 Double...类型的可变参数,可以在函数体内部作为名为numbers的Double[]类型的常量数组使用。 + +The example below calculates the arithmetic mean (also known as the average) for a list of numbers of any length: + +下面的例子可以对一个有任意个数的数字列表求算术平均数(也被称为平均数) + +```js +func arithmeticMean(numbers: Double...) -> Double { + var total: Double = 0 + for number in numbers { + total += number + } + return total / Double(numbers.count) +} +arithmeticMean(1, 2, 3, 4, 5) +// returns 3.0, which is the arithmetic mean of these five numbers +arithmeticMean(3, 8, 19) +// returns 10.0, which is the arithmetic mean of these three numbers +``` + +> NOTE 注意 + +> A function may have at most one variadic parameter, and it must always appear last in the parameter list, to avoid ambiguity when calling the function with multiple parameters. + +> 在调用带有多个参数的函数时,为了避免产生歧义,函数最多只能有一个可变参数,而且它必须总是放在参数列表的最后。 + + +> If your function has one or more parameters with a default value, and also has a variadic parameter, place the variadic parameter after all the defaulted parameters at the very end of the list. + +> 如果函数有一个或多个参数带有默认值,且还有一个可变参数,请把可变参数放在所有默认参数列表的最后。 + +## Constant and Variable Parameters 常量参数和变量参数 + +Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. + +函数参数默认都是常量。试图在函数体内改变参数值会导致编译时错误。这会防止你意外地改变参数值。 + +However, sometimes it is useful for a function to have a variable copy of a parameter’s value to work with. You can avoid defining a new variable yourself within the function by specifying one or more parameters as variable parameters instead. Variable parameters are available as variables rather than as constants, and give a new modifiable copy of the parameter’s value for your function to work with. + +然而,有时使用参数的拷贝值对于函数来说十分有用。为了避免在函数内部定义新变量,可以把一个或多个参数定义为变量参数。这时,变量参数可以作为变量而不是常量使用,并且提供一个新的可修改的参数值的拷贝给函数使用。 + +Define variable parameters by prefixing the parameter name with the keyword var: + +可以在参数名前增加关键字 var 来定义变量参数: + +```js +func alignRight(var string: String, count: Int, pad: Character) -> String { + let amountToPad = count - countElements(string) + for _ in 1...amountToPad { + string = pad + string + } + return string +} +let originalString = "hello" +let paddedString = alignRight(originalString, 10, "-") +// paddedString is equal to "-----hello" +// originalString is still equal to “hello" +``` + +This example defines a new function called alignRight, which aligns an input string to the right edge of a longer output string. Any space on the left is filled with a specified padding character. In this example, the string "hello" is converted to the string "-----hello”. + +这个例子定义了名为alignRight的新函数,它把输入的字符串放在一个更长的输出字符串的最右端(译者注:以实现右对齐)。这个字符串的左边用指定的字符填充。在这个例子中,字符串“hello” 转换为 字符串“——hello”。 + +The alignRight function defines the input parameter string to be a variable parameter. This means that string is now available as a local variable, initialized with the passed-in string value, and can be manipulated within the body of the function. + +函数alignRight 把输入参数 string 定义为变量参数。这意味着在函数体内部可以把string当做局部变量使用,并初始化为传入的字符串值。 + +The function starts by working out how many characters need to be added to the left of string in order to right-align it within the overall string. This value is stored in a local constant called amountToPad. The function then adds amountToPad copies of the pad character to the left of the existing string and returns the result. It uses the string variable parameter for all its string manipulation. + +为了在整个字符串中右对齐 string,首先要算出 string 的左边需要添加多少字符。这个值储存在局部常量 amoutToPad 中。然后,函数在现有 string 的左边添加 amoutToPad 多个填充字符并返回结果。所有对 string 的操作均使用了 string 的可变参数。 + +> NOTE + +> The changes you make to a variable parameter do not persist beyond the end of each call to the function, and are not visible outside the function’s body. The variable parameter only exists for the lifetime of that function call. + +> 变量参数的改变可以持续到每个函数调用的结束(不会超过),并在函数体外不可见。变量参数只存在于函数调用的生命周期中。 + +## In-Out Parameters In-Out参数 + +Variable parameters, as described above, can only be changed within the function itself. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead. + +变量参数,如前所述,只能在函数自身内部改变。如果希望函数可以修改参数值,并且这些变化持续到函数调用结束之后,需要定义该参数为In-Out 参数。 + +You write an in-out parameter by placing the inout keyword at the start of its parameter definition. An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. + +在定义的参数前添加inout关键字,可以定义in-out参数。传给函数的in-out参数的值会被函数修改,然后被传出函数,替换原来的值。 + +You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified. You place an ampersand (&) directly before a variable’s name when you pass it as an argument to an inout parameter, to indicate that it can be modified by the function. + +in-out参数作为函数参数,只能传入变量,不能传入常量或字面量。因为常量和字面量不能被修改。在变量名前直接加上&,把它作为参数传给inout参数,表示它可以被函数修改。 + +> NOTE 注意 + +> In-out parameters cannot have default values, and variadic parameters cannot be marked as inout. If you mark a parameter as inout, it cannot also be marked as var or let. + +> In-out参数不能有默认值,且可变参数不能用inout标记。如果定义一个参数为inout参数,就不能再用var或let标记。 + +Here’s an example of a function called swapTwoInts, which has two in-out integer parameters called a and b: + +下面是swapTwoInts函数的例子,它有两个inout 整型参数a和b: + +```js +func swapTwoInts(inout a: Int, inout b: Int) { + let temporaryA = a + a = b + b = temporaryA +} +``` + +The swapTwoInts function simply swaps the value of b into a, and the value of a into b. The function performs this swap by storing the value of a in a temporary constant called temporaryA, assigning the value of b to a, and then assigning temporaryA to b. + +函数swapTwoInts简单地交换a、b值。先把a值存储在临时常量temporaryA中,然后把b值赋给a,最后把temporaryA赋值给b来实现交换。 + +You can call the swapTwoInts function with two variables of type Int to swap their values. Note that the names of someInt and anotherInt are prefixed with an ampersand when they are passed to the swapTwoInts function: + +可以调用 swapTwoInts 函数交换两个 Int 型变量的值。注意把someInt 和anotherInt 传给swapTwoInts函数时,它们的名字前都有&作为前缀: + +```js +var someInt = 3 +var anotherInt = 107 +swapTwoInts(&someInt, &anotherInt) +println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") +// prints "someInt is now 107, and anotherInt is now 3” +``` + +The example above shows that the original values of someInt and anotherInt are modified by the swapTwoInts function, even though they were originally defined outside of the function. + +上面的例子显示出函数 swapTwoInts 改变了 someInt 和anotherInt 的初始值,尽管它们的初始值是在函数外定义的。 + +> NOTE 注意 + +> In-out parameters are not the same as returning a value from a function. The swapTwoInts example above does not define a return type or return a value, but it still modifies the values of someInt and anotherInt. In-out parameters are an alternative way for a function to have an effect outside of the scope of its function body. + +> In-out参数和函数返回值不一样。虽然上面swapTwoInts的例子没有定义返回类型和返回值,但它仍然修改了someInt 和anotherInt的值。In-out参数是函数对函数体范围外产生影响的另一种方式。 + +# Function Types 函数类型 + +Every function has a specific function type, made up of the parameter types and the return type of the function. + +每个函数都有一个特定的函数类型,由函数参数类型和返回类型组成。 + +For example: + +例如: + +```js +func addTwoInts(a: Int, b: Int) -> Int { + return a + b +} +func multiplyTwoInts(a: Int, b: Int) -> Int { + return a * b +} +``` + +This example defines two simple mathematical functions called addTwoInts and multiplyTwoInts. These functions each take two Int values, and return an Int value, which is the result of performing an appropriate mathematical operation. + +这个例子定义了两个简单的数学函数addTwoInts和multiplyTwoInts。每个函数都接受两个整型值并返回一个整型值——执行相应数学运算的结果。 + +The type of both of these functions is (Int, Int) -> Int. This can be read as: + +这两个函数的类型都是(Int, Int) -> Int,可以解读为: + +“A function type that has two parameters, both of type Int, and that returns a value of type Int.” + +这个函数类型有两个Int类型参数和一个Int类型返回值。 + +Here’s another example, for a function with no parameters or return value: + +下面是另一个例子,没有参数和返回值的函数。 + +```js +func printHelloWorld() { + println("hello, world") +} +``` + +The type of this function is () -> (), or “a function that has no parameters, and returns Void.” Functions that don’t specify a return value always return Void, which is equivalent to an empty tuple in Swift, shown as (). + +这个函数的类型是() -> (),或理解为”没有参数,且返回Void的函数。“不定义返回值的函数始终返回Void,相当于Swift中的空元组,写作()。 + +## Using Function Types 函数类型的使用 + +You use function types just like any other types in Swift. For example, you can define a constant or variable to be of a function type and assign an appropriate function to that variable: + +你可以像使用Swift中任何其他类型那样使用函数类型。例如,可以定义一个函数类型的常量或变量,并且把对应的函数赋值给它。 + +```js +var mathFunction: (Int, Int) -> Int = addTwoInts +``` + +This can be read as: + +这可以解读为: + +“Define a variable called mathFunction, which has a type of ‘a function that takes two Int values, and returns an Int value.’ Set this new variable to refer to the function called addTwoInts.” + +”定义名为mathFunction的变量,该变量类型为’可以接收两个Int类型参数并返回Int类型值的函数’。设置这个新变量指向addTwoInts函数“ + +The addTwoInts function has the same type as the mathFunction variable, and so this assignment is allowed by Swift’s type-checker. + +函数addTwoInts 和mathFunction 有相同的类型,因此Swift的类型检查允许这种赋值。 + +You can now call the assigned function with the name mathFunction: + +可以使用mathFunction名来调用被赋值的函数: + +```js +println("Result: \(mathFunction(2, 3))") +// prints "Result: 5” +``` + +A different function with the same matching type can be assigned to the same variable, in the same way as for non-function types: + +可以把具有相同类型的不同函数赋值给同一变量,对于无函数类型的函数也适用。 + +```js +mathFunction = multiplyTwoInts +println("Result: \(mathFunction(2, 3))") +// prints "Result: 6” +``` +As with any other type, you can leave it to Swift to infer the function type when you assign a function to a constant or variable: + +与其他类型一样,当把函数赋值给常量或变量时,可以留给Swift来推断函数类型。 + +```js +let anotherMathFunction = addTwoInts +// anotherMathFunction is inferred to be of type (Int, Int) -> Int +``` + +## Function Types as Parameter Types 作为参数类型的函数类型 + +You can use a function type such as (Int, Int) -> Int as a parameter type for another function. This enables you to leave some aspects of a function’s implementation for the function’s caller to provide when the function is called. + +可以使用类似 (Int, Int) -> Int的函数类型作为另一个函数的参数类型。这样在函数调用时,可以把一个函数的某些实现留给函数调用者。 + +Here’s an example to print the results of the math functions from above: + +下面的例子可以输出上面数学函数的计算结果。 + +```js +func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) { + println("Result: \(mathFunction(a, b))") +} +printMathResult(addTwoInts, 3, 5) +// prints "Result: 8” +``` + +This example defines a function called printMathResult, which has three parameters. The first parameter is called mathFunction, and is of type (Int, Int) -> Int. You can pass any function of that type as the argument for this first parameter. The second and third parameters are called a and b, and are both of type Int. These are used as the two input values for the provided math function. + +这个例子定义了printMathResult函数,它有三个参数。第一参数是 mathFunction,(Int, Int) -> Int类型的。可以传入任何这种类型的函数作为该函数的第一个参数。第二和第三个参数是a和b,都是Int类型。把它们作为所提供的数学函数的两个输入值。 + +When printMathResult is called, it is passed the addTwoInts function, and the integer values 3 and 5. It calls the provided function with the values 3 and 5, and prints the result of 8. + +调用printMathResult 时,传入addTwoInts 函数、Int类型的3和5作为参数。让后把3和5作为参数调用 addTwoInts ,最后输出结果8。 + +The role of printMathResult is to print the result of a call to a math function of an appropriate type. It doesn’t matter what that function’s implementation actually does—it matters only that the function is of the correct type. This enables printMathResult to hand off some of its functionality to the caller of the function in a type-safe way. + +函数printMathResult 的作用是输出相应类型的数学函数的调用结果。它与传入函数的具体实现无关,只与函数具有的正确类型有关。这让函数 printMathResult 以类型安全的方式,把它的部分功能交给其调用者去实现。 + +## Function Types as Return Types + +## 作为返回类型的函数类型 + +You can use a function type as the return type of another function. You do this by writing a complete function type immediately after the return arrow (->) of the returning function. + +可以使用函数 类型作为另一个函数的返回类型。需要在返回函数的返回箭头(->)后编写完整的函数类型。 + +The next example defines two simple functions called stepForward and stepBackward. The stepForward function returns a value one more than its input value, and the stepBackward function returns a value one less than its input value. Both functions have a type of (Int) -> Int: + +下面的例子定义了stepForward 和stepBackward两个简单函数。stepForward 函数返回值比输入值多1,而stepBackward 函数返回值比输入值少1.这两个函数都是 (Int) -> Int类型: + +```js +func stepForward(input: Int) -> Int { + return input + 1 +} +func stepBackward(input: Int) -> Int { + return input - 1 +} +``` + +Here’s a function called chooseStepFunction, whose return type is “a function of type (Int) -> Int”. chooseStepFunction returns the stepForward function or the stepBackward function based on a Boolean parameter called backwards: + +下面是函数chooseStepFunction,它的返回类型是(Int) -> Int的函数。chooseStepFunction 根据Boolean 类型参数 backwards 判断是返回 stepForward 函数,还是返回 stepBackward 函数。 + +```js +func chooseStepFunction(backwards: Bool) -> (Int) -> Int { + return backwards ? stepBackward : stepForward +} +``` + +You can now use chooseStepFunction to obtain a function that will step in one direction or the other: + +使用函数chooseStepFunction,可以获得返回函数,指示前进或后退。 + +```js +var currentValue = 3 +let moveNearerToZero = chooseStepFunction(currentValue > 0) +// moveNearerToZero now refers to the stepBackward() function +``` + +The preceding example works out whether a positive or negative step is needed to move a variable called currentValue progressively closer to zero. currentValue has an initial value of 3, which means that currentValue > 0 returns true, causing chooseStepFunction to return the stepBackward function. A reference to the returned function is stored in a constant called moveNearerToZero. + +前面的例子实现了是需要正向还是反向移动,使得变量 currentValue 逐步趋向于零的功能。currentValue 的初始值是3,这意味着currentValue > 0 返回true,使得chooseStepFunction 返回stepBackward 函数。返回的函数引用存储在常量moveNearerToZero中。 + + +Now that moveNearerToZero refers to the correct function, it can be used to count to zero: + +现在 moveNearerToZero 指向了正确的函数,可以用来计数到零: + +```js +println("Counting to zero:") +// Counting to zero: +while currentValue != 0 { + println("\(currentValue)... ") + currentValue = moveNearerToZero(currentValue) +} +println("zero!") +// 3... +// 2... +// 1... +// zero! +``` + +# Nested Functions +# 嵌套函数 + +All of the functions you have encountered so far in this chapter have been examples of global functions, which are defined at a global scope. You can also define functions inside the bodies of other functions, known as nested functions. + +到目前为止,本章所有的全局函数都是在全局范围定义的。也可以在其他函数的内部定义函数,称为嵌套函数。 + +Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope. + +默认情况下,嵌套函数不能在外部访问,但仍可以被闭包函数调用。闭包函数也可以返回它其中一个嵌套函数,使得这个嵌套函数可以在其他作用域内使用。 + +You can rewrite the chooseStepFunction example above to use and return nested functions: + +可以重写上面的chooseStepFunction 例子来返回嵌套函数: + +```js +func chooseStepFunction(backwards: Bool) -> (Int) -> Int { + func stepForward(input: Int) -> Int { return input + 1 } + func stepBackward(input: Int) -> Int { return input - 1 } + return backwards ? stepBackward : stepForward +} +var currentValue = -4 +let moveNearerToZero = chooseStepFunction(currentValue > 0) +// moveNearerToZero now refers to the nested stepForward() function +while currentValue != 0 { + println("\(currentValue)... ") + currentValue = moveNearerToZero(currentValue) +} +println("zero!") +// -4... +// -3... +// -2... +// -1... +// zero! +``` diff --git a/src/chapter2/07_Closures.md b/src/chapter2/07_Closures.md index e69de29..4f07c0a 100644 --- a/src/chapter2/07_Closures.md +++ b/src/chapter2/07_Closures.md @@ -0,0 +1,454 @@ +# Closures 闭包 + +Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. + +闭包是一个封闭的代码块,可以在你的程序中传递和使用。Swift中的闭包跟objective-C和C语言中的block很类似,其他语言中的匿名函数也跟此相似。 + +Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables, hence the name “closures”. Swift handles all of the memory management of capturing for you. + +在定义闭包的上下文环境中,闭包可以捕获和保存这个上下文环境中任何常量和变量的指针引用,可以理解成将这些常量和变量封闭了起来,所以取名为“闭包”。Swift会帮你处理所有闭包涉及的内存管理。 + + +> Note: +> 注意: +> Don’t worry if you are not familiar with the concept of “capturing”. It is explained in detail below in Capturing Values. +> 如果你还不是很熟悉“capturing”,下面有关于“capturing values”的细节。 + +Global and nested functions, as introduced in Functions, are actually special cases of closures. Closures take one of three forms: + +前面介绍过的全局函数和嵌套函数其实是闭包的特殊情况,闭包有以下三种: + +* Global functions are closures that have a name and do not capture any values. +* 全局函数是一种拥有函数名但是不会捕获任何值的闭包 + +* Nested functions are closures that have a name and can capture values from their enclosing function. +* 嵌套的函数是一种拥有函数名并且会捕获外层函数中变量和常量的闭包 + +* Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context. +* 闭包表达式是一种没有名字的闭包,他通过轻量简便的语法定义,可以从他们的上下文中捕获变量和常量 + +Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include: + +Swift中的闭包表达式是一种非常简洁明了的语法,通过使用优化的闭包语法,使得一般场景中的代码显得非常的整洁,这些优化包含 + +* Inferring parameter and return value types from context +* 从上下文中自动推断出参数和返回值类型 + +* Implicit returns from single-expression closures +* 单条语句的闭包自动获得隐式的返回 + +* Shorthand argument names +* 简化的参数名称(通过使用 $0...$n 来使用参数) + +* Trailing closure syntax +* 返回类型后置语法(函数返回值类型位于函数声明的末端) + +## Closure Expressions 闭包表达式 + +Nested functions, as introduced in Nested Functions, are a convenient means of naming and defining self-contained blocks of code as part of a larger function. However, it is sometimes useful to write shorter versions of function-like constructs without a full declaration and name. This is particularly true when you work with functions that take other functions as one or more of their arguments. + +嵌套函数是一种在函数中独立的代码块,尤其在大型函数中,嵌套函数可以提供简便的命名和定义。但是有时候使用不带完整声明和命名的函数结构体会很实用,尤其在一些函数使用其他函数作为参数的时候。 + +Closure expressions are a way to write inline closures in a brief, focused syntax. Closure expressions provide several syntax optimizations for writing closures in their simplest form without loss of clarity or intent. The closure expression examples below illustrate these optimizations by refining a single example of the sort function over several iterations, each of which expresses the same functionality in a more succinct way. + +闭包表达式是一种短小精悍的方式来定义内联的闭包,闭包表达式提供几种优化过的语法来定义闭包,并且不会看起来有混淆和歧义。下面的闭包表达式例子定义一些简单的排序函数,每一种都通过更简洁明了的方式达到了同样的目的。 +      + +### The Sort Function 排序函数 + +Swift’s standard library provides a function called sort, which sorts an array of values of a known type, based on the output of a sorting closure that you provide. Once it completes the sorting process, the sort function returns a new array of the same type and size as the old one, with its elements in the correct sorted order. + +Swift 的标准库提供了一个函数叫做 sort,通过你提供的排序闭包用来对数组中的元素进行排序。一旦完成排序,sort 函数会返回一个跟以前类型和数量一样的数组,并且里面的元素都已经排列成正确的顺序。 + +The closure expression examples below use the sort function to sort an array of String values in reverse alphabetical order. Here’s the initial array to be sorted: + +下面的闭包表达式例子通过使用sort函数来对一个都是 String 类型的数组排序,数组的定义如下: + +``` +let names = ["Chris","Alex","Ewa","Barry","Daniella"] +``` + +The sort function takes two arguments: + +sort 函数有2个参数 + +* An array of values of a known type. + +* 已知类型的数组 + +* A closure that takes two arguments of the same type as the array’s contents, and returns a Bool value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value, and false otherwise. +* 这个闭包将两个相同类型的数组元素作为它的参数,然后返回一个布尔值用来表明,排序时第一个值是否应该排在第二个值的前面或者后面。如果第一个值应该出现在第二个值之前,这个闭包返回 true,否则返回 false。 + +This example is sorting an array of String values, and so the sorting closure needs to be a function of type (String, String) -> Bool. + +这是一个对 String 类型的数组进行排序的例子,所以这个排序的闭包需要有以下类型 (String,String) ->Bool。 + +One way to provide the sorting closure is to write a normal function of the correct type, and to pass it in as the sort function’s second parameter: + +函数名字加正确的类型是排序闭包的写法之一,然后在 sort 函数中作为第二个参数传入。 + +``` +func backwards(s1:String,s2:String) -> Bool{ + return s1>s2 +} + +var reversed = sort(names,backwards) +// reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"] +``` + +If the first string (s1) is greater than the second string (s2), the backwards function will return true, indicating that s1 should appear before s2 in the sorted array. For characters in strings, “greater than” means “appears later in the alphabet than”. This means that the letter "B" is “greater than” the letter "A", and the string "Tom" is greater than the string "Tim". This gives a reverse alphabetical sort, with "Barry" being placed before "Alex", and so on. + +如果第一个字符串 s1 比第二个字符串 s2 比较值更大,函数 backwards 会返回 true,这样在排序之后的数组中 s1 应该会排在 s2 之前,对于在字符串中的字符来说,“值大于”等于“在字符表中出现的更晚”,这意味着字符”B“比字符”A“的值更大,所以字符串”Tom“会比 ”Tim“ 更大。这个排序将会把字符表倒序排列,所以 “Barry” 会放在 “Alex” 之前。 + +However, this is a rather long-winded way to write what is essentially a single-expression function (a > b). In this example, it would be preferable to write the sorting closure inline, using closure expression syntax. + +然而这么繁重的写法在本质上只是一个表达式:a>b。在下面的例子里,使用闭包表达式写排序内联的闭包将会是更好的方式。 + +### Closure Expression Syntax 闭包表达式语法 + +Closure expression syntax has the following general form: + +闭包表达式语法定义如下: + +{ (parameters) -> return type in + statements +} + +{(参数)-> 返回值 in  +    语句 +} + +Closure expression syntax can use constant parameters, variable parameters, and inout parameters. Default values cannot be provided. Variadic parameters can be used if you name the variadic parameter and place it last in the parameter list. Tuples can also be used as parameter types and return types. + +闭包表达式语法可以使用常量,变量还有 inout 来作为参数,不能提供缺省参数,如果你要使用可选参数可以将它们定义在参数列表的结尾,tuples 可以作为参数和返回值类型。 + +The example below shows a closure expression version of the backwards function from earlier: + +下面的例子是闭包表达式版本的 backwords 排序函数: + +``` +reversed = sort(names,{(s1:String,s2:String) -> Bool in  +return s1>s2}) +``` + +Note that the declaration of parameters and return type for this inline closure is identical to the declaration from the backwards function. In both cases, it is written as (s1: String, s2: String) -> Bool. However, for the inline closure expression, the parameters and return type are written inside the curly braces, not outside of them. + +在这个表达式的定义和返回值类型跟前面的 backwords 函数完全相同,在这两个例子中,都是这样写的(s1:String,s2:String)->Bool,然而对于内联的闭包表达式,参数和返回值类型都写在大括号里面。 + +The start of the closure’s body is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin. + +闭包语句的书写在关键字 in 之后,这个关键字表明的意思是:闭包表达式的参数和返回值类型已经定义结束,闭包的语句可以开始书写了。 +Because the body of the closure is so short, it can even be written on a single line: + +因为这个闭包的语句太短了,所以可以写在一行之内 + +``` +reversed = sort(name,{(s1:String,s2:String) -> Bool in return s1 > s2 }) +``` + +This illustrates that the overall call to the sort function has remained the same. A pair of parentheses still wrap the entire set of arguments for the function. However, one of those arguments is now an inline closure. + +这个表达式在 sort 函数中跟前面的例子得到的效果是完全一样的,一对圆括号包含了函数中所有的参数集。 + +### Inferring Type From Context 上下文推断返回值类型 + +Because the sorting closure is passed as an argument to a function, Swift can infer the types of its parameters and the type of the value it returns from the type of the sort function’s second parameter. This parameter is expecting a function of type (String, String) -> Bool. This means that the String, String, and Bool types do not need to be written as part of the closure expression’s definition. Because all of the types can be inferred, the return arrow (->) and the parentheses around the names of the parameters can also be omitted: + +由于排序的闭包是作为函数的参数传入的,Swift 会根据 sort 函数的第二个参数类型来推断其闭包参数和返回值的类型。这个参数期望的函数类型为 (String,String)->Bool,这意味着 String、 String 和BOOL类型并不强制需要在写闭包表达式里,因为所有类型都可以推断,返回的箭头 -> 和他周围的参数名字都可以被省略 + +``` +     reversed = sort(names,{s1,s2 in return s1 > s2)}) +``` + +It is always possible to infer parameter types and return type when passing a closure to a function as an inline closure expression. As a result, you rarely need to write an inline closure in its fullest form. + +作为函数参数的内联闭包表达式都可以推断参数和返回值的类型,所以你很少需要写内联闭包的完整形式。 + +Nonetheless, you can make the types explicit if you wish, and doing so is encouraged if it avoids ambiguity for readers of your code. In the case of the sort function, the purpose of the closure is clear from the fact that sorting is taking place, and it is safe for a reader to assume that the closure is likely to be working with String values, because it is assisting with the sorting of an array of strings. + +虽然如此,如果你愿意你可以明确的写出参数的类型,这样可以让你的代码阅读起来能减少歧义。在排序的例子中,由于这个例子是用来给字符串数组排序的,这就使得这个闭包的目的很明显,而且对读者来说也很容易的就想到这个闭包是用来处理字符串的。 + +### Implicit Returns from Single-Expression Closures 单表达式的隐式返回值 + +Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration, as in this version of the previous example: + +单表达式的返回值可以通过隐藏 return 关键字来隐式返回结果,上面的表达式可以写成: + +``` +reversed = sort(names,{s1,s2 in s1 > s2}) +``` + +Here, the function type of the sort function’s second argument makes it clear that a Bool value must be returned by the closure. Because the closure’s body contains a single expression (s1 > s2) that returns a Bool value, there is no ambiguity, and the return keyword can be omitted. + +这个例子里面,sort 的第二个参数类型定义了闭包的返回值是BOOL类型,而且闭包表达式中只包含了一个表达式 (s1 >s2)而且这个表达式返回 BOOL 类型,所以这里可以省略掉 return,并且不会产生歧义。 + +### Shorthand Argument Names 参数名的缩写 + +Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on. + +Swift 里为内联的参数提供了参数的缩写功能,你可以通过 $0,$1,$2 来调用闭包的参数。 + +If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body: + +如果你在闭包表达式中使用参数名称的缩写,在闭包参数列表中就可以省略对其的定义,对应参数的类型将会通过函数参数的定义来推断,in 关键字也可以被忽略掉: + +``` +reversed = sort(name,{$0>$1} ) +``` + +Here, $0 and $1 refer to the closure’s first and second String arguments. + +这个例子中,$0 和 $1 表示闭包中的第一和第二个参数 + +### Operator Functions 运算符函数 + +There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a function that has two parameters of type String, and returns a value of type Bool. This exactly matches the function type needed for the sort function’s second parameter. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation: + +实际上还有更精简的方式来书写上面的闭包表达式,Swift 的 String 类型对于大于号(>)做了运算符的扩展,作为函数接受两个 String 类型的参数,返回值是 BOOL 类型,刚好满足 sort 函数中第二个参数的函数类型定义,所以,你可以简单的将大于运算法传递进去,Swift会推断你需要使用这个 String 的运算符函数 + +``` +reversed = sort(names,>) +``` + +For more about operator functions, see Operator Functions. + +更多关于运算符函数请看 Operator Functions + +## Trailing Closures 跟尾闭包 + +If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports: + +如果你需要将一个闭包表达式作为一个函数的最后一个参数传入,使用跟尾闭包的写法会更有可读性,尾闭包表达式全部写在函数最后的括号中。 + +``` +func someFunctionThatTakesAClosure(closure: () -> ()) { +    // function body goes here +} +  +// here's how you call this function without using a trailing closure: +  +someFunctionThatTakesAClosure({ +    // closure's body goes here +    }) +  +// here's how you call this function with a trailing closure instead: +  +someFunctionThatTakesAClosure() { +    // trailing closure's body goes here +} + +``` + +> Note: +> 注意: +> If a closure expression is provided as the function’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function’s name when you call the function. +> 如果函数只有闭包表达式这一个参数,你可以在使用跟尾闭包的时候把()省略掉。 + +The string-sorting closure from the Closure Expression Syntax section above can be written outside of the sort function’s parentheses as a trailing closure: + +上面的 sort 函数可以改写为 + +``` +reversed = sort(name){$0>$1} +``` + +Trailing closures are most useful when the closure is sufficiently long that it is not possible to write it inline on a single line. As an example, Swift’s Array type has a map method which takes a closure expression as its single argument. The closure is called once for each item in the array, and returns an alternative mapped value (possibly of some other type) for that item. The nature of the mapping and the type of the returned value is left up to the closure to specify. + +跟尾闭包的写法在一些闭包包体逻辑非常长的时候很有用,例如,Swift 中的 array 类型有一个 map 的方法,这个方法只需要一个闭包表达式作为其参数,这个闭包会对数组中每一个元素调用一次,然后返回该函数映射的值(也可能是不同的类型),具体的映射方式和返回值由闭包中的逻辑决定。 + +After applying the provided closure to each array element, the map method returns a new array containing all of the new mapped values, in the same order as their corresponding values in the original array. + +将数组闭包函数应用到数组每个元素之后,map 方法将返回一个新的数组,数组中包含了与原数组一一对应的值 + +Here’s how you can use the map method with a trailing closure to convert an array of Int values into an array of String values. The array [16, 58, 510] is used to create the new array ["OneSix", "FiveEight", "FiveOneZero"]: + +在下面的例子中,你可以使用map函数和跟尾闭包来降数组中的 Int 值转换成 String 值。数组 [16,58,510] 用来创建新的数组 [“OneSix”,”FiveEigh”,”FiveOneZero”]: + +``` +let digitNames = [ +    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four", +    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" +] +let numbers = [16, 58, 510] +``` +The code above creates a dictionary of mappings between the integer digits and English-language versions of their names. It also defines an array of integers, ready to be converted into strings. + +上面的代码使用数字和它所对应的英文字符串创建了一个字典,然后定义了一个数字的数组,用来进行转换。 + +You can now use the numbers array to create an array of String values, by passing a closure expression to the array’s map method as a trailing closure. Note that the call to numbers.map does not need to include any parentheses after map, because the map method has only one parameter, and that parameter is provided as a trailing closure: + +现在你可以使用 numbers 数组来创建一个对应的字符串数组,将一个闭包表达式作为跟尾闭包传递进数组的 map 函数,在写 numbers。map 方法的后面不需要添加括号,因为 map 方法只有一个参数,这个参数已经作为跟尾闭包的形式提供了: + +``` +let strings = numbers.map { +    (var number) -> String in +    var output = "" +    while number > 0 { +        output = digitNames[number % 10]! + output +        number /= 10 +    } +    return output +} +// strings is inferred to be of type String[] +// its value is ["OneSix", "FiveEight", "FiveOneZero"] +``` + +The map function calls the closure expression once for each item in the array. You do not need to specify the type of the closure’s input parameter, number, because the type can be inferred from the values in the array to be mapped. + +map 函数将会对数组中的每一个元素调用一次闭包表达式,你不需要制定闭包表达式的输入参数类型,number,因为这个类型可以通过数组中元素的类型来进行推断。 + +In this example, the closure’s number parameter is defined as a variable parameter, as described in Constant and Variable Parameters, so that the parameter’s value can be modified within the closure body, rather than declaring a new local variable and assigning the passed number value to it. The closure expression also specifies a return type of String, to indicate the type that will be stored in the mapped output array. + +这个例子中,闭包中的 number 参数定义成变量参数(具体参见constans and variable parameters),所以这个参数的值可以在闭包内进行修改。闭包表达式指定了返回值类型为 String,表明存储应映射值的新数组类型为 String + +The closure expression builds a string called output each time it is called. It calculates the last digit of number by using the remainder operator (number % 10), and uses this digit to look up an appropriate string in the digitNames dictionary. + +上面的闭包表达式每次被调用的时候创建了一个字符串返回,使用取余运算(number % 10) 计算最后一位数字并且用这个数字在 digitNames 字典中查找对应的字符串。 + +> Note: +> 注意: +> The call to the digitNames dictionary’s subscript is followed by an exclamation mark (!), because dictionary subscripts return an optional value to indicate that the dictionary lookup can fail if the key does not exist. In the example above, it is guaranteed that number % 10 will always be a valid subscript key for the digitNames dictionary, and so an exclamation mark is used to force-unwrap the String value stored in the subscript’s optional return value. +> 字典 digitNames 的下标之后跟着一个惊叹号(!),因为字典下标返回了一个可选值,表明该 Key 对应的返回值可能不存在,在上面的例子中,number % 10 保证了在字典中总会有值对应,因此惊叹号强制取出存储在下标中的 String 类型的值 + +The string retrieved from the digitNames dictionary is added to the front of output, effectively building a string version of the number in reverse. (The expression number % 10 gives a value of 6 for 16, 8 for 58, and 0 for 510.) + +从 digitNames 字典中获取的字符串添加进输出的前部,逆序的建立了一个字符串的数组版本。(在表达式 number % 10 中,如果 number 为16,则返回6,58则返回8,510返回0) + +The number variable is then divided by 10. Because it is an integer, it is rounded down during the division, so 16 becomes 1, 58 becomes 5, and 510 becomes 51. + +number 变量之后除以10,因为是整数,在计算过程中未除尽的部分会被忽略,因此16变成1,58变成5,510变成了51 + +The process is repeated until number /= 10 is equal to 0, at which point the output string is returned by the closure, and is added to the output array by the map function. + +将整个过程重复,直到 number / 10 变成 0,这时闭包会将字符串输出,而 map 函数则将返回的字符串添加进一个新的映射数组中。 + +The use of trailing closure syntax in the example above neatly encapsulates the closure’s functionality immediately after the function that closure supports, without needing to wrap the entire closure within the map function’s outer parentheses. + +上面的例子中跟尾闭包语法在函数之后将具体的逻辑简介的包装了起来,而不需要将闭包包裹在括号之内 + +## Capturing Values 捕获值 + +A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists. + +闭包可以从变量和常量定义的上下文中捕获他们的值,即使定义他们的域已经不存在了,闭包仍然可以访问和改变他们的值 + +The simplest form of a closure in Swift is a nested function, written within the body of another function. A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function. + +Swift 中嵌套函数是闭包最简形式,嵌套函数是定义在其他函数中的函数,它可以捕获外部函数中的所有参数和定义的常量和变量 + +Here’s an example of a function called makeIncrementor, which contains a nested function called incrementor. The nested incrementor function captures two values, runningTotal and amount, from its surrounding context. After capturing these values, incrementor is returned by makeIncrementor as a closure that increments runningTotal by amount each time it is called. + +这里有一个 makeIncrementor 函数的例子,包含了一个叫做 incrementor 的嵌套函数,嵌套函数 incrementor 从上下文中捕获了两个值,runningTotal 和 amount。之后 makeIncrementor 将 incrementor 作为闭包返回,每次调用 incrementor 时,都会以 amount 作为增量来增加 runningTotal 的值 + +``` +func makeIncrementor(forIncrement amount: Int) -> () -> Int { + var runningTotal = 0 + func incrementor() -> Int { + runningTotal += amount + return runningTotal + } + return incrementor +} +``` +The return type of makeIncrementor is () -> Int. This means that it returns a function, rather than a simple value. The function it returns has no parameters, and returns an Int value each time it is called. To learn how functions can return other functions, see Function Types as Return Types. + +makeIncrementor 的返回值类型是 () -> Int,这表示返回一个函数。这个返回的函数没有其他参数并且每次调用都返回一个 Int 类型的值。要学习如何在函数中返回其他函数,参见Function Types as Return Types + +The makeIncrementor function defines an integer variable called runningTotal, to store the current running total of the incrementor that will be returned. This variable is initialized with a value of 0. + +makeIncrementor 函数定义了一个整形变量叫做 runningTotal。这个变量存储了当前函数的返回值,并且初始化为0。 + + +The makeIncrementor function has a single Int parameter with an external name of forIncrement, and a local name of amount. The argument value passed to this parameter specifies how much runningTotal should be incremented by each time the returned incrementor function is called. + +makeIncrementor 函数有一个单独的 Int 类型的变量,外部名称为 forIncrement,内部名称为 amount。这个参数的值用来表明,每次 incrementor 函数执行的时候,runningTotal 应该增加多少值。 + +makeIncrementor defines a nested function called incrementor, which performs the actual incrementing. This function simply adds amount to runningTotal, and returns the result. + +makeIncrementor 定义了一个内嵌函数叫做 incrementor,这个函数执行真正的递增逻辑。在这个函数中将 amount 的值增加到 runningTotal 中,然后返回它的值。 + +When considered in isolation, the nested incrementor function might seem unusual: + +如果单独拿出来看,内嵌函数 incrementor 看上起会有点奇怪: + +``` +func incrementor() -> Int { + runningTotal += amount + return runningTotal +} +``` + +The incrementor function doesn’t have any parameters, and yet it refers to runningTotal and amount from within its function body. It does this by capturing the existing values of runningTotal and amount from its surrounding function and using them within its own function body. + +intrementor 函数没有任何参数,他引用的变量 runningTotal 和 amouint 来自包含它的函数体,通过捕获外部环境中的变量来实现上面例子中的效果。 + +Because it does not modify amount, incrementor actually captures and stores a copy of the value stored in amount. This value is stored along with the new incrementor function. + +因为没有修改 amount 参数,incrementor 函数实际上是捕获和存储了一个 amount 的副本,这个值和 incrementor 函数一起存储了起来。 + +However, because it modifies the runningTotal variable each time it is called, incrementor captures a reference to the current runningTotal variable, and not just a copy of its initial value. Capturing a reference ensures sure that runningTotal does not disappear when the call to makeIncrementor ends, and ensures that runningTotal will continue to be available the next time that the incrementor function is called. + +然后,因为它每次调用的时候都修改了 runningTotal 变量,incrementor 捕获了一个指向当前 runningTotal 变量的引用,不单单只是拷贝了他的初始值。捕获一个变量的引用,可以确保 runningTotal 变量不会在 makeIncrementor 调用结束的时候消失掉,而且确保变量 runningTotal 在下一次调用的时候也依然可以访问。 + +> Note: +> 注意: +> Swift determines what should be captured by reference and what should be copied by value. You don’t need to annotate amount or runningTotal to say that they can be used within the nested incrementor function. Swift also handles all memory management involved in disposing of runningTotal when it is no longer needed by the incrementor function. +> Swift 会决定对一个值是保存它的拷贝还是引用。你不需要声明 amount 和 runningTotal 他们会在incrementor内嵌函数中使用。Swift 也会处理所有的内存管理,当 runningTotal 不再需要的时候会将它释放掉。 + +Here’s an example of makeIncrementor in action: + +这里是 makeIncrementor 的一个例子: + +``` +let incrementByTen = makeIncrementor(forIncrement: 10) +``` + +This example sets a constant called incrementByTen to refer to an incrementor function that adds 10 to its runningTotal variable each time it is called. Calling the function multiple times shows this behavior in action: + +这个例子将一个叫做 incrementByTen 的常量保存10与 runningTotal 变量每次想加的引用。调用函数多次来查看结果: + +``` +incrementByTen() +// returns a value of 10 +incrementByTen() +// returns a value of 20 +incrementByTen() +// returns a value of 30 +``` + +If you create another incrementor, it will have its own stored reference to a new, separate runningTotal variable. In the example below, incrementBySeven captures a reference to a new runningTotal variable, and this variable is unconnected to the one captured by incrementByTen: + +如果你创建另外一个 incrementor,它会有一个新的引用与之前的 runningTotal 完全分离,下面这个例子里面 incrementBySeven 捕获了一个新的 runningTotal 变量,这个变量跟 incrementByTen 捕获的没有任何联系 + +``` +let incrementBySeven = makeIncrementor(forIncrement: 7) +incrementBySeven() +// returns a value of 7 +incrementByTen() +// returns a value of 40 +``` + +> Note: +> 注意: +> If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles. For more information, see Strong Reference Cycles for Closures. +> 如果你在闭包里访问了一个类实例的属性,闭包会捕获这个实例或者成员函数的引用,你会创建一个互相强引用的环,Swift 使用捕获列表来打破这个互相引用的环,详情请看 Strong Reference Cycles for Closures. + +## Closures Are Reference Types 闭包都是引用类型 + +In the example above, incrementBySeven and incrementByTen are constants, but the closures these constants refer to are still able to increment the runningTotal variables that they have captured. This is because functions and closures are reference types. + +上面的例子里面 incrementBySeven 和 incrementByTen 都是常量,但是这些常量引用的闭包仍然可以增加他们捕获的 runningTotal 变量的值,这是因为函数和闭包都是引用类型。 + +Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure. In the example above, it is the choice of closure that incrementByTen refers to that is constant, and not the contents of the closure itself. + +任何时候当你将一个函数或者闭包赋值给一个常量或者变量,实际上你将这个闭包的引用设置给了这个常量或者变量。上面的例子中,变成常量的是这个闭包的引用,而不是它的闭包体内包含的内容。 + +This also means that if you assign a closure to two different constants or variables, both of those constants or variables will refer to the same closure: + +这意味着如果你将一个闭包赋值给两个不同的变量和常量,他们都会指向同一个闭包 + +``` +       let alsoIncrementByTen = incrementByTen +               alsoIncrementByTen() +               // returns a value of 50 +``` diff --git a/src/chapter2/08_Enumerations.md b/src/chapter2/08_Enumerations.md index e69de29..09b272a 100644 --- a/src/chapter2/08_Enumerations.md +++ b/src/chapter2/08_Enumerations.md @@ -0,0 +1,383 @@ +# 枚举 (Enumerations) + +枚举为一组相关值定义了一个通用的类型,使你能够在代码中使用这些值的同时保持类型安全。 + +|| 枚举为一组相关的值定义了一个通用的类型,让我们能够在代码中安全地操作这些值。 + +如果你熟悉C语言,你就知道C语言里的枚举是给相关名称赋予一系列整型值。相比C语言,Swift中的枚举更灵活,并且不需要为每一个枚举成员指定一个值。如果枚举成员被赋予了值(也就是“原始值”),这个值可以是字符串、字符、任一整型值或者浮点类型的值。 + +|| 如果对 C 语言比较熟悉,可能就会知道在 C 语言里枚举是给相关键值赋予一系列的整型值。但 Swift 的枚举更加灵活,而且不需要为每个枚举成员指定初始值。但如果枚举成员被赋予了初始值,这个值可以是字符、字符串、任何整型或浮点类型的值。 + +或者,枚举成员也可以成员值指定任一类型的关联值,就像其他语言的联合类型或者变体类型。你可以在一个枚举变量里定义一系列通用的相关成员,每一个成员都有一个相应类型的值与其关联。 + +|| 此外,就像其它编程语言中的联合类型或变体类型一样,Swift 的枚举变量可以给每个成员指定单独的值类型。我们可以把一系列相关的值定义为一个枚举变量,而且每个枚举成员都有相应的值与其关联。 + +枚举作为Swift语言中的“一等公民”,采用了许多一贯只被类支持的特性,比如计算属性,以提供关于枚举变量当前值的额外信息,以及实例方法,以提供关于枚举变量所代表值的功能。枚举类型也支持为枚举成员定义初始化值,还有在原有实现上扩展以增加其功能,以及遵循协议提供标准功能。 + +|| 枚举作为 Swift 的核心类型拥有强大的能力,它继承了很多一直以来只被类支持的特性,比如提供当前值之外更丰富信息的属性运算,或者用以展现该变量中所关联值的实例方法。枚举类型可以通过定义构造器为每个成员提供初始值,也可以在他们原始实现上扩展更多的功能,并通过遵循特定协议对外提供标准功能。 + + +关于这些特性的更多信息,请参考属性,方法,初始化,扩展,以及协议。 + +|| 关于这些特性的更多信息,请参考[属性](),[方法](),[初始化](),[扩展](),以及[协议]()。 + + +## 枚举语法 + +## || 枚举类型的语法 + +你可以用enum关键字引入枚举类型,并把完整的定义用一对大括号包裹起来: + +|| 我们可以用 `enum` 关键字定义枚举变量,并把完整的定义用一对大括号包裹起来: + +``` +enum SomeEnumeration { + // 枚举定义 +} +``` + +以指南针的四个方位为例: + +|| 以指南针的四个方位为例: + +``` +enum CompassPoint { + case North + case South + case East + case West +} +``` + +在枚举变量内定义的值(例如North、South、East、West)称为这一枚举变量的成员值或者成员。Case 关键字表明将被定义的新的一行成员值。 + +|| 这些被枚举变量定义的值(如 North、South、East、West)被称作该枚举变量的成员。`case` 关键字表示这一行定义了新的成员值。 + +> 注意:不像C和Objective-C,Swift语言中的枚举类型在创建时并不会被赋予一个默认的整型值。在上面的CompassPoints例子中,North、South、East、West并不是默认为0,1,2,3。相反,这些不同的枚举成员都是完备的值,并且以显式声明的CompassPoint作为其类型。 + +> 注意:不像 C 或者 Objective-C,Swift 中的枚举类型不会在创建时被赋予一组初始的整型值。在上面的 `CompassPoint` 指南针例子中,`North`、`South`、`East`、`West` 并不是默认的 `0`,`1`,`2`,`3`。相反地,通过 `CompassPoint` 类型的显示声明,这些枚举成员会拥有各自独立的类型。 + +多个成员值可以用逗号分隔在同一行出现: + +|| 多个成员值可以写在同一行,使用逗号分隔: + +``` +enum Planet { + case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune +} +``` + +每一个枚举变量都定义了一个全新的类型。和Swift中的其他类型一样,枚举变量的命名(比如CompassPoint和Planet)都需要以大写字母开头,并采用单数而不是复数形式,这样阅读时就一目了然了。 + +|| 每个枚举变量都定义了一个全新的类型。和 Swift 中其它类型一样,枚举变量的命名(比如`CompassPoint` 和 `Planet`)都需要以大写字母开头,并采用单数而不是复数形式,这样阅读时就一目了然。 + +``` +var directionToHead = CompassPoint.West +``` + +当以CompassPoint其中一个值初始化时, 编译器将推断directionToHead的类型。而一旦把directionToHead以CompassPoint类型完成声明后,在设置它为CompassPoint其他值的时候,你就可以使用更为简短便捷的点语法了: + +|| 当以 `CompassPoint` 其中一个值初始化时,编译器将推断 `directionToHead` 的类型。而一旦把 `directionToHead` 以 `CompassPoint` 类型声明后,就可以用很简单的方法设置它为 `CompassPoint` 的其它值: + +``` +directionToHead = .East +``` + +directionToHead的类型已知了,因此你可以在赋值时省略类型。当遇到显式声明的枚举变量时,借助点语法可以使你的代码更具可读性。 + +|| 一旦 `directionToHead` 的类型已知,我们就可以在赋值时忽略类型的指定。当遇到显式声明的枚举变量时,这种语法可以让代码更具可读性。 + + +## 用Switch语句匹配枚举值 + +你可以通过Switch语句匹配某一枚举值: + +|| 我们可以通过 `Switch` 语句匹配某个枚举值: + +``` +directionToHead = .South +switch directionToHead { +case .North: + println("Lots of planets have a north") +case .South: + println("Watch out for penguins") +case .East: + println("Where the sun rises") +case .West: + println("Where the skies are blue") +} +// 输出 "Watch out for penguins" +``` + +这段代码可以这样理解: + +|| 这段代码可以这样理解: + +“考虑directionToHead的值:当它等于.North时,打印 “Lots of planets have a north” ;当它等于.South时,打印 “Watch out for penguins” ”,后续省略。 + +|| “对比 `directionToHead` 的值:如果它等于 `.North`,输出 "Lots of planets have a north";如果它等于 `.South`,正如此例,将输出 "Watch out for penguins"。” + +|| 以此类推。 + +就像在流程控制里所描述的那样,switch语句在确定枚举变量的值的时候要全面完备。如果删掉.West对应的情况,这段代码将不能通过编译,因为它没有考虑到完整的CompassPoint 成员列表。要求全面完备确保了枚举变量不会被意外忽略。 + +|| 就像在 [流程控制]() 里描述的一样,`switch` 语句必须考虑到所有的枚举成员。在上面的例子中,如果 `case` 语句忽略了 `.West` 成员,代码编译将不会通过,因为它没照顾到所有 `CompassPoint` 的成员。这种严格的要求确保了每个枚举成员不会被意外地忽略。 + +当不能为每一个枚举变量都提供case关键字时,可以为 那些没有一一提到的枚举成员设置一个default关键字: + +|| 当不能为每一个枚举成员都提供 `case` 语句时,可以给那些没有显式提到的成员设置一个 `default` 关键字: + +``` +let somePlanet = Planet.Earth +switch somePlanet { +case .Earth: + println("Mostly harmless") +default: + println("Not a safe place for humans") +} +// 输出 "Mostly harmless" +``` + + +## 相关值 + +## || 相关值 + +上一节内容的例子展示了枚举成员是具有独立定义和独立类型的值。你可以为Planet.Earth设置一个常量或变量,以及后续做检验。但有时为枚举成员关联其他类型的值将会很有用。这让你能为枚举成员添加额外的配置信息,并允许其在每次使用时改变其信息内容。 + +|| 上一节的例子展示了枚举成员是具有独立定义和独立类型的值。我们可以为 `Planet.Earth` 赋值为常量或变量,或者后面再调整。有时在每个枚举成员旁边关联值的类型会很有用,因为这可以让我们在声明枚举成员时附带更多额外的信息,而且允许我们在以后的每次使用过程中修改这些信息。 + +你能为Swift中的枚举变量定义任意给定类型的关联值,并且如果需要,这些关联值的类型可以根据枚举成员而不同。这样的枚举变量在其他编程语言中被称为可区分联合类型、带标签的联合类型或者变异类型。 + +|| Swift 中的枚举类型可以存储任何指定类型的相关值,而且如果需要的话,每个枚举成员的类型都可以互不一样。这种枚举类型就像是在其它编程语言中被大众熟知的*鉴别联集*、*标签联合*或*变体类型*。 + +例如,假设一个存货清单跟踪系统需要跟踪两种带有不同条形码的产品。一种产品被打上UPC-A格式的一维条形码,这一格式使用0到9的数字。每个条形码包含一个“号码系统”数字,紧跟着十个“标识符”数字,最后还有一个“校验位”数字以验证该条形码是否被正确扫描了。 + +|| 举个例子,在一个存货清单跟踪系统中需要跟踪两种带有不同条形码的产品。其中一些产品使用由数值 0 到 9 表示的一维条形码 UPC-A。这种条形码一开始会有一个指定进制的数值,然后紧跟 10 位标识符,最后会有一个校验位用来检查该条形码是否被正确地扫描。 + +![](http://gtms04.alicdn.com/tps/i4/TB1y.hbFFXXXXXibFXX.wCCNpXX-366-175.png) + +另一种产品被打上QR格式的二维码,这一格式使用ISO 8859-1的任意字符,并且可以编码长达2953个字符的字符串。 + +|| 而另一些产品则使用了 QR 格式的二维码,这一格式允许使用 ISO 8859-1 字符集的任意字符,并且可以表示长达 2953 个字符的字符串。 + +![](http://gtms01.alicdn.com/tps/i1/TB11AE1FFXXXXaSXXXX39VBMpXX-257-257.png) + +如果我们的存货清单跟踪系统能分别以长度为三的整型数组的形式保存UPC-A格式,并以任意长度的字符串格式保存QR格式就好了。 + +|| 如果我们的存货清单跟踪系统能同时以三个整型数组的形式储存 UPC-A 格式,并以任意长度的字符串格式保存QR格式的话,那就很方便了。 + +``` +enum Barcode { + case UPCA(Int, Int, Int) + case QRCode(String) +} +``` + +在 Swift 中,可以这样定义这两种产品条纹码的枚举变量: + +|| 在 Swift 中,可以这样声明兼容两种产品条形码的枚举类型: + +``` +enum Barcode { + case UPCA(Int, Int, Int) + case QRCode(String) +} +``` + +这段代码可以这样理解: + +|| 这段代码可以这样理解: + +“定义一个叫Barcode的枚举类型,可包含关联长度为三的整型数组的UPCA 以及关联字符串的QRCode”。 + +|| “声明一个叫 `Barcode` 的枚举类型,它包含了以三个整型数组(`Int`,`Int`,`Int`)的形式储存的 UPC,以及以字符串(`String`)形式存储的 `QRCode` ”。 + +这一定义不包含任何确切整型值或者字符串,它只定义Barcode.UPCA和Barcode.QRCode对应Barcode枚举变量和常量可以存储的关联值的类型。 + +|| 这个声明不包含任何确切的整型值或者字符串,它只是在 `Barcode.UPCA` 和 `Barcode.QRCode` 的描述中定义了 `Barcode` 的枚举变量或常量允许使用的值类型。 + +现在条纹码可以使用任一类型创建: + +|| 现在条形码可以通过任意一个类型创建: + +``` +var productBarcode = Barcode.UPCA(8, 85909_51226, 3) +``` + +这个例子创建了一个叫productBarcode的新变量,赋予它一个关联了(8, 8590951226, 3)数组的Barcode.UPCA。上面的“标识符”值中包含了一个下划线:85909_51226,让它作为条纹码更具可读性。 + + +|| 这个例子创建了一个叫 `productBarcode` 的新变量,并赋予了它由三个数值数组关联的 `Barcode.UPCA` 成员值。在上面的 10 位标识符中出现了一个下划线 `85909_51226`,这是为了让它作为条形码更具可读性。 + +我们也可以赋予这个产品另外一个条纹码: + +|| 或者,这个产品我们可以赋予它另外一种条形码: + + +``` +productBarcode = .QRCode("ABCDEFGHIJKLMNOP") +``` + +这时,原有的Barcode.UPCA和它的整型值被替换成新的Barcode.QRCode和字符串。类型为Barcode的常量和变量可以保存.UPCA或者.QRCode(以及它们的关联值),并且在任一给定时间内只能保存其中一个。 + +|| 这时原有的 `Barcode.UPCA` 和它关联的数值会被替换成新的 `Barcode.QRCode` 字符串。已经被定义为 `Barcode` 类型的变量或常量允许使用 `.UPCA` 或者 `.QRCode` 类型存储数据,但同一时刻只能使用其中一种类型。 + +和之前一样,我们可以使用switch语句来检验不同类型的条纹码。然而这次,可以把关联值抽取出来作为switch语句的一部分。你可以抽取关联值作为一个常量(带有前缀let)或者变量(带有前缀var)以在switch语句的case主体内使用: + +|| 和前面的一样,使用 `switch` 语句可以用来检查不同类型的条形码,但这次,我们可以把关联值抽取出来作为 `switch` 语句的一部分。我们可以把关联值以常量(使用前缀 `let`)或者变量(使用前缀 `var`)抽出,放在 `case` 内容里面。 + +``` +switch productBarcode { +case .UPCA(let numberSystem, let identifier, let check): + println("UPC-A with value of \(numberSystem), \(identifier), \(check).") +case .QRCode(let productCode): + println("QR code with value of \(productCode).") +} +// 输出 "QR code with value of ABCDEFGHIJKLMNOP." +``` + +如果一个枚举成员所有的都能被抽取为常量,或者都能被抽取为变量,你可以在枚举成员名称前放一个let或者var作为注解,简单来说就是这样: + +|| 如果一个枚举成员所有的关联类型都能被抽取为常量变量,则可以在枚举成员名称前放一个 `let` 或者`var` 声明,简单来说就是这样: + +``` +switch productBarcode { +case let .UPCA(numberSystem, identifier, check): + println("UPC-A with value of \(numberSystem), \(identifier), \(check).") +case let .QRCode(productCode): + println("QR code with value of \(productCode).") +} +// 输出 "QR code with value of ABCDEFGHIJKLMNOP." +``` + + +## 原始值 + +## || 原始值 + +上面关联值小节里的条纹码的例子展示了枚举成员是如何声明其和不同类型的值相关联。作为关联值的替代,枚举成员也可以在初始化时预先设置默认值(也叫原始值),并且其类型保持相同。 + +|| 在上面 [关联值]() 的例子中,描述了如何为每个枚举成员声明不同的存储类型。另外,所有枚举成员可以使用同一种类型预定义指定的值(一般称作 *原始值*)。 + +以下是一个例子展示命名的枚举成员和原始的ASCII码相关联: + +|| 下面的例子展示的是在每个枚举成员旁边定义了 ASCII 原始值: + +``` + enum ASCIIControlCharacter: Character { + case Tab = "\t" + case LineFeed = "\n" + case CarriageReturn = "\r" +} +``` + +这里,一个名为ASCIIControlCharacter的枚举类型的原始值被定义为字符类型,并且被设置为一些更为常见的ASCII控制字符。关于字符可参考“字符串与字符”一章。 + +|| 在这个例子里,一个名为 `ASCIIControlCharacter` 的枚举类型的值被定义为 `Character`,并给每个枚举成员预设了一些常见的 ASCII 字符。关于字符可参考 [字符串与字符]() 一章。 + +注意原始值和关联值并不一样。像这个例子中的三个ASCII码一样,原始值是你在代码中第一次定义枚举类型时就已设置的值,并且一个特定枚举成员的原始值始终不会改变。而关联值是你根据某一枚举成员创建一个常量或变量时才设置的值,并且每次创建时都可以不同。 + +|| 注意原始值和关联值并不一样,就像这个例子中的三个 ASCII 码。原始值是在代码中第一次定义枚举类型时就已设置的值,并且一个特定枚举成员的原始值始终不会改变。而关联值是根据某一枚举成员创建一个常量或变量时才设置的值,并且每次创建时都可以不同。 + +原始值可以是字符串、字符,或者任一整型和浮点数类型。每个原始值在其枚举类型声明时都必须保持唯一。当整型被用作原始值时,如果一些枚举成员没有设置特定值,它们的原始值会自增。 + +|| 原始值可以是字符串、字符,或者任一整型和浮点数类型。每个原始值在其枚举类型声明时都必须保持唯一。当整型被用作原始值时,如果一些枚举成员没有设置特定值,它们的原始值会自增。 + +下面的枚举类型是对之前Planet枚举类型的一个改良,使用整型的原始值代表每个星球离太阳距离大小的顺序。 + +|| 下面的枚举类型是对之前 `Planet` 枚举类型的一个改良,使用整型的原始值代表每个星球距离太阳近远的顺序。 + +``` +enum Planet: Int { + case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune +} +``` + +这里的自增意味着Planet.Venus的原始值为2,依此类推。 + +|| 这里的自增意味着 `Planet.Venus` 的原始值为 2,依此类推。 + +你可以通过枚举成员的toRaw方法访问其原始值: + +|| 我们可以通过枚举成员的 `toRaw` 方法访问其原始值: + +``` +let earthsOrder = Planet.Earth.toRaw() +// earthsOrder 为 3 +``` + +你也可以通过枚举类型的fromRaw方法去试图查找带有某一特定原始值的枚举成员。这个例子通过其原始值为7找到了Uranus: + +|| 也可以通过枚举类型的 `fromRaw` 方法去试图检索带有某一特定原始值的枚举成员。这个例子通过原始值为 7 找到了 `Uranus`: + +``` +let possiblePlanet = Planet.fromRaw(7) +// possiblePlanet类型为Planet?并且值等于Planet.Uranus +``` + +然而,不是所有的int值都能匹配到一个星球。因此,fromRaw方法返回一个可选的枚举成员。以上的例子中,possiblePlanet的类型是”Planet?”或者”可选Planet”。 + +|| 不过不是所有的 `Int` 值都会匹配到一个星球。所以,`fromRaw` 方法返回的是一个 *可选的* 枚举成员。上面的例子中,`possiblePlanet` 的类型是 `Planet?` 或者 ”可选 `Planet`” + +如果你试图查找位置为9的星球,fromRaw方法返回的这个可选Planet将为nil: + +|| 如果试图检索位置为 9 的星球,`fromRaw` 方法返回的这个可选 `Planet` 将为 `nil`: + +``` +let positionToFind = 9 +if let somePlanet = Planet.fromRaw(positionToFind) { + switch somePlanet { + case .Earth: + println("Mostly harmless") + default: + println("Not a safe place for humans") + } +} else { + println("There isn't a planet at position \(positionToFind)") +} +// 输出 "There isn't a planet at position 9" +``` + +这个例子使用可选绑定试图访问原始值为9的星球。如果可以取回,if let somePlanet = Planet.fromRaw(9)这一语句将取回一个可选Planet并把它赋予somePlanet。而这种情况下是不可能取回一个位置为9的星球,因此,else分支就被执行了。 + + +|| 这个例子使用可选绑定试图访问原始值为 9 的星球。如果可以访问,则 `if let somePlanet = Planet.fromRaw(9)` 这一语句将一个可选 `Planet` 赋值给 `somePlanet`。但目前没有办法找到一个位置为 9 的星球,所以 `else` 分支就被执行了。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/chapter2/09_Classes_and_Structures.md b/src/chapter2/09_Classes_and_Structures.md index e69de29..c3a543a 100644 --- a/src/chapter2/09_Classes_and_Structures.md +++ b/src/chapter2/09_Classes_and_Structures.md @@ -0,0 +1,662 @@ +## Classes and Structures +## 类与结构体 + +*Classes* and *structures* are general-purpose, flexible constructs that become the building blocks of your program’s code. You define properties and methods to add functionality to your classes and structures by using exactly the same syntax as for constants, variables, and functions.” + +*类*和*结构体*是通用的、灵活的结构,是您搭建代码的基石。您可以使用和定义常量、变量或函数相同的句法,为类添加属性和方法以增加新的功能。 + +Unlike other programming languages, Swift does not require you to create separate interface and implementation files for custom classes and structures. In Swift, you define a class or a structure in a single file, and the external interface to that class or structure is automatically made available for other code to use. + +与其他编程语言不同,Swift并不强制要求分隔自定义类或结构体的头文件和实现文件。在Swift中,你只需将类或结构体定义在一个文件中,这个类或结构体的外部接口就自动能被其他代码使用。 + +``` +NOTE + +An instance of a class is traditionally known as an object. However, Swift classes and structures are much closer in functionality than in other languages, and much of this chapter describes functionality that can apply to instances of either a class or a structure type. Because of this, the more general term instance is used. +``` + +``` +注意 +类的实例通常被当作为一个对象。但与其他语言相比,Swift中的类和结构体从功能上来说更为相似。本章中介绍的大部分功能,可以适用于任何一个类或者结构体的实例。因此我们将使用一个更加宽泛的词语--实例(instance)来描述类和结构体。 +``` + +### Comparing Classes and Structures +### 比较类和结构体 +Classes and structures in Swift have many things in common. Both can: + +* Define properties to store values +* Define methods to provide functionality +* Define subscripts to provide access to their values using subscript syntax +* Define initializers to set up their initial state +* Be extended to expand their functionality beyond a default implementation +* Conform to protocols to provide standard functionality of a certain kind + +For more information, see [Properties](), [Methods](), [Subscripts](), [Initialization](), [Extensions](), and [Protocols](). + + +在Swift中类和结构体有许多相同之处,它们都有如下功能: + +* 能定义属性以便存储数据 +* 能定义方法以便提供功能 +* ~~定义了下标以便使用下标语法来访问实例的值~~ +* 能定义设定初始状态的初始化方法 +* 能被扩展,使其有默认实现之外的功能 +* 遵从协议(protocol)规则,以便提供~~协议所规定类型的标准功能~~ + +欲了解更多信息,请参见[属性](),[方法](),[下标](),[构造过程](),[扩展]()和[协议]()章节。 + +Classes have additional capabilities that structures do not: + +* Inheritance enables one class to inherit the characteristics of another. +* Type casting enables you to check and interpret the type of a class instance at runtime. +* Deinitializers enable an instance of a class to free up any resources it has assigned. +* Reference counting allows more than one reference to a class instance. + +For more information, see [Inheritance](), [Type Casting](), [Initialization](), and [Automatic Reference Counting](). + +不过类也有一些结构体没有的额外功能: + +* 继承使得一个类能继承来自另一个类的特性。 +* 类型转换可以在运行时检查和解析一个类的实例的类型。 +* 析构方法让类的实例被~~指派给了其他内存~~时能够释放自己的资源。 +* 引用计数允许一个类的实例被多处引用。 + +欲了解更多信息,请参见[继承](),[类型转换](),[构造过程](),[自动引用计数]()章节。 + +``` +NOTE + +Structures are always copied when they are passed around in your code, and do not use reference counting. +``` + +``` +注意: +结构体在代码中被传递时始终是传值,不会涉及到引用计数 + +``` + +‌ +### Definition Syntax +### 定义语法 + +Classes and structures have a similar definition syntax. You introduce classes with the `class` keyword and structures with the `struct` keyword. Both place their entire definition within a pair of braces: + +定义类和结构体的语法相似。定义类以关键词`class`开头,而定义结构体以关键词`struct`开头。类和结构体的所有定义都写在一对大括号内: + +``` +class SomeClass { + // class definition goes here +} +struct SomeStructure { + // structure definition goes here +} +``` + + + +``` +NOTE + +Whenever you define a new class or structure, you effectively define a brand new Swift type. Give types UpperCamelCase names (such as SomeClass and SomeStructure here) to match the capitalization of standard Swift types (such as String, Int, and Bool). Conversely, always give properties and methods lowerCamelCase names (such as frameRate and incrementCount) to differentiate them from type names. +``` + + +``` +注意: + +当你定义一个新的类或者结构体时,你实际上定义了一种新的Swift类型。为了和Swift标准类型(像String,Int,Bool)保持一致,请使用首字母大写的驼峰命名法(例如SomeClass或者SomeStructure)。相反的,为了和类型名区分,属性和方法名建议使用小写驼峰命名法(如frameRate或者incrementCount)。 +``` + +Here’s an example of a structure definition and a class definition: + +下方给出了结构体和类定义的例子: + + +``` +struct Resolution { + var width = 0 + var height = 0 +} +class VideoMode { + var resolution = Resolution() + var interlaced = false + var frameRate = 0.0 + var name: String? +} +``` + +The example above defines a new structure called `Resolution`, to describe a pixel-based display resolution. This structure has two stored properties called `width` and `height`. Stored properties are constants or variables that are bundled up and stored as part of the class or structure. These two properties are inferred to be of type `Int` by setting them to an initial integer value of 0. + +例子定义了一个名叫`Resolution`的新结构体,用来描述一个基于像素的显示方案。这个结构体有两个~~属性~~:`宽`和`高`。~~属性~~是捆绑存储在类和结构体中的变量或常量。这两个属性的类型将被编译器推断为`Int`,并赋予初始值0。 + +The example above also defines a new class called `VideoMode`, to describe a specific video mode for video display. This class has four variable stored properties. The first, `resolution`, is initialized with a new `Resolution` structure instance, which infers a property type of `Resolution`. For the other three properties, new `VideoMode` instances will be initialized with an `interlaced` setting of `false` (meaning “non-interlaced video”), a playback frame rate of `0.0`, and an optional `String` value called `name`. The `name` property is automatically given a default value of `nil`, or “no `name` value”, because it is of an optional type. + +例子中也定义了一个名叫`VideoMode`的新类,用来表示视频显示的模式。VideoMode类中有四个属性。第一个是`resolution`,它在这里被初始化为一个新的`Resolution`结构体实例,而编译器将自动推断分辨率的类型为`Resolution`。除此之外新的`VideoMode`实例还将初始化三个属性,默认为`false`的`interlaced`(意味着这是个“不交错的视频”),默认值为`0.0`表示播放帧速率的`frameRate`,以及一个值可选类型为`String`的`name`。属性`name`将自动赋值为`nil`,或者说`name`无值,因为它的值是可选的。 + +### Class and Structure Instances +### 类和结构体的实例 + +The `Resolution` structure definition and the `VideoMode` class definition only describe what a `Resolution` or `VideoMode` will look like. They themselves do not describe a specific resolution or video mode. To do that, you need to create an instance of the structure or class. + +结构体`Resolution`和类`VideoMode`的定义只是告诉你`Resolution`和`VideoMode`含有什么内容。但他们本身并不描述一个特定的分辨率或者视频模式。要做到这一点,我们要生成一个他们的实例。 + +The syntax for creating instances is very similar for both structures and classes: + +生成结构体实例和生成类实例的语法非常类似: + +``` +let someResolution = Resolution() +let someVideoMode = VideoMode() +``` + +Structures and classes both use initializer syntax for new instances. The simplest form of initializer syntax uses the type name of the class or structure followed by empty parentheses, such as `Resolution()` or `VideoMode()`. This creates a new instance of the class or structure, with any properties initialized to their default values. Class and structure initialization is described in more detail in [Initialization](). + +结构体和类都是用初始化语法来生成新的实例。初始化语法最简单的形式是结构体或类的类型名后加上空括号,例如`Resolution()`和`VideoMode()`。这将创建一个所有的属性都是默认值的结构体或类。类和结构体的初始化在[构造过程]()章节中有更详细的说明。 + +### Accessing Properties +### 属性的访问 +You can access the properties of an instance using *dot syntax*. In dot syntax, you write the property name immediately after the instance name, separated by a period (.), without any spaces: + +你可以使用*点语法*访问实例的属性。使用点语法时,属性名将紧跟在实例名之后,中间以句号(.)隔开: + +``` +println("The width of someResolution is \(someResolution.width)") +// prints "The width of someResolution is 0 +``` + +In this example, `someResolution.width` refers to the `width` property of `someResolution`, and returns its default initial value of `0`. + +在这个例子中,`someResolution.width`表示`someResolution`的`width`属性,且返回默认初始值`0`。 + +You can drill down into sub-properties, such as the `width` property in the `resolution` property of a `VideoMode`: + +你可以继续使用点语法来访问属性的属性,例如`VideoMode`中`resolution`的属性`width`: + +``` +println("The width of someVideoMode is \(someVideoMode.resolution.width)") +// prints "The width of someVideoMode is 0" +``` + +You can also use dot syntax to assign a new value to a variable property: +我们还可以使用点语法来为属性变量赋值: + +``` +someVideoMode.resolution.width = 1280 +println("The width of someVideoMode is now \(someVideoMode.resolution.width)") +// prints "The width of someVideoMode is now 1280" +``` + +``` +NOTE + +Unlike Objective-C, Swift enables you to set sub-properties of a structure property directly. In the last example above, the width property of the resolution property of someVideoMode is set directly, without your needing to set the entire resolution property to a new value. +``` +``` +注意: + +与Objective-C不同,Swift允许直接为结构体的属性的子属性赋值。在上方的例子中,someVideoMode中属性resolution的子属性width被直接赋值了,不再需要给整个resolution属性赋予新值。 +``` +‌ +### Memberwise Initializers for Structure Types +### 结构体带参数的初始化方法 + +All structures have an automatically-generated memberwise initializer, which you can use to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name: + +所有结构体都有一个自动生成的带参数的初始化方法,用于新结构体实例成员属性的初始化。新实例属性的初值会通过变量名传递到带参数的初始化方法里: + +``` +let vga = Resolution(width: 640, height: 480) +``` + +Unlike structures, class instances do not receive a default memberwise initializer. Initializers are described in more detail in [Initialization](). +与结构体不同,类市里没有默认的带参数的初始化方法。对初始化方法的详细说明见[构造过程]()章节。 + +‌ +### Structures and Enumerations Are Value Types +### 结构体和枚举都是值类型 + +A value *type* is a type that is copied when it is assigned to a variable or constant, or when it is passed to a function. +值*类型*的变量在赋值给一个变量、常量或作为参数传给函数时,传递的是该变量值的拷贝。 + +You’ve actually been using value types extensively throughout the previous chapters. In fact, all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes. + +实际上在前面章节我们已经广泛使用了值类型。事实上Swift中几乎所有的基本类型---如整数、浮点数、布尔值、字符串、数组和字典,都是值类型,并在底层都以结构体的方式来实现。 + +All structures and enumerations are value types in Swift. This means that any structure and enumeration instances you create—and any value types they have as properties—are always copied when they are passed around in your code. + +Swift中的所有结构体和枚举都是值类型。这意味着你创建的任何一个结构体或枚举的实例,以及他们的属性在代码中被传递的时候都会被复制。 + +Consider this example, which uses the `Resolution` structure from the previous example: + +让我们来看看这个例子,它使用了先前例子中定义的`Resolution`: + +``` +let hd = Resolution(width: 1920, height: 1080) +var cinema = hd +``` + +This example declares a constant called `hd` and sets it to a `Resolution` instance initialized with the width and height of full HD video (`1920` pixels wide by `1080` pixels high). + +例子中声明了一个名叫`hd`的常量,并将它初始化为全高清视频分辨率(宽`1920`长`1080`)的`Resolution`实例。 + +It then declares a variable called `cinema` and sets it to the current value of `hd`. Because `Resolution` is a structure, a copy of the existing instance is made, and this new copy is assigned to `cinema`. Even though `hd` and `cinema` now have the same width and height, they are two completely different instances behind the scenes. +然后例子中声明了一个变量`cinema`,将`hd`的当前值赋给了它。由于`Resolution`是结构体,赋值操作会生成一个新的实例并传递给`cinema`。因此虽然`hd`和`cinema`有相同的宽高,但它们实际上仍是完全不同的实例。 + +Next, the `width` property of `cinema` is amended to be the width of the slightly-wider 2K standard used for digital cinema projection (`2048` pixels wide and `1080` pixels high): + +接下来,将`cinema`的`width`属性改为数字电影中2K宽屏的标准宽度(宽`2048`像素高`1080`像素): + +``` +cinema.width = 2048 +``` + +Checking the `width` property of `cinema` shows that it has indeed changed to be `2048`: + +检查一下`cinema`的`width`属性的确改成了`2048`: + +``` +println("cinema is now \(cinema.width) pixels wide") +// prints "cinema is now 2048 pixels wide" +``` + +However, the `width` property of the original `hd` instance still has the old value of `1920`: +不过,原来的`hd`实例的`width`属性还是旧值`1920`: + +``` +println("hd is still \(hd.width) pixels wide") +// prints "hd is still 1920 pixels wide" +``` + +When `cinema` was given the current value of `hd`, the *values* stored in `hd` were copied into the new `cinema` instance. The end result is two completely separate instances, which just happened to contain the same numeric values. Because they are separate instances, setting the width of `cinema` to `2048` doesn’t affect the width stored in `hd`. + +当使用`hd`给`cinema`赋值时,`hd`存储的*值*被复制了一份并传递给`cinema`实例。结果`hd`和`cinema`成为了仅仅是属性值相同的两个完全无关的实例。所以将`cinema`的`width`属性改为`2048`不会影响`hd`中的`width`值。 + +The same behavior applies to enumerations: +枚举也有相同的特性: + +``` +enum CompassPoint { + case North, South, East, West +} +var currentDirection = CompassPoint.West +let rememberedDirection = currentDirection +currentDirection = .East +if rememberedDirection == .West { + println("The remembered direction is still .West") +} +// prints "The remembered direction is still .West" +``` + +When `rememberedDirection` is assigned the value of `currentDirection`, it is actually set to a copy of that value. + +当`rememberedDirection`赋值为`currentDirection`时,实际上只是赋值了值拷贝而已。 + +Changing the value of `currentDirection` thereafter does not affect the copy of the original value that was stored in `rememberedDirection`. +改变`currentDirection`的值并不会影响存储了原始值副本的`rememberedDirection`。 +‌ +### Classes Are Reference Types +### 类是引用类型 + +Unlike value types, *reference types* are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead. + +与值类型不同,在赋值给变量、常量或者传参时,引用类型不会被复制,而是传递与当前值相同的引用。 + +Here’s an example, using the `VideoMode` class defined above: +请看这个例子,例子中使用了上文定义的`VideoMode`: + +``` +let tenEighty = VideoMode() +tenEighty.resolution = hd +tenEighty.interlaced = true +tenEighty.name = "1080i" +tenEighty.frameRate = 25.0 +``` + +This example declares a new constant called `tenEighty` and sets it to refer to a new instance of the `VideoMode` class. The video mode is assigned a copy of the HD resolution of `1920` by `1080` from before. It is set to be interlaced, and is given a name of `"1080i"`. Finally, it is set to a frame rate of `25.0` frames per second. + +例子中声明了一个名为`tenEighty`的新常量,并将它赋值为一个新的`VideoMode`实例。视频模式使用了上文的`hd`值的拷贝,其分辨率为`1920`x`1080`。`tenEighty`的`interlaced`为true,`name`为`1080i`,`frameRate`为`25.0`帧每秒。 + +Next, `tenEighty` is assigned to a new constant, called `alsoTenEighty`, and the frame rate of `alsoTenEighty` is modified: + +然后,一个新常量`alsoTenEighty`被赋值为`tenEighty`,`alsoTenEighty`的帧率做了如下修改: + +``` +let alsoTenEighty = tenEighty +alsoTenEighty.frameRate = 30.0 +``` + +Because classes are reference types, `tenEighty` and `alsoTenEighty` actually both refer to the same `VideoMode` instance. Effectively, they are just two different names for the same single instance. + +因为类是引用类型,`tenEighty`和`alsoTenEighty`都指向了同一个`VideoMode`实例。实际上它们是同一个实例的两个不同的名字而已。 + +Checking the `frameRate` property of `tenEighty` shows that it correctly reports the new frame rate of `30.0` from the underlying `VideoMode` instance: + +接下来我们检查一下`tenEighty`的属性`frameRate`,这能说明`VideoMode`实例的帧率真的变成了新值`30.0`。 + +``` +println("The frameRate property of tenEighty is now \(tenEighty.frameRate)") +// prints "The frameRate property of tenEighty is now 30.0" +``` + +Note that `tenEighty` and `alsoTenEighty` are declared as constants, rather than variables. However, you can still change `tenEighty.frameRate` and `alsoTenEighty.frameRate` because the values of the `tenEighty` and `alsoTenEighty` constants themselves do not actually change. `tenEighty` and `alsoTenEighty` themselves do not “store” the `VideoMode` instance—instead, they both refer to a `VideoMode` instance behind the scenes. It is the `frameRate` property of the underlying `VideoMode` that is changed, not the values of the constant references to that `VideoMode`. + +请注意`tenEighty`和`alsoTenEighty`都被声明成了常量而不是变量。但是因为修改其属性的值并不会改变`tenEighty`和`alsoTenEighty`的值,我们还是可以修改`tenEighty.frameRate`和`alsoTenEighty.frameRate`。`tenEighty`和`alsoTenEighty`并不存储`VideoMode`实例,而是引用一个`VideoMode`实例的地址。改变`frameRate`的值只是改变了引用的`VideoMode`实例的属性,并没有改变引用的`VideoMode`的地址。 +‌ +### Identity Operators +### 身份操作符 + +Because classes are reference types, it is possible for multiple constants and variables to refer to the same single instance of a class behind the scenes. (The same is not true for structures and enumerations, because they are value types and are always copied when they are assigned to a constant or variable, or passed to a function.) + +因为类是引用类型,因此允许多个常量和变量都引用同一个类实例。(结构体和枚举却不是,因为它们是值类型,并且在赋值给变量常量或者传值的时候传递的都是值的拷贝。) + +It can sometimes be useful to find out if two constants or variables refer to exactly the same instance of a class. To enable this, Swift provides two identity operators: + +* Identical to (===) +* Not identical to (!==) + +有时候需要判断两个常量或变量是否指向同一个类实例。Swift提供了两个身份操作符来实现这个功能: + + * 指向同一实例(===) + * 指向不同实例(!==) + +Use these operators to check whether two constants or variables refer to the same single instance: + +以下例子使用了这两个操作符来判断两个常量或变量是否是同一实例: + +``` +if tenEighty === alsoTenEighty { + println("tenEighty and alsoTenEighty refer to the same Resolution instance.") +} +// prints "tenEighty and alsoTenEighty refer to the same Resolution instance." +``` + +Note that “identical to” (represented by three equals signs, or ===) does not mean the same thing as “equal to” (represented by two equals signs, or ==): + +提示:三个等号(===)的身份相等与两个等号(==)的值相等操作符是不一样的。 + +* “Identical to” means that two constants or variables of class type refer to exactly the same class instance. +* “Equal to” means that two instances are considered “equal” or “equivalent” in value, for some appropriate meaning of “equal”, as defined by the type’s designer. + + * 身份相等操作符两端的变量或常量比较的是是否从同一个类实例化而来。 + * 值相等操作符比较的则是两端的值或者对象的内存地址是否相等,这更接近我们平常理解的相等比较。 + +When you define your own custom classes and structures, it is your responsibility to decide what qualifies as two instances being “equal”. The process of defining your own implementations of the “equal to” and “not equal to” operators is described in Equivalence Operators. + +每当你定义一个类或者结构体时,你就有义务对两个实例的“相等”标准作出决断。在[比较运算符](#)中会描述如何去对对相等和不等的比较进行实现。 +‌ +### Pointers +### 指针 + +If you have experience with C, C++, or Objective-C, you may know that these languages use pointers to refer to addresses in memory. A Swift constant or variable that refers to an instance of some reference type is similar to a pointer in C, but is not a direct pointer to an address in memory, and does not require you to write an asterisk (*) to indicate that you are creating a reference. Instead, these references are defined like any other constant or variable in Swift. + +如果你之前有过编写C、C++或者Objective-C的经验,你应该会知道这些语言的指针都是对一个内存中的地址做引用的。一个变量或常量在Swift中与C的指针类似引用一个可以被引用的实例,但它不直接指向内存的某一个地址,也不需要在申明引用的变量名前加上星号(*)。在Swift中除此之外,引用的定义与其他语言相同。 + +## Choosing Between Classes and Structures + +You can use both classes and structures to define custom data types to use as the building blocks of your program’s code. + +在定义时你可以在你的代码中同时使用类和结构体来表示合适的数据类型。 + +However, structure instances are always passed by value, and class instances are always passed by reference. This means that they are suited to different kinds of tasks. As you consider the data constructs and functionality that you need for a project, decide whether each data construct should be defined as a class or as a structure. + +他们拥有不同的特性,结构体实例被赋值时始终传递的是值的拷贝,而类实例则始终传递的是引用。将他们根据项目的实际情况去选择定义出合适的数据是代码构建者应该去仔细斟酌的。 + +As a general guideline, consider creating a structure when one or more of these conditions apply: + +通常来说,符合以下一个或多个条件时应该使用结构体去定义数据: + +* The structure’s primary purpose is to encapsulate a few relatively simple data values. +* It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure. +* Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced. +* The structure does not need to inherit properties or behavior from another existing type. + +* 结构体主要目的时用来将少量相关数据进行封装。 +* 当期望实例在赋值时传递的是封装的值的被拷贝而不是仅仅是引用。 +* 当期望数据结构中的只类型也一同拷贝传值而不是传递引用。 +* 这个数据结构不需要去继承别的类。 + +Examples of good candidates for structures include: + +适合使用结构体的场景: + +* The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double. +* A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int. +* A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double. + +In all other cases, define a class, and create instances of that class to be managed and passed by reference. In practice, this means that most custom data constructs should be classes, not structures. + +* 用来描述几何图形的尺寸,包含了浮点类型的宽和高两个属性。 +* 用来描述一个范围,包含一个整型开始值和一个整型长度。 +* 用来描述一个三维坐标系统,包含双精度的x,y和z三个属性。 + +所有其他情况请定义类并使用类的实例以引用方式传递。实际上大多数情况还是应该用类来作为主要的数据结构承载,结构体仅在必要时。 + +## Assignment and Copy Behavior for Collection Types + +## 集合的赋值与拷贝 + +Swift’s Array and Dictionary types are implemented as structures. However, arrays have slightly different copying behavior from dictionaries and other structures when they are assigned to a constant or variable, and when they are passed to a function or method. + +在Swift中Array和Dictionary类型都是使用结构体实现的。然而当Array被赋值给常量、变量或者当作参数传入一个方法或者函数中时拷贝操作与Dictionary和其他结构体略有不同。 + +The behavior described for Array and Dictionary below is different again from the behavior of NSArray and NSDictionary in Foundation, which are implemented as classes, not structures. NSArray and NSDictionary instances are always assigned and passed around as a reference to an existing instance, rather than as a copy. + +以下Array和Dictionary与Foundation框架中的NSArray和NSDictionary的实现方式是有去别的,后者使用类实现,其实例在传递过程中均以引用形式进行传递而不是拷贝。 + +> NOTE +> +> The descriptions below refer to the “copying” of arrays, dictionaries, strings, and other values. Where copying is mentioned, the behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization. + +> 提示 +> +> 以下是对于数组,字典,字符串和其它值的拷贝的描述。 在你的代码中出现拷贝引用的地方便会一直时拷贝引用。然而,Swift只在确实有必要发生拷贝行为的场景下才回执行拷贝操作。为了性能的最优,Swift将会在最合适的实际进行拷贝操作,以达到性能最优的目的,而开发者不关心这方面的性能问题也没关系。 + +### Assignment and Copy Behavior for Dictionaries + +### Dictionary的赋值与拷贝 + +Whenever you assign a Dictionary instance to a constant or variable, or pass a Dictionary instance as an argument to a function or method call, the dictionary is copied at the point that the assignment or call takes place. This process is described in Structures and Enumerations Are Value Types. + +只要将一个字典实例进行赋值或者传参操作,就会产生拷贝行为,在[结构体和枚举都是值类型](#)小节中有详细描述过。 + +If the keys and/or values stored in the Dictionary instance are value types (structures or enumerations), they too are copied when the assignment or call takes place. Conversely, if the keys and/or values are reference types (classes or functions), the references are copied, but not the class instances or functions that they refer to. This copy behavior for a dictionary’s keys and values is the same as the copy behavior for a structure’s stored properties when the structure is copied. + +如果字典实例中所储存的键值是结构体或枚举类型,那么赋值的时候也是拷贝赋值。相反,如果键值是引用类型,那么赋值操作只传递会引用,而不是实例本身的拷贝。在结构体中的键值也具有相同特性。 + +The example below defines a dictionary called ages, which stores the names and ages of four people. The ages dictionary is then assigned to a new variable called copiedAges and is copied when this assignment takes place. After the assignment, ages and copiedAges are two separate dictionaries. + +下面的示例定义了一个名为ages的字典实例,存储了四个人的名字和年龄。ages被赋值给了一个名为copiedAges的新变量时字典实例被重新复制了一份。赋值结束后,ages和copiedAges成为两个相互独立的字典实例。 + +``` +var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] +var copiedAges = ages +``` + +The keys for this dictionary are of type String, and the values are of type Int. Both types are value types in Swift, and so the keys and values are also copied when the dictionary copy takes place. + +这个字典的键是字符串型,值是整型。这两种类型在Swift 中都是值类型,所以当字典被拷贝时,它们也都会被一起拷贝。 + +You can prove that the ages dictionary has been copied by changing an age value in one of the dictionaries and checking the corresponding value in the other. If you set the value for "Peter" in the copiedAges dictionary to 24, the ages dictionary still returns the old value of 23 from before the copy took place: + +我们可以通过改变一个字典中的年龄,然后检查另一个字典中所对应的值,来证明ages字典确实是被拷贝了。如果在copiedAges字典中将Peter的值设为24,那么ages字典中Peter值仍然会返回23: + +``` +copiedAges["Peter"] = 24 +println(ages["Peter"]) +// prints "23" +``` + +### Assignment and Copy Behavior for Arrays +### 数组的赋值与拷贝 + +The assignment and copy behavior for Swift’s Array type is more complex than for its Dictionary type. Array provides C-like performance when you work with an array’s contents and copies an array’s contents only when copying is necessary. + +在Swift 中,数组(Arrays)类型的赋值和拷贝行为要比字典(Dictionary)类型的复杂的多。当操作数组内容时,数组(Array)能提供接近C语言的的性能,并且拷贝行为只有在必要时才会发生。 + +If you assign an Array instance to a constant or variable, or pass an Array instance as an argument to a function or method call, the contents of the array are not copied at the point that the assignment or call takes place. Instead, both arrays share the same sequence of element values. When you modify an element value through one array, the result is observable through the other. + +如果你将一个数组(Arrays)实例赋给一个变量或常量,或者将其作为参数传递给函数或方法,在事件发生时数组的内容不会被拷贝。而且两个数组使用的还是同一套序列。只有当你在一个数组内修改某一元素,修改结果才c会在另一数组显示。 + +For arrays, copying only takes place when you perform an action that has the potential to modify the length of the array. This includes appending, inserting, or removing items, or using a ranged subscript to replace a range of items in the array. If and when array copying does take place, the copy behavior for an array’s contents is the same as for a dictionary’s keys and values, as described in Assignment and Copy Behavior for Dictionaries. + +对数组来说,拷贝行为仅仅当操作有可能修改数组长度时才会发生。这种行为包括了附加(appending),插入(inserting),删除(removing)或者使用范围下标(ranged subscript)去替换这一范围内的元素。只有当数组拷贝确要发生时,数组内容的行为规则与字典中键值的相同,详见[Dictionary的赋值与拷贝](#)。 + +The example below assigns a new array of Int values to a variable called a. This array is also assigned to two further variables called b and c: + +下面的示例将一个整数数组赋给了一个名为a的变量,继而又被赋给了变量b和c: + +``` +var a = [1, 2, 3] +var b = a +var c = a +``` + +You can retrieve the first value in the array with subscript syntax on either a, b, or c: + +我们可以在a,b,c上使用下标语法以得到数组的第一个元素: + +``` +println(a[0]) +// 1 +println(b[0]) +// 1 +println(c[0]) +// 1 +``` + +If you set an item in the array to a new value with subscript syntax, all three of a, b, and c will return the new value. Note that the array is not copied when you set a new value with subscript syntax, because setting a single value with subscript syntax does not have the potential to change the array’s length: + +如果通过下标语法修改数组中某一元素的值,那么a,b,c中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能: + +``` +a[0] = 42 +println(a[0]) +// 42 +println(b[0]) +// 42 +println(c[0]) +// 42 +``` + +However, if you append a new item to a, you do modify the array’s length. This prompts Swift to create a new copy of the array at the point that you append the new value. Henceforth, a is a separate, independent copy of the array. + +然而,当你给a附加新元素时,数组的长度发生改变。 Swift 会创建这个数组的一个拷贝。此后,a将会是一个独立拷贝。 + +If you change a value in a after the copy is made, a will return a different value from b and c, which both still reference the original array contents from before the copy took place: + +拷贝发生后,如果再修改a中元素值的话,a将会返回与b,c不同的结果,因为后两者引用的是原来的数组: + +``` +a.append(4) +a[0] = 777 +println(a[0]) +// 777 +println(b[0]) +// 42 +println(c[0]) +// 42 +``` + +#### Ensuring That an Array Is Unique + +#### 确保数组的唯一性 + +It can be useful to ensure that you have a unique copy of an array before performing an action on that array’s contents, or before passing that array to a function or method. You ensure the uniqueness of an array reference by calling the unshare method on a variable of array type. (The unshare method cannot be called on a constant array.) + +在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一个唯一拷贝的。通过在数组变量上调用unshare方法来确定数组引用的唯一性。(当数组赋给常量时,不能调用unshare方法) + +If multiple variables currently refer to the same array, and you call the unshare method on one of those variables, the array is copied, so that the variable has its own independent copy of the array. However, no copying takes place if the variable is already the only reference to the array. + +如果一个数组被多个变量引用,在其中的一个变量上调用unshare方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不会有拷贝发生。 + +At the end of the previous example, b and c both reference the same array. Call the unshare method on b to make it become a unique copy: + +在上一个示例的最后,b和c都引用了同一个数组。此时在b上调用unshare方法则会将b变成一个唯一个拷贝: + +``` +b.unshare() +``` + +If you change the first value in b after calling the unshare method, all three arrays will now report a different value: + +在unshare方法调用后再修改b中第一个元素的值,这三个数组(a,b,c)会返回不同的三个值: + +``` +b[0] = -105 +println(a[0]) +// 777 +println(b[0]) +// -105 +println(c[0]) +// 42 +``` + +#### Checking Whether Two Arrays Share the Same Elements + +#### 判定两个数组是否共用相同元素 + +Check whether two arrays or subarrays share the same storage and elements by comparing them with the identity operators (=== and !==). + +我们通过使用恒等运算符(identity operators) (=== 和 !==)来判定两个数组或子数组共用相同的储存空间或元素。 + +The example below uses the “identical to” operator (===) to check whether b and c still share the same array elements: + +下面这个示例使用了“等同(identical to)” 运算符(===) 来判定b和c是否共用相同的数组元素: + +``` +if b === c { + println("b and c still share the same array elements.") +} else { + println("b and c now refer to two independent sets of array elements.") +} +// prints "b and c now refer to two independent sets of array elements." +``` + +Alternatively, use the identity operators to check whether two subarrays share the same elements. The example below compares two identical subarrays from b and confirms that they refer to the same elements: + +此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了b的两个相等的子数组,并且确定了这两个子数组都引用相同的元素: + +``` +if b[0...1] === b[0...1] { + println("These two subarrays share the same elements.") +} else { + println("These two subarrays do not share the same elements.") +} +// prints "These two subarrays share the same elements." +``` + +#### Forcing a Copy of an Array + +#### 强制复制数组 + +Force an explicit copy of an array by calling the array’s copy method. This method performs a shallow copy of the array and returns a new array containing the copied items. + +我们通过调用数组的copy方法进行强制显式复制。这个方法对数组进行了浅拷贝(shallow copy),并且返回一个包含此拷贝数组的新数组。 + +The example below defines an array called names, which stores the names of seven people. A new variable called copiedNames is set to the result of calling the copy method on the names array: + +下面这个示例中定义了一个names数组,其包含了七个人名。还定义了一个copiedNames变量,用以储存在names上调用copy方法所返回的结果: + +``` +var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"] +var copiedNames = names.copy() +``` + +You can prove that the names array has been copied by changing an item in one of the arrays and checking the corresponding item in the other. If you set the first item in the copiedNames array to "Mo" rather than "Mohsen", the names array still returns the old value of "Mohsen" from before the copy took place: + +我们可以通过修改数组中某一个元素,并且检查另一个数组中对应元素的方法来判定names数组确已被复制。如果你将copiedNames中第一个元素从"Mohsen"修改为"Mo",则names数组返回的仍是拷贝发生前的"Mohsen": + +``` +copiedNames[0] = "Mo" +println(names[0]) +// prints "Mohsen" +``` + + +> NOTE +> +> If you simply need to be sure that your reference to an array’s contents is the only reference in existence, call the unshare method, not the copy method. The unshare method does not make a copy of the array unless it is necessary to do so. The copy method always copies the array, even if it is already unshared. + +> 提示: +> +> 如果你仅需要确保你对数组的引用是唯一引用,请调用unshare方法,而不是copy方法。unshare方法只在必要时才会创建数组拷贝。copy方法会在任何时候都创建一个新的拷贝,即使已经标记为唯一。 \ No newline at end of file diff --git a/src/chapter2/10_Properties.md b/src/chapter2/10_Properties.md index e69de29..21d6e8f 100644 --- a/src/chapter2/10_Properties.md +++ b/src/chapter2/10_Properties.md @@ -0,0 +1,419 @@ +# 属性 + +属性将值与特定的类,结构体或者枚举关联起来,存储属性将常量或变量值作为实力的一部分保存起来,而计算属性则用来计算(而非存储)一个值。计算属性可以用于类,结构体和枚举,存储属性只能用于类和结构体。 + +存储和计算属性通常用于特定类型的实例。不过,属性也可以用于类型本身,这样的属性被称为类属性。 + +另外,可以为属性定义监听器,以监听属性值的变化,这样就可以在属性值发生变化时触发自定义操作。可以在定义存储属性的时候为其添加属性监听器,也可以为子类继承父类的属性添加监听器。 + + +## 存储属性 + +简单而言,存储属性是一个特定类型实例或结构体中的常量或变量。存储属性可以是变量存储属性(用关键词 `var` 声明),也可以是常量属性(用关键词 `let` 声明)。 + +在定义存储属性时,可以为其指定默认值,详见 《构造过程 - 默认属性值》。在存储属性初始化过程中,依然可以设置和修改它的初始值,甚至修改常量存储属性,详见 《构造过程 - 在构造过程中修改恒定属性》。 + +下面的示例定义了一个名为 `FixedLengthRange` 的结构体,它表示一个整型的范围,其范围长度一旦创建不能改变: + + struct FixedLengthRange { + var firstValue: Int + let length: Int + } + var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) + // 表示整型值的范围为 0, 1, 2 + rangeOfThreeItems.firstValue = 6 + // 修改 firstValue 后, 表示的整型值的范围为: 6, 7, 8 + +`FixedLengthRange` 的实例包含一个名为 `firstValue` 的变量存储属性和一个名为 `length` 常量存储属性。在上面的示例中, `length` 在 `FixedLengthRange` 实例创建时初始化,并且在此后不能被修改,因为它是一个常量属性。 + + +###存储属性与常量实例 + +如果你为一个结构体创建一个实例, 并且把这个实例赋值给一个常量, 那么无论这个实例的属性是否为变量, 其属性都不能被修改。 + + let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) + // this range represents integer values 0, 1, 2, and 3 + // 表示取值范围为 整数的 0, 1, 2, 3 + rangeOfFourItems.firstValue = 6 + // this will report an error, even thought firstValue is a variable property + // 尽管firstValue是变量属性, 对其赋值也会报错 + +因为 `rangeOfFourItems`通过`let`关键词声明, 所以是个常量, 故, 无论它的属性是否为变量, 都无法改变它的属性值。 所以, 当一个实例是常量类型, 那么它的所有属性也会变成常量类型。 + +这种情况对于引用类型(类)并不适用。 如果将某个引用类型的实例赋值给一个常量, 那么依然可以修改该实例的属性。 + +###惰性存储属性 + +惰性存储属性只有在首次调用时才会进行初始化。 通过在存储属性声明前加上 `@lazy` 来声明一个惰性存储属性。 + +> 注意 +在声明一个惰性存储属性时, 必须将其定义为变量(通过`var`声明)。 这样做的原因是, 在实例初始化完成前, 可能无法获得惰性属性的初始值。 相反的, 常量属性的初始值必须在实例初始化完成之前赋值,所以常量属性不能被声明为惰性属性。 + +当某个属性的初始化依赖于其他实例的初始化时,惰性属性是非常有用的。惰性属性在需要复杂计算和耗费时间较长的属性初始化时,也是非常有用的,因为它可以在需要时再进行计算和初始化。 + +下面这个例子中,演示了在一个复杂类的初始化过程中,如何通过惰性存储属性来避免不必要的初始化。示例中定义了两个类:`DataImporter`, `DataManager`(代码片段)。 + + class DataImporter { + /* + DataImporter is a class to import data from an external file. + The class is assumed to take a non-trivial amount of time to initialize. + + DataImporter 是一个可以从外部文件导入数据的类 + 该类的初始化会消耗很长一段时间 + + */ + var fileName = "data.txt" + // the DataImporter class would provide data importing functionality here + // 这里是DataImporter类导入数据的代码 + } + + class DataManager { + @lazy var importer = DataImporter() + var data = String[]() + // the DataManager class would provide data management functionality here + // DataManager类数据管理功能的代码 + } + + let manager = DataManager() + manager.data += "Some data" + manager.data += "Some more data" + // the DataImporter instance for the importer property has not yet been created + // importer 实例尚未初始化 + +`DataManager` 类拥有一个名为 `data` 的存储属性,该属性是一个空的字符串数组。虽然剩余的功能代码没有展示出来,不过 `DataManager` 类的目的是提供管理和访问该字符串数组的功能。 + +`DataManager`类的一个功能是从文件中导入数据。 该功能由 `DataImporter` 类提供,需要花费很长时间进行初始化。原因是 `DataImporter` 类的实例在初始化的时候需要读取文件并将文件内容写入内存。 + +对于 `DataManager` 类来说,无论是从文件中导入了数据,都不影响它管理自己的数据,所以没必要在它自己初始化的时候就去创建 `DataManager` 实例。更好的做法是,将 `DataImporter` 实例在第一次使用的时候初始化。 + +因为使用了 `@lazy`,故 `DataImporter` 的实例 `importer` 只会在 `importer`的实例第一次被访问的时候才会初始化,例如访问它的 `fileName` 属性: + +println(manager.importer.fileName) +// the DataImporter instance for the importer property has now been created +//此时, `DataImporter` 的实例 `importer` 才被创建 +// prints "data.txt" + +###存储属性和实例变量 + +`Object-C`中类的实例对象有两种存储值和引用的方法。除了属性,还可以使用实例变量保存值。 + +在`Swift`中没有实例变量,`Swift` 将这些概念统一为了属性。这样避免了在不同上下文中值的不同访问方式的混淆,并且使属性声明简单明了。所有的属性信息:名称,类型,内存管理特征都包含在类型定义中。 + +##计算属性 + +除了存储属性外,类,结构体,枚举还可以定义计算属性。计算属性不能存储值,而是通过 `getter` 方法和 `setter` 方法(可选)间接的设置其他属性和值。 + + struct Point { + var x = 0.0, y = 0.0 + } + struct Size { + var width = 0.0, height = 0.0 + } + struct Rect { + var origin = Point() + var size = Size() + var center: Point { + get { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set(newCenter) { + origin.x = newCenter.x - (size.width / 2) + origin.y = newCenter.y - (size.height / 2) + } + } + } + var square = Rect(origin: Point(x: 0.0, y: 0.0), + size: Size(width: 10.0, height: 10.0)) + let initialSquareCenter = square.center + square.center = Point(x: 15.0, y: 15.0) + println("square.origin is now at (\(square.origin.x), \(square.origin.y))") + // prints "square.origin is now at (10.0, 10.0)" + // 输出 "square.origin is now at (10.0, 10.0)" + +这个示例定义了三个结构体来表示一个几何形状: + +`Point` 封装了坐标(x, y)。 + +`Size` 封装了宽度和高度。 + +`Rect` 用坐标原点和大小定义了一个矩形。 + +`Rect` 结构体提供了一个名为 `center` 的计算属性。矩形的中心点总是可以通过它的原点坐标和大小计算出来,所以你没有必要保存一个确切的矩形中心点的值。这里的 `Rect` 为一个名为 `center` 的计算属性定义了自定义的 `getter` 和 `setter` 方法,以此来设置矩形的中心点。 + +例子中接下来创建了一个名为 `square` 的 `Rect` 实例,`point` 初始值为(0,0), `width` 和 `height` 都是10,在下图中用蓝色正方形表示。 + +然后通过点运算符(`square.center`)访问了 `square` 实例的 `center` 属性,此时会触发 `center` 的 `getter` 方法,并返回当前的属性值。和直接返回值不同, `getter` 方法会计算并返回最新的属性值。从上面的代码可以看出, `getter` 方法正确的返回了中心点 `(5, 5)`。 + +接着为 `center` 属性设置了新值 `(15, 15)`,在下图中可以看出 `square` 向右上移动到了一个新的位置(橙色区域)。在设置 `center` 属性时调用了它的 `setter` 方法,修改了 `origin` 的 `x,y`值,并且改变了 `square` 的位置。 + +![Alt text](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/computedProperties_2x.png) + + +###Setter声明简写 + +如果没有给属性的 `setter` 方法的新值指定名称,那么可以使用默认值 `newValue` 。下面是 `Rect` 结构体的简写形式: + + struct AlternativeRect { + var origin = Point() + var size = Size() + var center: Point { + get { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set { + origin.x = newValue.x - (size.width / 2) + origin.y = newValue.y - (size.height / 2) + } + } + } + + +###只读计算属性 + +只有 `getter` 没有 `setter` 的计算属性是只读计算属性。只读计算属性可以通过点操作符访问,但不能为其设置其他值。 + +> 注意 + +> 必须使用 `var` 关键词定义计算属性,包括只读计算属性,因为它们的值是可能改变的。 `let` 关键词只用于常量属性,其值在初始化后不可改变。 + +必须使用var关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。 + +只读计算属性的声明可以去掉get关键词和花括号: + + struct Cuboid { + var width = 0.0, height = 0.0, depth = 0.0 + var volume: Double { + return width * height * depth + } + } + + let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) + println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") + // prints "the volume of fourByFiveByTwo is 40.0" + // 输出 "the volume of fourByFiveByTwo is 40.0" + +这个示例定义了一个名为 `Cuboid` 的结构体,表示一个3D的立方体,有 `width`,`height`,`depth`等属性。它还有一个名为 `volume` 的只读计算属性用来计算并返回 `cuboid` 当前的体积。我们没有必要去设置 `volume` 的值,因为 `volume` 的值可以通过 `width`,`height`,`depth`计算出来。所以比较合适的做法是,提供一个只读计算属性让用户可以获得当前的 `volume` 。 + +###属性观察者 + +属性观察者用来观察并响应属性值的变化。在为属性赋值时,无论新值是否与原值相同,都会触发属性的观察者。 + +可以为除了惰性属性外的其他任何存储属性定义观察者。通过属性重写,可以在子类中为它的父类属性(无论是存储或是计算属性)添加观察者。属性重写在《构造过程 - 构造方法的继承与重写》一章中有详细介绍。 + +> 注意 +> +> 不需要为非重写计算属性定义观察者,因为你可以直接使用计算属性的`setter`来完成。 + +可以通过两种方式为属性定义观察者: +* `willSet` 在值发生改变之前调用 +* `didSet` 在值发生改变之后调用 + +`willSet` 会将新属性值作为常量参数,并且可以为该常量参数指定名称。如果没有为该参数指定名称,那么会使用默认的参数名称 `newValue`。 + +与 `willSet` 类似,`didSet` 会将原属性值作为常量参数。同样可以为参数指定名称或者使用默认值 `oldValue`。 + +> 注意 +> +> `willSet` 和 `didSet` 在属性初始化时不会被调用。 + +这里有一个使用了 `willSet` 和 `didSet` 的示例。示例中定义了一个名为 `StepCounter` 的类,用来统计当人步行时的总步数,通过使用计步器等装置可以用这个类追踪人在日常工作中的运动量。 + + class StepCounter { + var totalSteps: Int = 0 { + willSet(newTotalSteps) { + println("About to set totalSteps to \(newTotalSteps)") + } + didSet { + if totalSteps > oldValue { + println("Added \(totalSteps - oldValue) steps") + } + } + } + } + let stepCounter = StepCounter() + stepCounter.totalSteps = 200 + // About to set totalSteps to 200 + // Added 200 steps + // 输出 About to set totalSteps to 200 + // 输出 Added 200 steps + stepCounter.totalSteps = 360 + // About to set totalSteps to 360 + // Added 160 steps + // 输出 About to set totalSteps to 360 + // 输出 Added 160 steps + stepCounter.totalSteps = 896 + // About to set totalSteps to 896 + // Added 536 steps + // 输出 About to set totalSteps to 896 + // 输出 Added 536 steps + +`StepCounter`定义了一个 `int` 类型的属性 `totalSteps`。 `totalSteps` 包含了两个观察者`willSet`和`didSet`。 + +当`totalSteps`的值改变时(不论新值是否与原值相同),`willSet` 和 `didSet` 都会被调用。 + +示例中的`willSet`使用了一个名为`newTotalSteps`的参数接收新值。在这个例子中只是简单的将新值输出。 + +`didSet`观察者会在`totalSteps`的值被修改后调用。它将`totalSteps`的新值与原值做比较,如果新值大于原值,则会输出新增了多少步。`didSet`观察者没有指定参数名,所以使用默认参数名`oldValue`。 + +> 注意 + +> 如果在`didSet`中给属性设置新值,那么新值会替换刚刚设置的值。 + + +##全局变量和局部变量 + +上面关于属性的计算和观察功能对于全局变量和局部变量同样适用。全局变量定义在所有函数,方法,闭包,类型之外。局部变量定义在函数,方法或闭包内部。 + +在前面的章节中全局和局部变量都是存储变量,类似于存储属性,它为特定类型的值提供存储空间,并允许对其进行读写。 + +另外,还可以在全局或局部作用域中定义计算变量,或者为存储变量定义观察者。计算变量用来计算而非存储一个值,声明方式和计算属性一样。 + +> 注意 +> +> 和惰性存储属性的方式类似,全局常量和变量总是延迟计算的。不同的是,全局常量和变量不需要使用`@lazy`属性进行声明。 +> +> 局部常量和变量则绝不会延迟计算。 + + +##类型属性 + +实例属性属于一个特定类型的实例。每次创建该类型的实例,它都拥有自己独立的一组属性,与其他实例对象无关。 + +还可以定义属于类型自身的属性。不论该类型有多少实例,这些属性都只有一份。这种属性被称为类型属性。 + +类型属性用于定义所有特定的类型实例都可以使用的值,比如所有实例都可以使用同一个常量属性(类似于`C`中的静态常量),或者就像所有的实例都可以使用全局变量属性(类似于`C`中的静态常量)。 + +对于值类型(结构和枚举),可以定义存储和计算类型的属性。对于类,则只能定义计算类型的属性。 + +值类型的存储类型属性可以是变量和常量。计算类型属性和计算实例属性相同,通常声明为变量属性。 + +> 注意 +> +> 与存储实例属性不同,必须为存储类型属性定义默认值。原因是类型本身没有一个可以在初始化时为类型属性赋值的构造器。 + + +###类型属性语法 + +在`C`和`Objective-C`中,只能使用全局静态变量来定义依赖于某个属性的变量或常量。但在`Swift`中,类型属性可以作为类型定义的一部分,它的作用域也在类型的范围内。 + +使用`static`关键词定义值类型的类型属性,`class`类型的类型属性用关键词`class`声明。下面的示例演示了存储类型属性和计算类型属性的语法: + + + struct SomeStructure { + static var storedTypeProperty = "Some value." + static var computedTypeProperty: Int { + // return an Int value here + // 这里将返回一个`Int`的值 + } + } + enum SomeEnumeration { + static var storedTypeProperty = "Some value." + static var computedTypeProperty: Int { + // return an Int value here + // 这里将返回一个`Int`的值 + } + } + class SomeClass { + class var computedTypeProperty: Int { + // return an Int value here + // 这里将返回一个`Int`的值 + } + } + + +> 注意 +> +> 上面的计算类型属性的示例都是只读的,仍然可以定义可读写的计算类型属性。 + + +###查询和设置类型属性 + + +就像实例属性,类型属性通过点操作符查询和设置。不过,类型属性的是通过类型自身查询和设置,而非类型的实例: + + println(SomeClass.computedTypeProperty) + // prints "42" + // 输出 "42" + + println(SomeStructure.storedTypeProperty) + // prints "Some value." + // 输出 "Some value." + SomeStructure.storedTypeProperty = "Another value." + println(SomeStructure.storedTypeProperty) + // prints "Another value." + // 输出 "Another value." + + +下面的示例定义了一个结构体和两个类型属性来为声道音量建模。每一个声道的音量范围是0到10。 + +下图演示了如何将两个声道合并为一个立体声道。当某个声道的音量值是0时,所有灯都不会亮。当音量值是10时,所有灯都会亮起。下图中,左侧的音量值为9,右侧的音量值为7: + +![Alt text](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png) + +上面的声道通过`AudioChannel`的结构体实例表示如下: + + struct AudioChannel { + static let thresholdLevel = 10 + static var maxInputLevelForAllChannels = 0 + var currentLevel: Int = 0 { + didSet { + if currentLevel > AudioChannel.thresholdLevel { + // cap the new audio level to the threshold level + // 将音量值设置为最大值 + currentLevel = AudioChannel.thresholdLevel + } + if currentLevel > AudioChannel.maxInputLevelForAllChannels { + // store this as the new overall maximum input level + // 将音量值设置为当前值 + AudioChannel.maxInputLevelForAllChannels = currentLevel + } + } + } + } + + +`AudioChannel`定义了两个存储属性。首先,定义了音量最大值`thresholdLevel`,它是一个对所有实例可见的常量值。如果音量大于10,那么就取上限值10。 + +第二个类型属性是一个名为`maxInputLevelForAllChannels`的变量存储属性,用来表示所有实例的最大音量值。初始值为0。 + +`AudioChannel`结构体还定义了一个实例属性`currentLevel`,用来表示当前声道的音量值,取值0到10。 + +`currentLevel`的值在每次设置时都会通过`didSet`进行两种检查: + +* 如果`currentLevel`的新值大于允许的最大值`thresholdLevel`,则属性监听器将`currentLevel`设置为`thresholdLevel`。 +* 如果`currentLevel`的新值大于之前所有`AudioChannel`实例的值。那么属性监听器会将新值保存在静态属性`maxInputLevelForAllChannels`中。 + + +> 注意 +> +> 在第一次检查过程中,`didSet`监听器将`currentLevel`设置为了不同的值,但此时不会再次调用属性监听器。 + +可以使用`AudioChannel`创建两个声道实例:`leftChannel`和`rightChannel`: + + var leftChannel = AudioChannel() + var rightChannel = AudioChannel() + +如果将`currentLevel`的左声道的值置为7,则可以看到类型属性`maxInputLevelForAllChannels`也更新为了7: + + leftChannel.currentLevel = 7 + println(leftChannel.currentLevel) + // prints "7" + // 输出 "7" + println(AudioChannel.maxInputLevelForAllChannels) + // print "7" + // 输出 "7" + +如果想将`currentLevel`的右声道设置为11,你会发现右声道的`currentLevel`值被设置为了10,同时`maxInputLevelForAllChannels` 也更新为10。 + + + rightChannel.currentLevel = 11 + println(rightChannel.currentLevel) + // prints "10" + // 输出 "10" + println(AudioChannel.maxInputLevelForAllChannels) + // prints "10" + // 输出 "10" \ No newline at end of file diff --git a/src/chapter2/11_Methods.md b/src/chapter2/11_Methods.md index e69de29..ccc3213 100644 --- a/src/chapter2/11_Methods.md +++ b/src/chapter2/11_Methods.md @@ -0,0 +1,438 @@ +# 方法 (Methods) + +Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. Classes, structures, and enumerations can also define type methods, which are associated with the type itself. Type methods are similar to class methods in Objective-C. + +方法是指和某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法, 这些实例方法可以将特定任务和功能封装到一个指定类型的实例中。类、结构体、枚举也可以定义类型方法, 这些类型方法仅和类型本身有关联。 类型方法和 Objective-C 中的类方法(class methods)很相似。 + +The fact that structures and enumerations can define methods in Swift is a major difference from C and Objective-C. In Objective-C, classes are the only types that can define methods. In Swift, you can choose whether to define a class, structure, or enumeration, and still have the flexibility to define methods on the type you create. + +事实上,Swift 中的结构体和枚举也可以定义方法, 这也是和 C、Objective-C 一个主要区别。在 Objective-C 中,只有类(Class)才能定义方法。在 Swift 中,你可以选择在类、结构体、枚举、甚至是你自己定义的类型中,去定义方法。 + +## 实例方法(Instance Methods) + +Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose. Instance methods have exactly the same syntax as functions, as described in Functions. + +实例方法是属于某个特定类、结构体、枚举实例的函数。实例方法提供获取和修改实例变量的途径,又或是提供与实例用途相关的功能,以满足这些实例的功能需要。实例方法的语法和函数一样,参考`函数`这一章节。 + +You write an instance method within the opening and closing braces of the type it belongs to. An instance method has implicit access to all other instance methods and properties of that type. An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance. + +实例类型要写在类型的大括号中。实例方法可以隐式地访问这个类型的所有实例方法和属性。实例方法只能被它所属类型的实例调用,不能在实例之外被独立调用。 + +Here’s an example that defines a simple `Counter` class, which can be used to count the number of times an action occurs: + +这里有一个例子,定义了一个可以用来记录动作发生次数的计数器 Counter 类 + +``` + +class Counter { + var count = 0 + func increment() { + count++ + } + func incrementBy(amount: Int) { + count += amount + } + func reset() { + count = 0 + } +} + +``` + +The Counter class defines three instance methods: + +`Counter` 类定义了三个实例方法: + +* `increment` increments the counter by 1. + +* `increment` * 计数器每次递增 +1。 + +* `incrementBy(amount: Int)` increments the counter by an specified integer amount. + +* `incrementBy(amount: Int)` 计数器根据给定的 amount 值增量 + +* `reset` resets the counter to zero. + +* `reset` 重置计数器归零。 + +The `Counter` class also declares a variable property, count, to keep track of the current counter value. + +`Counter` 类也声明了一个变量属性 count,用来记录当前的计数器的值。 + +You call instance methods with the same dot syntax as properties: + +你可以像调用属性一样,通过相同的点语法 (dot syntax) 来调用实例方法: + +``` + +let counter = Counter() +// the initial counter value is 0 +// 计数器初始值为0 +counter.increment() +// the counter's value is now 1 +// 计数器的值现在是1 +counter.incrementBy(5) +// the counter's value is now 6 +// 计数器的值现在是6 +counter.reset() +// the counter's value is now 0 +// 计数器的值现在是0 + +``` + +## 方法的内部参数名和外部参数名(Local and External Parameter Names for Methods) + +Function parameters can have both a local name (for use within the function’s body) and an external name (for use when calling the function), as described in External Parameter Names. The same is true for method parameters, because methods are just functions that are associated with a type. However, the default behavior of local names and external names is different for functions and methods. + +函数参数可以有一个内部名称(用于函数内部)和一个外部名称(用于函数调用),正如`函数`章节里`外部参数名`描述的那样。方法参数和函数参数一样,因为方法只不过是和类型相关联的函数罢了。然而,内部名称和外部名称在函数和方法内部的默认行为却是不一样的。 + +Methods in Swift are very similar to their counterparts in Objective-C. As in Objective-C, the name of a method in Swift typically refers to the method’s first parameter using a preposition such as with, for, or by, as seen in the incrementBy method from the preceding Counter class example. The use of a preposition enables the method to be read as a sentence when it is called. Swift makes this established method naming convention easy to write by using a different default approach for method parameters than it uses for function parameters. + +Swift 里的方法和 Objective-C 里的方法是非常相似的。和 Objective-C 中一样, Swift 里的方法通常使用 with、for、by 之类的介词来指向第一个参数,比如计数器例子里的 `incrementBy`。介词的使用可以使这个方法的调用如同阅读一个句子。与函数参数不同的是,对于方法参数,Swift 使用了一套不同的默认处理规则,使得方法的命名规则写起来更加简单。 + +Specifically, Swift gives the first parameter name in a method a local parameter name by default, and gives the second and subsequent parameter names both local and external parameter names by default. This convention matches the typical naming and calling convention you will be familiar with from writing Objective-C methods, and makes for expressive method calls without the need to qualify your parameter names. + + +具体来说,Swift 默认第一个参数名仅作为内部名称,第二个和之后的参数名作为内部名称和外部名称。这个约定和典型的命名和调用方法的约定相匹配,你会觉得和写 Objective-C 方法十分相似,而且这个约定不需要限制你的参数名。 + +Consider this alternative version of the Counter class, which defines a more complex form of the incrementBy method: + +我们来看看计数器 `Counter` 的另外一个版本,这个版本定义了更加复杂的 `incrementBy` 方法: + +``` + +class Counter { + var count: Int = 0 + func incrementBy(amount: Int, numberOfTimes: Int) { + count += amount * numberOfTimes + } +} + +``` + +This `incrementBy` method has two parameters—`amount` and `numberOfTimes`. By default, Swift treats `amount` as a local name only, but treats `numberOfTimes` as both a local and an external name. You call the method as follows: + +这个 `incrementBy` 方法有两个参数 -- `amount` 和 `numberOfTimes`。根据默认约定,Swift 只把 `amount` 看做内部变量,但是把 `numberOfTimes` 同时作为内部名称和外部名称。你可以这样调用: + +``` + +let counter = Counter() +counter.incrementBy(5, numberOfTimes: 3) +// counter value is now 15 +// 计数器的值现在是15 + +``` + +You don’t need to define an external parameter name for the first argument value, because its purpose is clear from the function name `incrementBy`. The second argument, however, is qualified by an external parameter name to make its purpose clear when the method is called. + +你不需要为第一个参数值定义一个外部参数名称,因为这个参数值的意图已经被函数名 `incrementBy` 表达得很清楚了。但是第二个参数被外部参数名限制了,这是为了在方法调用的时候,表达出更清晰的意图。 + +This default behavior effectively treats the method as if you had written a hash symbol (`#`) before the `numberOfTimes` parameter: + +这种默认行为使方法处理变得高效,就好像你在 `numberOfTimes` 前加了一个 `#` 号: + +``` + +func incrementBy(amount: Int, #numberOfTimes: Int) { + count += amount * numberOfTimes +} + +``` + +The default behavior described above mean that method definitions in Swift are written with the same grammatical style as Objective-C, and are called in a natural, expressive way. + +以上描述的默认行为表,Swift 中的方法定义和 Objective-C 有着同样的语法风格,都使用了自然的表达手法。 + +## 修改方法的外部参数名称行为(Modifying External Parameter Name Behavior for Methods) + +Sometimes it’s useful to provide an external parameter name for a method’s first parameter, even though this is not the default behavior. You can either add an explicit external name yourself, or you can prefix the first parameter’s name with a hash symbol to use the local name as an external name too. + +有时候,为方法的第一个参数提供一个外部参数名称是有用的,即使这不是个默认行为。你可以自己增加一个明确的外部名称,也可以给第一个参数增加一个 # 的前缀,使得内部名称可以作为外部名称使用。 + +Conversely, if you do not want to provide an external name for the second or subsequent parameter of a method, override the default behavior by using an underscore character (`_`) as an explicit external parameter name for that parameter. + +相反地,如果你不想为方法里的第二个及后续参数提供外部名称,可以使用 `_` 作为显式的外部参数名称来重写这个规则。 + +### The self Property + +Every instance of a type has an implicit property called `self`, which is exactly equivalent to the instance itself. You use this implicit self property to refer to the current instance within its own instance methods. + +每个类型的实例都有一个隐式的属性,叫做 `self`,这个变量和实例自身等价。你可以使用 self 来指代当前实例。 + +The `increment` method in the example above could have been written like this: + +例子中的 `increment` 可以写成这样: + +``` + +func increment() { + self.count++ +} + +``` + +In practice, you don’t need to write `self` in your code very often. If you don’t explicitly write `self`, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of `count` (rather than `self.count`) inside the three instance methods for `Counter`. + +事实上,你不需要经常在代码里写 `self`。如果你没有明确写 `self`,无论你调用了已知属性或是方法,Swift 会假设你调用的是当前实例的属性或者方法。这个假设已经在第一个例子里证实了, `Counter` 里直接调用了 `count` 而不是通过 `self.count`。 + +The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the implicit `self` property to distinguish between the parameter name and the property name. + +当一个实例方法的参数名和这个实例里的属性名重复时,这个规则怎么处理呢?在这种情况下,参数名拥有更高的优先级,同时属性名的引用需要使用更清晰明确的方法。可以使用隐式的 `self` 来区分参数名和属性名。 + +Here, `self` disambiguates between a method parameter called `x` and an instance property that is also called `x`: + +这里的 `self` 避免了方法参数名为 `x` 和实例属性名为 `x` 的混淆冲突: + +``` + +struct Point { + var x = 0.0, y = 0.0 + func isToTheRightOfX(x: Double) -> Bool { + return self.x > x + } +} +let somePoint = Point(x: 4.0, y: 5.0) +if somePoint.isToTheRightOfX(1.0) { + println("This point is to the right of the line where x == 1.0") +} +// prints "This point is to the right of the line where x == 1.0 + +``` + +Without the `self` prefix, Swift would assume that both uses of `x` referred to the method parameter called `x`. + +如果没有 `self` 这个前缀,Swift 会假设这两个 `x` 都指代参数 `x`。 + +### 修改实例方法中的值类型 (Modifying Value Types from Within Instance Methods) + +Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods. + +结构体和枚举都是值类型。值类型的属性默认不能在实例方法中被修改。 + +However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to `mutating` behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit `self` property, and this new instance will replace the existing one when the method ends. + +然而,如果你需要通过特殊方法来修改结构体或枚举的属性,你得为那个方法使用 `变异` 这个用法。这个方法可以在方法内部改变它的属性,并且在这个方法结束时,它产生的任何改变都会保留在原来的结构体实例中。方法也可以指定一个完整全新的实例替换隐式的 `self` 属性,从而在方法结束的时候,会替换原来的实例。 + +You can opt in to this behavior by placing the `mutating` keyword before the `func` keyword for that method: + +你可以通过在 `func` 前增加 `mutating` 来调用 `变异` + +``` + +struct Point { + var x = 0.0, y = 0.0 + mutating func moveByX(deltaX: Double, y deltaY: Double) { + x += deltaX + y += deltaY + } +} +var somePoint = Point(x: 1.0, y: 1.0) +somePoint.moveByX(2.0, y: 3.0) +println("The point is now at (\(somePoint.x), \(somePoint.y))") +// prints "The point is now at (3.0, 4.0) + +``` + +The `Point` structure above defines a mutating `moveByX` method, which moves a `Point` instance by a certain amount. Instead of returning a new point, this method actually modifies the point on which it is called. The `mutating` keyword is added to its definition to enable it to modify its properties. + +上面这个 `Point` 结构体定义了一个变异的 `moveByX` 方法,这个方法可以将 `Point` 实例按照某个数值移动。这个方法在调用的时候修改了这个实例的坐标数据,而不是替换了这个实例。为了可以修改它的属性,在这个方法定义前增加了 `mutating` 。 + +Note that you cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties, as described in Stored Properties of Constant Structure Instances: + +注意,你不能在一个作为常量的结构体实例上使用编译方法。因为它的属性已经无法更改,即使它们是可变属性。正如 `属性` 这个章节中的 `常量结构体实例的属性`中描述的那样。 + +``` + +let fixedPoint = Point(x: 3.0, y: 3.0) +fixedPoint.moveByX(2.0, y: 3.0) +// this will report an error +// 这里会报错 + +``` + +### 用变异方法给 self 赋值(Assigning to self Within a Mutating Method) + +Mutating methods can assign an entirely new instance to the implicit `self` property. The `Point` example shown above could have been written in the following way instead: + +变异方法可以将一个全新的实例赋值给 `self`。上面的 `Point` 例子可以被重写为如下: + +``` + +struct Point { + var x = 0.0, y = 0.0 + mutating func moveByX(deltaX: Double, y deltaY: Double) { + self = Point(x: x + deltaX, y: y + deltaY) + } +} + +``` + +This version of the mutating `moveByX` method creates a brand new structure whose `x` and `y` values are set to the target location. The end result of calling this alternative version of the method will be exactly the same as for calling the earlier version. + +这个版本的变异 `moveByX` 重新创造了一个指向新坐标 `x` 和 `y` 的实例。调用这个版本的方法,执行结果和上一个版本相同。 + +Mutating methods for enumerations can set the implicit `self` parameter to be a different member from the same enumeration: + +枚举类型的变异方法可以将 `self` 赋值为相同枚举类型中的另外一个成员。 + +``` + +enum TriStateSwitch { + case Off, Low, High + mutating func next() { + switch self { + case Off: + self = Low + case Low: + self = High + case High: + self = Off + } + } +} +var ovenLight = TriStateSwitch.Low +ovenLight.next() +// ovenLight is now equal to .High +ovenLight.next() +// ovenLight is now equal to .Off + +``` + +This example defines an enumeration for a three-state switch. The switch cycles between three different power states (`Off`, `Low` and `High`) every time its `next` method is called. + +这个例子定义了一个有三种状态的枚举类型。每次调用 `next` 方法都会在这三种状态中切换一次。 + +## 类型方法 (Type Methods) + +Instance methods, as described above, are methods that are called on an instance of a particular type. You can also define methods that are called on the type itself. These kinds of methods are called type methods. You indicate type methods for classes by writing the keyword `class` before the method’s `func` keyword, and type methods for structures and enumerations by writing the keyword `static` before the method’s `func` keyword. + +上面描述的实例方法,都是在一个指定类型的实例中调用的。你也可以定义可以被类型自身调用的方法。这些类型的方法被称为类型方法。在类中,你可以通过在 `func` 关键词前增加 `class` 前缀来指定这个方法为类型方法。在结构体和枚举中,你可以通过在 `func` 关键词前增加 `static` 前缀来指定这个方法为类型方法 + +> NOTE 提示 + +> In Objective-C, you can define type-level methods only for Objective-C classes. In Swift, you can define type-level methods for all classes, structures, and enumerations. Each type method is explicitly scoped to the type it supports. + +> 在 Objective-C 中,你可以给 Objective-C 类指定一个类型级别的方法。在 Swift 中,你可以为所有类、结构体、枚举指定类型级别的方法。每个类型方法仅显式包含在它支持的类中 + +Type methods are called with dot syntax, like instance methods. However, you call type methods on the type, not on an instance of that type. Here’s how you call a type method on a class called `SomeClass`: + +和实例方法一样,类型方法也通过点语法 (dot syntax) 被调用。然后你可以直接在类型上调用这个方法,而不是在一个类型的实例上。下面的 `SomeClass` 会表明如何调用类型方法: + +``` + +class SomeClass { + class func someTypeMethod() { + // type method implementation goes here + } +} +SomeClass.someTypeMethod() + +``` + +Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type. For structures and enumerations, this means that you can use self to disambiguate between static properties and static method parameters, just as you do for instance properties and instance method parameters. + +在类型方法体中,隐式 self 属性指代类型本身,而不是某个类型的实例。对于结构体和枚举来说,这意味着你可以使用 self 来解除静态属性和静态方法参数之间的混淆,就如同实例属性和实例方法参数那样。 + +More generally, any unqualified method and property names that you use within the body of a type method will refer to other type-level methods and properties. A type method can call another type method with the other method’s name, without needing to prefix it with the type name. Similarly, type methods on structures and enumerations can access static properties by using the static property’s name without a type name prefix. + +更普遍地说,在类型方法体内,没有显式调用的方法和属性都会被指代为其他类型级别的方法和属性。类型方法可以通过其他方法名来调用其他类型方法,而不需要在方法名前增加类型名称。同样的,结构体和枚举的类型方法也可以通过静态属性名直接获取静态属性,而不需要在属性名前增加类型名称。 + +The example below defines a structure called `LevelTracker`, which tracks a player’s progress through the different levels or stages of a game. It is a single-player game, but can store information for multiple players on a single device. + +下面这个例子指定了 `LevelTracker` 结构体。它可以跟踪一个游戏中成员的不同关卡过程。这是一个单玩家游戏,但是可以在单个设备中存储多个玩家的游戏数据。 + +All of the game’s levels (apart from level one) are locked when the game is first played. Every time a player finishes a level, that level is unlocked for all players on the device. The `LevelTracker` structure uses static properties and methods to keep track of which levels of the game have been unlocked. It also tracks the current level for an individual player. + +游戏刚开始的时候,所有游戏关卡(除了第一关)都被锁定。每次一个玩家结束了一个关卡,这个关卡会为这个设备上的所有玩家解锁。`LevelTracker` 结构体使用了静态属性和方法来记录哪个游戏关卡被解锁,同时记录为每个成员玩家当前所在关卡。 + +``` + +struct LevelTracker { + static var highestUnlockedLevel = 1 + static func unlockLevel(level: Int) { + if level > highestUnlockedLevel { highestUnlockedLevel = level } + } + static func levelIsUnlocked(level: Int) -> Bool { + return level <= highestUnlockedLevel + } + var currentLevel = 1 + mutating func advanceToLevel(level: Int) -> Bool { + if LevelTracker.levelIsUnlocked(level) { + currentLevel = level + return true + } else { + return false + } + } +} + +``` + +The `LevelTracker` structure keeps track of the highest level that any player has unlocked. This value is stored in a static property called `highestUnlockedLevel`. + +`LevelTracker` 结构体跟踪记录被任何一个玩家解锁的最高关卡。这个值被保存在静态属性 `highestUnlockedLevel` 中。 + +`LevelTracker` also defines two type functions to work with the `highestUnlockedLevel` property. The first is a type function called `unlockLevel`, which updates the value of `highestUnlockedLevel` whenever a new level is unlocked. The second is a convenience type function called `levelIsUnlocked`, which returns `true` if a particular level number is already unlocked. (Note that these type methods can access the `highestUnlockedLevel` static property without your needing to write it as `LevelTracker.highestUnlockedLevel`.) + +`LevelTracker` 也指定了两个与 `highestUnlockedLevel` 一起工作的类型函数。第一个类型函数 `unlockLevel` ,当新关卡解锁时,用来更新 `highestUnlockedLevel` 。第二个是 `levelIsUnlocked`,如果某个关卡编号已经解锁,返回 `true`。(注意,这些类型方法在获取 `highestUnlockedLevel` 属性时,不需要写成 `LevelTracker.highestUnlockedLevel` 也能直接获取 `highestUnlockedLevel` 这个静态属性。) + +In addition to its static property and type methods, `LevelTracker` tracks an individual player’s progress through the game. It uses an instance property called `currentLevel` to track the level that a player is currently playing. + +除了静态属性和类型方法之外,`LevelTracker` 也跟踪每个独立玩家在游戏中的进程。通过一个实例属性 `currentLevel` 来记录玩家当前的关卡。 + +To help manage the `currentLevel` property, `LevelTracker` defines an instance method called `advanceToLevel`. Before updating `currentLevel`, this method checks whether the requested new level is already unlocked. The `advanceToLevel` method returns a Boolean value to indicate whether or not it was actually able to set `currentLevel`. + +为了帮助管理 `currentLevel` 属性,`LevelTracker` 指定了一个实例方法 `advanceToLevel`。在更新 `currentLevel` 之前,这个方法会检测请求的下一关已经解锁。`advanceToLevel` 方法返回一个布尔值,表示是否可以前进到某个关卡。 + +The `LevelTracker` structure is used with the `Player` class, shown below, to track and update the progress of an individual player: + +`LevelTracker` 结构体可以和 `Player` 类结合使用,来跟踪和更新每个玩家的游戏进程: + +``` + +class Player { + var tracker = LevelTracker() + let playerName: String + func completedLevel(level: Int) { + LevelTracker.unlockLevel(level + 1) + tracker.advanceToLevel(level + 1) + } + init(name: String) { + playerName = name + } +} + +``` + +The `Player` class creates a new instance of `LevelTracker` to track that player’s progress. It also provides a method called `completedLevel`, which is called whenever a player completes a particular level. This method unlocks the next level for all players and updates the player’s progress to move them to the next level. (The Boolean return value of `advanceToLevel` is ignored, because the level is known to have been unlocked by the call to `LevelTracker.unlockLevel` on the previous line.) + +`Player` 实例化了一个 `LevelTracker` 来追踪玩家的游戏进程。它也提供了 `completedLevel` 方法,当一个玩家完成某个特定关卡时将会被调用。这个方法为所有玩家解锁下一个关卡,并且更新玩家的进程到下一个关卡。(`advanceToLevel` 的布尔返回值被忽略了, 因为 `LevelTracker.unlockLevel` 已经在这个方法之前解锁了下一个关。) + +You can create a instance of the `Player` class for a new player, and see what happens when the player completes level one: + +你可以为一个新玩家实例化一个 `Player` 类,然后看看当玩家完成第一关的时候会发生什么: + +``` + +var player = Player(name: "Argyrios") +player.completedLevel(1) +println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)") +// prints "highest unlocked level is now 2 + +``` + +If you create a second player, whom you try to move to a level that is not yet unlocked by any player in the game, the attempt to set the player’s current level fails: + +如果你实例化了第二个玩家,并试图让他进入一个尚未解锁的关卡,这会导致失败: + +``` + +player = Player(name: "Beto") +if player.tracker.advanceToLevel(6) { + println("player is now on level 6") +} else { + println("level 6 has not yet been unlocked") +} +// prints "level 6 has not yet been unlocked + +``` \ No newline at end of file diff --git a/src/chapter2/12_Subscripts.md b/src/chapter2/12_Subscripts.md index 078d7e0..de6f2e5 100644 --- a/src/chapter2/12_Subscripts.md +++ b/src/chapter2/12_Subscripts.md @@ -1,16 +1,34 @@ +# Subscripts # 下标 (Subscripts) +Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval. For example, you access elements in an Array instance as someArray[index] and elements in a Dictionary instance as someDictionary[key]. + 下标可以定义在类(Class)、结构体(structures)和枚举(enumerations)这些目标中,可以认为是访问对象、集合或序列的快捷方式。举例来说,用下标访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`,而不需要再调用实例的某个方法来获得元素的值。 +You can define multiple subscripts for a single type, and the appropriate subscript overload to use is selected based on the type of index value you pass to the subscript. Subscripts are not limited to a single dimension, and you can define subscripts with multiple input parameters to suit your custom type’s needs. + 对于同一个目标可以定义多个下标,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。 > 译者:这里下标重载在本小节中原文并没有任何演示 - +## Subscript Syntax ## 下标语法 +Subscripts enable you to query instances of a type by writing one or more values in square brackets after the instance name. Their syntax is similar to both instance method syntax and computed property syntax. You write subscript definitions with the subscript keyword, and specify one or more input parameters and a return type, in the same way as instance methods. Unlike instance methods, subscripts can be read-write or read-only. This behavior is communicated by a getter and setter in the same way as for computed properties: + 下标允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和实例属性的混合。与定义实例方法类似,定义下标使用`subscript`关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标可以设定为读写或只读。这种方式又有点像实例属性的getter和setter: +``` +subscript(index: Int) -> Int { + get { + // return an appropriate subscript value here + } + set(newValue) { + // perform a suitable setting action here + } +} +``` + ``` subscript(index: Int) -> Int { get { @@ -23,18 +41,42 @@ subscript(index: Int) -> Int { } ``` +The type of newValue is the same as the return value of the subscript. As with computed properties, you can choose not to specify the setter’s (newValue) parameter. A default parameter called newValue is provided to your setter if you do not provide one yourself. + `newValue`的类型必须和下标定义的返回类型相同。与实例属性相同的是set的入参声明`newValue`就算不写,在set代码块中依然可以使用`newValue`这个变量来访问新赋的值。 +As with read-only computed properties, you can drop the get keyword for read-only subscripts: + 与只读实例属性一样,可以直接将原本应该写在get代码块中的代码写在subscript中即可: +``` +subscript(index: Int) -> Int { + // return an appropriate subscript value here +} +``` + ``` subscript(index: Int) -> Int { // 返回与入参匹配的Int类型的值 } ``` +Here’s an example of a read-only subscript implementation, which defines a TimesTable structure to represent an n-times-table of integers: + 下面代码演示了一个在TimesTable结构体中使用只读下标的用法,该结构体用来展示传入整数的N倍。 +``` +struct TimesTable { + let multiplier: Int + subscript(index: Int) -> Int { + return multiplier * index + } +} +let threeTimesTable = TimesTable(multiplier: 3) +println("six times three is \(threeTimesTable[6])") +// prints "six times three is 18" +``` + ``` struct TimesTable { let multiplier: Int @@ -47,19 +89,31 @@ println("3的6倍是\(threeTimesTable[6])") // 输出 "3的6倍是18" ``` +In this example, a new instance of TimesTable is created to represent the three-times-table. This is indicated by passing a value of 3 to the structure’s initializer as the value to use for the instance’s multiplier parameter. + 在上例中,通过TimesTable结构体创建了一个用来表示索引值三倍的实例。数值3作为结构体构造函数入参表示这个值将成为实例成员multiplier的值。 +You can query the threeTimesTable instance by calling its subscript, as shown in the call to threeTimesTable[6]. This requests the sixth entry in the three-times-table, which returns a value of 18, or 3 times 6. + 你可以通过下标来来得到结果,比如`threeTimesTable[6]`。这句话访问了threeTimesTable的第六个元素,返回18或者6的3倍。 +> NOTE +> +> An n-times-table is based on a fixed mathematical rule. It is not appropriate to set threeTimesTable[someIndex] to a new value, and so the subscript for TimesTable is defined as a read-only subscript. + > 提示 > -> TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么下标只定义为只读的原因。 - +> TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么下标只定义为只读的原因。 +## Subscript Usage ## 下标用法 +The exact meaning of “subscript” depends on the context in which it is used. Subscripts are typically used as a shortcut for accessing the member elements in a collection, list, or sequence. You are free to implement subscripts in the most appropriate way for your particular class or structure’s functionality. + 下标根据使用场景不同也具有不同的含义。通常下标是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以为特定的类或结构体中自由的实现下标来提供合适的功能。 +For example, Swift’s Dictionary type implements a subscript to set and retrieve the values stored in a Dictionary instance. You can set a value in a dictionary by providing a key of the dictionary’s key type within subscript braces, and assigning a value of the dictionary’s value type to the subscript: + 例如,Swift的字典(Dictionary)实现了通过下标来对其实例中存放的值进行存取操作。在字典中设值可以通过给字典提供一个符合字典索引类型的索引值的表达式赋一个与字典存放值类型匹配的值来做到: ``` @@ -67,23 +121,68 @@ var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] numberOfLegs["bird"] = 2 ``` +``` +var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] +numberOfLegs["bird"] = 2 +``` + +The example above defines a variable called numberOfLegs and initializes it with a dictionary literal containing three key-value pairs. The type of the numberOfLegs dictionary is inferred to be Dictionary. After creating the dictionary, this example uses subscript assignment to add a String key of "bird" and an Int value of 2 to the dictionary. + 上例定义一个名为numberOfLegs的变量并用一个字典表达式初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为`Dictionary`。字典实例创建完成之后通过下标的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。 +For more information about Dictionary subscripting, see [Accessing and Modifying a Dictionary](#). + 更多关于字典(Dictionary)下标的信息请参考[字典的访问与修改](#) +> NOTE + +> Swift’s Dictionary type implements its key-value subscripting as a subscript that takes and receives an optional type. For the numberOfLegs dictionary above, the key-value subscript takes and returns a value of type Int?, or “optional int”. The Dictionary type uses an optional subscript type to model the fact that not every key will have a value, and to give a way to delete a value for a key by assigning a nil value for that key. + > 提示 > > Swift中Dictionary的下标实现中,在get部分返回值是`Int?`,也就是说不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。 +## Subscript Options ## 下标选项 +Subscripts can take any number of input parameters, and these input parameters can be of any type. Subscripts can also return any type. Subscripts can use variable parameters and variadic parameters, but cannot use in-out parameters or provide default parameter values. + 下标允许任意数量的入参索引,并且每个入参类型也没有限制。下标的返回值也可以是任何类型。下标可以使用变量参数和可变参数,但使用in-out参数或给参数设置默认值都是不允许的。 +A class or structure can provide as many subscript implementations as it needs, and the appropriate subscript to be used will be inferred based on the types of the value or values that are contained within the subscript braces at the point that the subscript is used. This definition of multiple subscripts is known as subscript overloading. + 一个类或结构体可以根据自身需要提供多个下标实现,在定义下标时通过入参个类型进行区分,使用下标时会自动匹配合适的下标实现运行,这就是下标的重载。 +While it is most common for a subscript to take a single parameter, you can also define a subscript with multiple parameters if it is appropriate for your type. The following example defines a Matrix structure, which represents a two-dimensional matrix of Double values. The Matrix structure’s subscript takes two integer parameters: + 一个下标入参是最常见的情况,但只要有合适的场景也可以定义多个下标入参。如下例定义了一个Matrix结构体,将呈现一个Double类型的二维数组。Matrix结构体的下标需要两个整型参数: +``` +struct Matrix { + let rows: Int, columns: Int + var grid: Double[] + init(rows: Int, columns: Int) { + self.rows = rows + self.columns = columns + grid = Array(count: rows * columns, repeatedValue: 0.0) + } + func indexIsValidForRow(row: Int, column: Int) -> Bool { + return row >= 0 && row < rows && column >= 0 && column < columns + } + subscript(row: Int, column: Int) -> Double { + get { + assert(indexIsValidForRow(row, column: column), "Index out of range") + return grid[(row * columns) + column] + } + set { + assert(indexIsValidForRow(row, column: column), "Index out of range") + grid[(row * columns) + column] = newValue + } + } +} +``` + ``` struct Matrix { let rows: Int, columns: Int @@ -109,14 +208,24 @@ struct Matrix { } ``` +Matrix provides an initializer that takes two parameters called rows and columns, and creates an array that is large enough to store rows * columns values of type Double. Each position in the matrix is given an initial value of 0.0. To achieve this, the array’s size, and an initial cell value of 0.0, are passed to an array initializer that creates and initializes a new array of the correct size. This initializer is described in more detail in [Creating and Initializing an Array](#). + Matrix提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳rows * columns个数的Double类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考[Creating and Initializing an Array](#)。 +You can construct a new Matrix instance by passing an appropriate row and column count to its initializer: + 你可以通过传入合适的row和column的数量来构造一个新的Matrix实例: ``` var matrix = Matrix(rows: 2, columns: 2) ``` +``` +var matrix = Matrix(rows: 2, columns: 2) +``` + +The preceding example creates a new Matrix instance with two rows and two columns. The grid array for this Matrix instance is effectively a flattened version of the matrix, as read from top left to bottom right: + 上例中创建了一个新的两行两列的Matrix实例。在阅读顺序从左上到右下的Matrix实例中的数组实例grid是矩阵二维数组的扁平化存储: ``` @@ -128,6 +237,8 @@ row0 [0.0, 0.0, row1 0.0, 0.0] ``` +Values in the matrix can be set by passing row and column values into the subscript, separated by a comma: + 将值赋给带有row和column下标的matrix实例表达式可以完成赋值操作,下标入参使用逗号分割 ``` @@ -135,6 +246,8 @@ matrix[0, 1] = 1.5 matrix[1, 0] = 3.2 ``` +These two statements call the subscript’s setter to set a value of 1.5 in the top right position of the matrix (where row is 0 and column is 1), and 3.2 in the bottom left position (where row is 1 and column is 0): + 上面两句话分别让matrix的右上值为1.5,坐下值为3.2: ``` @@ -142,6 +255,8 @@ matrix[1, 0] = 3.2 3.2, 0.0] ``` +The Matrix subscript’s getter and setter both contain an assertion to check that the subscript’s row and column values are valid. To assist with these assertions, Matrix includes a convenience method called indexIsValid, which checks whether the requested row or column is outside the bounds of the matrix: + Matrix下标的getter和setter中同时调用了下标入参的row和column是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValid的成员方法,用来确认入参的row或column值是否会造成数组越界: ``` @@ -150,6 +265,8 @@ func indexIsValidForRow(row: Int, column: Int) -> Bool { } ``` +An assertion is triggered if you try to access a subscript that is outside of the matrix bounds: + 断言在下标越界时触发: ``` diff --git a/src/chapter2/12_Subscripts_Chinese.md b/src/chapter2/12_Subscripts_Chinese.md new file mode 100644 index 0000000..414c34f --- /dev/null +++ b/src/chapter2/12_Subscripts_Chinese.md @@ -0,0 +1,191 @@ +# 下标 (Subscripts) + +下标可以定义在类(Class)、结构体(structures)和枚举(enumerations)这些目标中,可以认为是访问对象、集合或序列的快捷方式。举例来说,用下标访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`,而不需要再调用实例的某个方法来获得元素的值。 + +对于同一个目标可以定义多个下标,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。 + +> 译者:这里下标重载在本小节中原文并没有任何演示 + +## 下标语法 + +下标允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和实例属性的混合。与定义实例方法类似,定义下标使用`subscript`关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标可以设定为读写或只读。这种方式又有点像实例属性的getter和setter: + +``` +subscript(index: Int) -> Int { + get { + // 返回与入参匹配的Int类型的值 + } + + set(newValue) { + // 执行赋值操作 + } +} +``` + +`newValue`的类型必须和下标定义的返回类型相同。与实例属性相同的是set的入参声明`newValue`就算不写,在set代码块中依然可以使用`newValue`这个变量来访问新赋的值。 + +与只读实例属性一样,可以直接将原本应该写在get代码块中的代码写在subscript中即可: + +``` +subscript(index: Int) -> Int { + // 返回与入参匹配的Int类型的值 +} +``` + +Here’s an example of a read-only subscript implementation, which defines a TimesTable structure to represent an n-times-table of integers: + +下面代码演示了一个在TimesTable结构体中使用只读下标的用法,该结构体用来展示传入整数的N倍。 + +``` +struct TimesTable { + let multiplier: Int + subscript(index: Int) -> Int { + return multiplier * index + } +} +let threeTimesTable = TimesTable(multiplier: 3) +println("3的6倍是\(threeTimesTable[6])") +// 输出 "3的6倍是18" +``` + +在上例中,通过TimesTable结构体创建了一个用来表示索引值三倍的实例。数值3作为结构体构造函数入参表示这个值将成为实例成员multiplier的值。 + +你可以通过下标来来得到结果,比如`threeTimesTable[6]`。这句话访问了threeTimesTable的第六个元素,返回18或者6的3倍。 + +> 提示 +> +> TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么下标只定义为只读的原因。 + +## 下标用法 + +下标根据使用场景不同也具有不同的含义。通常下标是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以为特定的类或结构体中自由的实现下标来提供合适的功能。 + +例如,Swift的字典(Dictionary)实现了通过下标来对其实例中存放的值进行存取操作。在字典中设值可以通过给字典提供一个符合字典索引类型的索引值的表达式赋一个与字典存放值类型匹配的值来做到: + +``` +var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] +numberOfLegs["bird"] = 2 +``` + +``` +var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] +numberOfLegs["bird"] = 2 +``` + +上例定义一个名为numberOfLegs的变量并用一个字典表达式初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为`Dictionary`。字典实例创建完成之后通过下标的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。 + +更多关于字典(Dictionary)下标的信息请参考[字典的访问与修改](#) + +> 提示 +> +> Swift中Dictionary的下标实现中,在get部分返回值是`Int?`,也就是说不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。 + +## Subscript Options +## 下标选项 + +下标允许任意数量的入参索引,并且每个入参类型也没有限制。下标的返回值也可以是任何类型。下标可以使用变量参数和可变参数,但使用in-out参数或给参数设置默认值都是不允许的。 + +一个类或结构体可以根据自身需要提供多个下标实现,在定义下标时通过入参个类型进行区分,使用下标时会自动匹配合适的下标实现运行,这就是下标的重载。 + +一个下标入参是最常见的情况,但只要有合适的场景也可以定义多个下标入参。如下例定义了一个Matrix结构体,将呈现一个Double类型的二维数组。Matrix结构体的下标需要两个整型参数: + +``` +struct Matrix { + let rows: Int, columns: Int + var grid: Double[] + init(rows: Int, columns: Int) { + self.rows = rows + self.columns = columns + grid = Array(count: rows * columns, repeatedValue: 0.0) + } + func indexIsValidForRow(row: Int, column: Int) -> Bool { + return row >= 0 && row < rows && column >= 0 && column < columns + } + subscript(row: Int, column: Int) -> Double { + get { + assert(indexIsValidForRow(row, column: column), "Index out of range") + return grid[(row * columns) + column] + } + set { + assert(indexIsValidForRow(row, column: column), "Index out of range") + grid[(row * columns) + column] = newValue + } + } +} +``` + +``` +struct Matrix { + let rows: Int, columns: Int + var grid: Double[] + init(rows: Int, columns: Int) { + self.rows = rows + self.columns = columns + grid = Array(count: rows * columns, repeatedValue: 0.0) + } + func indexIsValidForRow(row: Int, column: Int) -> Bool { + return row >= 0 && row < rows && column >= 0 && column < columns + } + subscript(row: Int, column: Int) -> Double { + get { + assert(indexIsValidForRow(row, column: column), "Index out of range") + return grid[(row * columns) + column] + } + set { + assert(indexIsValidForRow(row, column: column), "Index out of range") + grid[(row * columns) + columns] = newValue + } + } +} +``` + +Matrix提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳rows * columns个数的Double类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考[Creating and Initializing an Array](#)。 + +你可以通过传入合适的row和column的数量来构造一个新的Matrix实例: + +``` +var matrix = Matrix(rows: 2, columns: 2) +``` + +上例中创建了一个新的两行两列的Matrix实例。在阅读顺序从左上到右下的Matrix实例中的数组实例grid是矩阵二维数组的扁平化存储: + +``` +// 示意图 +grid = [0.0, 0.0, 0.0, 0.0] + + col0 col1 +row0 [0.0, 0.0, +row1 0.0, 0.0] +``` + +将值赋给带有row和column下标的matrix实例表达式可以完成赋值操作,下标入参使用逗号分割 + +``` +matrix[0, 1] = 1.5 +matrix[1, 0] = 3.2 +``` + +上面两句话分别让matrix的右上值为1.5,坐下值为3.2: + +``` +[0.0, 1.5, + 3.2, 0.0] +``` + +Matrix下标的getter和setter中同时调用了下标入参的row和column是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValid的成员方法,用来确认入参的row或column值是否会造成数组越界: + +``` +func indexIsValidForRow(row: Int, column: Int) -> Bool { + return row >= 0 && row < rows && column >= 0 && column < columns +} +``` + +断言在下标越界时触发: + +``` +let someValue = matrix[2, 2] +// 断言将会触发,因为 [2, 2] 已经超过了matrix的最大长度 +``` + +> 译者:这里有个词Computed Properties 这里统一翻译为实例属性了 微软术语引擎里没有这个词 + diff --git a/src/chapter2/13_Inheritance.md b/src/chapter2/13_Inheritance.md index f9bf1ab..2ab8e9e 100644 --- a/src/chapter2/13_Inheritance.md +++ b/src/chapter2/13_Inheritance.md @@ -1 +1,393 @@ -#TODO +##Inheritance +##继承 + +A class can *inherit* methods, properties, and other characteristics from another class. When one class inherits from another, the inheriting class is known as a *subclass*, and the class it inherits from is known as its *superclass*. + +一个类可以从另外一个类中*继承*方法,属性以及其他特性。当一个类继承另外一个类时,这个类被称作为*子类*,而被继承的类被称作为*父类*。 + +Inheritance is a fundamental behavior that differentiates classes from other types in Swift. +Classes in Swift can call and access methods, properties, and subscripts belonging to their superclass and can provide their own overriding versions of those methods, properties, and subscripts to refine or modify their behavior. Swift helps to ensure your overrides are correct by checking that the override definition has a matching superclass definition. + +在Swift语言中,继承是区分类与其他类型的一个基本行为。一个类可以访问并调用其父类中的方法、属性和下标,也可以通过重写它们来修改或优化它们的行为。Swift通过检查重写的定义是否能与一个父类中的定义匹配来确保重写的正确性。 + +Classes can also add property observers to inherited properties in order to be notified when the value of a property changes. Property observers can be added to any property, regardless of whether it was originally defined as a stored or computed property. + +类也可以通过对继承的属性添加观察器来监听它们的变化。属性观察器可以被添加用于监听任何属性,无论被监听的属性最初是被定义为存储属性还是计算属性。 + +##Defining a Base Class +##定义一个基类 +Any class that does not inherit from another class is known as a *base* class. +一个没有继承其他类的类被称为*基类*。 + +``` +NOTE +Swift classes do not inherit from a universal base class. Classes you define without specifying a superclass automatically become base classes for you to build upon. +``` +``` +注意 +Swift中的类没有继承一个全局的基类。没有指定父类的类会自动成为一个可用来被扩展的基类。 +``` + +The example below defines a base class called `Vehicle`. This base class declares two properties(`numberOfWheels` and `maxPassengers`) that are universal to all vehicles. These properties are used by a method called `description`, which returns a `String` description of the vehicle’s characteristics: + +下面的例子定义了一个基类`Vehicle`。类中声明了两个所有车辆都通用的属性(`numberOfWheels`和`maxPassengers`)。这两个属性在方法`description`中被使用,这个方法返回一个`String`类型的值来描述车辆的特性: + +``` +class Vehicle { + var numberOfWheels: Int + var maxPassengers: Int + func description() -> String { + return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" + } + init() { + numberOfWheels = 0 + maxPassengers = 1 + } +} +``` + +The `Vehicle` class also defines an initializer to set up its properties. Initializers are described in detail in [Initialization](), but a brief introduction is required here in order to illustrate how inherited properties can be modified by subclasses. + +`Vehicle`类中还定义了一个构造器来初始化它的属性。构造器在[构造过程]()章节中有详细描述,不过在这里会简单介绍一下被继承的属性是如何被子类修改的。 + +You use initializers to create a new instance of a type. Although initializers are not methods, they are written in a very similar syntax to instance methods. An initializer prepares a new instance for use, and ensures that all properties of the instance have valid initial values. + +构造器会被用来创建一个类型的实例。尽管构造器并不是方法,但是书写它们的语法和实例方法非常类似。构造器准备一个新的实例供使用,并确保实例中所有的属性都有合法的初始值。 + +In its simplest form, an initializer is like an instance method with no parameters, written using the `init` keyword: + +在最简单的形式中,构造器就像是一个没有参数的实例方法,用关键词`init`来声明: + +``` +init() { +// perform some initialization here +} +``` + +To create a new instance of `Vehicle`, call this initializer with *initializer syntax*, written as `TypeName` followed by empty parentheses: + +创建一个`Vehicle`类实例需要通过使用*构造器语法*来调用构造器,写法是在类名后加上一对空的括号: + +``` +let someVehicle = Vehicle() +``` + +The initializer for `Vehicle` sets some initial property values (`numberOfWheels = 0` and `maxPassengers = 1`) for an arbitrary vehicle. + +`Vehicle`类的构造器为一个车辆初始化了一些属性(`numberOfWheels = 0` 和 `maxPassengers = 1`)。 + +The `Vehicle` class defines common characteristics for an arbitrary vehicle, but is not much use in itself. To make it more useful, you need to refine it to describe more specific kinds of vehicle. + +`Vehicle类`为车辆定义了一些公用的特性,但是这些特性对类本身来说并没有太多作用。为了提高这些特性的可用性,需要描述更多不同特定种类的车辆。 + +##Subclassing +##子类化 + +*Subclassing* is the act of basing a new class on an existing class. The subclass inherits characteristics from the existing class, which you can refine. You can also add new characteristics to the subclass. + +*子类化*是指在一个已有类的基础上建立一个新的类。新建的子类会继承已有类的特性,并可以优化它们。在子类中还可以增加新的特性。 + +To indicate that a class has a superclass, write the superclass name after the original class name, separated by a colon: + +当声明一个类的父类时,需要将父类的名字写在原有类之后,并用冒号分隔: + +``` +class SomeClass: SomeSuperclass { +// class definition goes here +} +``` + +The next example defines a second, more specific vehicle called `Bicycle`. This new class is based on the existing capabilities of `Vehicle`. You indicate this by placing the name of the class the subclass builds upon (`Vehicle`) after its own name (`Bicycle`), separated by a colon. + +下面的例子定义了另一个更具体的车辆类`Bicycle`。新定义的类基于`Vehicle`类中存在的功能。定义的时候需要把父类的名字(`Vehicle`)放在新定义类的名字(`Bicycle`)之后,并用冒号分隔。 + +This can be read as: +“Define a new class called `Bicycle`, which inherits the characteristics of `Vehicle`”: +下面的语句可以这样来解读: + +“定义一个新的名为`Bicycle`的类,它继承了`Vehicle`类的特性。” + +``` +class Bicycle: Vehicle { + init() { + super.init() + numberOfWheels = 2 + } +} +``` + +`Bicycle` is a subclass of `Vehicle`, and `Vehicle` is the superclass of `Bicycle`. The new `Bicycle` class automatically gains all characteristics of `Vehicle`, such as its `maxPassengers` and `numberOfWheels` properties.You can tailor those characteristics and add new ones to better match the requirements of the `Bicycle` class. + +`Bicycle`是`Vehicle`的子类, 反而言之`Vehicle`是`Bicycle`的父类。新创建的`Bicycle`类自动获得了`Vehicle`类的所有特性,比如它的`maxPassengers`与`numberOfWheels`属性。为了更好的满足`Bicycle`类的需求,你可以修改这些特性或者增加新的特性。 + +The `Bicycle` class also defines an initializer to set up its tailored characteristics. The initializer for `Bicycle` calls `super.init()`, the initializer for the `Bicycle` class’s superclass, `Vehicle`, and ensures that all of the inherited properties are initialized by `Vehicle` before `Bicycle` tries to modify them. + +`Bicycle`类同样定义了一个构造器来初始化其修改过的特性。`Bicycle`的构造器通过调用其父类`Vehicle`的构造器`super.init()`来确保所有继承下来的属性在被修改前都已被`Vehicle`类成功初始化。 + +``` +Note +Unlike Objective-C, initializers are not inherited by default in Swift. For more information, see [Initializer +Inheritance and Overriding](). +``` + +``` +注意 +与Objective-C不同的是,在Swift中构造器是不会默认被继承的。更多信息请参考[构造器的继承和重写]()。 +``` + +The default value of `maxPassengers` provided by `Vehicle` is already correct for a bicycle, and so it is not changed within the initializer for `Bicycle`. The original value of `numberOfWheels` is not correct, however, and is replaced with a new value of `2`. + +`Vehicle`类中为`maxPassengers`提供的默认值对于一辆自行车来说已经是正确的了,所以在`Bicycle`类的构造器中没有做修改。而`numberOfWheels`的原始值对于一辆自行车来说是错误的,所以它被替换成了一个新的值`2`。 + +As well as inheriting the properties of `Vehicle`, `Bicycle` also inherits its methods. If you create an instance of `Bicycle`, you can call its inherited `description` method to see how its properties have been updated: + +除了继承了`Vehicle`类中的属性,`Bicycle`类还继承了它的方法。创建了一个`Bicycle`的实例后,可以通过调用它继承过来的`description`方法来观察类中属性的更新: + +``` +let bicycle = Bicycle() +println("Bicycle: \(bicycle.description())") +// Bicycle: 2 wheels; up to 1 passengers +``` + +Subclasses can themselves be subclassed: + +子类可以被继续继承: + +``` +class Tandem: Bicycle { + init() { + super.init() + maxPassengers = 2 + } +} +``` + +This example creates a subclass of `Bicycle` for a two-seater bicycle known as a “tandem”. `Tandem` inherits the two properties from `Bicycle`, which in turn inherits these properties from `Vehicle`. `Tandem` doesn’t change the number of wheels—it’s still a bicycle, after all—but it does update `maxPassengers` to have the correct value for a tandem. + +上面的例子创建了一个`Bicycle`类的子类双人自行车`Tandem`。`Tandem`类继承了`Bicycle`类中的两个属性,而这两个属性又是从`Vehicle`类继承下来的。由于`Tandem`仍然是自行车的一种,所以类中没有改变车轮数量的属性`numberOfWheels`,但是它更新了表示最大乘客数的属性`maxPassengers`以满足一辆双人自行车的要求。 + +``` +NOTE +Subclasses are only allowed to modify variable properties of superclasses during initialization. You can’t +modify inherited constant properties of subclasses. +``` + +``` +注意 +在初始化过程中,子类只允许修改父类中的变量属性,而常量属性是不能被修改的。 +``` + +Creating an instance of `Tandem` and printing its description shows how its properties have been updated: + +下面通过创建一个`Tandem`的实例并打印其描述来观察它的属性更新: + +``` +let tandem = Tandem() +println("Tandem: \(tandem.description())") +// Tandem: 2 wheels; up to 2 passengers +``` + +Note that the `description` method is also inherited by `Tandem`. Instance methods of a class are inherited by any and all subclasses of that class. + +注意`description`方法也是`Tandem`类继承下来的。一个类中的实例方法会被它所有的子类继承。 + +##Overriding +##重写 +A subclass can provide its own custom implementation of an instance method, class method, instance property, or subscript that it would otherwise inherit from a superclass. This is known as *overriding*. + +子类可以为继承过来的实例方法、类方法、实例属性或下标提供它自己的实现,这个行为被称作*重写*。 + +To override a characteristic that would otherwise be inherited, you prefix your overriding definition with the `override` keyword. Doing so clarifies that you intend to provide an override and have not provided a matching definition by mistake. Overriding by accident can cause unexpected behavior, and any overrides without the `override` keyword are diagnosed as an error when your code is compiled. + +如果要重写某个属性,你需要在重写的定义前加上`override`关键字。这么做是为了表明你是为了提供一个重写的版本而不是错误地提供了一个同样的定义。意外地重写会引起不可预知的错误,没有加关键字`override`的重写都会在编译时被诊断为错误。 + +The `override` keyword also prompts the Swift compiler to check that your overriding class’s superclass (or one of its parents) has a declaration that matches the one you provided for the override. This check ensures that your overriding definition is correct. + +`override`关键字也会提醒Swift编译器去校验被继承的父类(或其中中一个父类)中是否有匹配的声明用于被重写。这个检查可以确保你的重写定义是正确的。 + +##Accessing Superclass Methods, Properties, and Subscripts +##访问父类的方法、属性和下标 + +When you provide a method, property, or subscript override for a subclass, it is sometimes useful to use the existing superclass implementation as part of your override. For example, you can refine the behavior of that existing implementation or store a modified value in an existing inherited variable. + +当你访问父类的方法、属性或下标时,有时在你的重写版本中使用已存在的父类实现会很有用。比如你可以优化一个已有的实现或者在一个继承来的变量重储存一个修改过的值。 + +Where this is appropriate, you access the superclass version of a method, property, or subscript by using the `super` prefix: + +在适当的地方,你可以通过`super`前缀来访问父类版本的方法、属性或下标: + +* An overridden method named `someMethod` can call the superclass version of `someMethod` by calling `super.someMethod()` within the overriding method implementation. +* An overridden property called `someProperty` can access the superclass version of `someProperty` as `super.someProperty` within the overriding getter or setter implementation. +* An overridden subscript for `someIndex` can access the superclass version of the same subscript as `super[someIndex]` from within the overriding subscript implementation. + +* 一个重写的方法`someMethod`可以在方法实现中通过`super.someMethod()`来调用父类版本的`someMethod`。 +* 一个重写的属性`someProperty`可以在getter和setter方法的重写实现中通过`super.someProperty`来访问父类版本的`someProperty`。 +* 一个重写的下标`someIndex`可以在实现中通过`super[someIndex]`来调用相同下标的父类版本的。 + +##Overriding Methods +##重写方法 +You can override an inherited instance or class method to provide a tailored or alternative implementation of the method within your subclass. + +在子类中你可以通过提供一个定制或替代的实现来重写一个继承过来的实例方法或类方法。 + +The following example defines a new subclass of `Vehicle` called `Car`, which overrides the `description` method it inherits from `Vehicle`: + +下面的例子定义了`Vehicle`类的一个新子类`Car`, 子类中重写了从父类中继承过来的方法`description`: + +``` +class Car: Vehicle { + var speed: Double = 0.0 + init() { + super.init() + maxPassengers = 5 + numberOfWheels = 4 + } + override func description() -> String { + return super.description() + "; " + + "traveling at \(speed) mph" + } +} +``` + +`Car` declares a new stored `Double` property called `speed`. This property defaults to `0.0`, meaning “zero miles per hour”. `Car` also has a custom initializer, which sets the maximum number of passengers to `5`, and the default number of wheels to `4`. + +`Car`类中声明了一个新的`Double`类存储型属性`speed`,默认值是`0.0`,代表”时速为每小时0英里”。Car类还有自定义构造器,构造器中设置最大乘客量为`5`,默认车轮数为`4`。 + +`Car` overrides its inherited `description` method by providing a method with the same declaration as the `description` method from `Vehicle`. The overriding method definition is prefixed with the `override` keyword. + +`Car`类重写了继承来的`description`方法,它的声明和父类`Vehicle`中一致。被重写的方法定义前加上了`override`关键字。 + +Rather than providing a completely custom implementation of `description`, the overriding method actually starts by calling `super.description` to retrieve the description provided by `Vehicle`. It then appends some additional information about the car’s current speed. + +重写的方法并没有完全自定义方法`description`的实现,它在开始时通过调用`super.description`来使用父类`Vehicle`中的方法实现,之后再为车辆的当前速度追加了一些额外信息。 + +If you create a new instance of `Car`, and print the output of its `description` method, you can see that the description has indeed changed: + +如果你创建一个`Car`类的实例,并打印`description`方法的输出,你会发现描述的信息已经发生了改变: + +``` +let car = Car() +println("Car: \(car.description())") +// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph +``` + +##Overriding Properties +##重写属性 + +You can override an inherited instance or class property to provide your own custom getter and setter for that property, or to add property observers to enable the overriding property to observe when the underlying property value changes. + +你可以重写继承来的实例属性或类属性来提供自定义的getter和setter方法,或添加属性观察器来使被重写的属性观察属性值什么时候发生改变。 + +##Overriding Property Getters and Setters +##重写属性的Getters和Setters + +You can provide a custom getter (and setter, if appropriate) to override any inherited property, regardless of whether the inherited property is implemented as a stored or computed property at its source. The stored or computed nature of an inherited property is not known by a subclass—it only knows that the inherited property has a certain name and type. You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type. + +你可以提供自定义的getter(或setter,如适用)来重写任何被继承的属性,无论被继承的属性是存储型的还是计算型的。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性有名字和类型。在你重写一个属性时,必须声明它的名字和类型。这是为了让编译器检测你重写的属性在父类中有相同的名字和类型。 + +You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property. + +你可以通过提供getter和setter来将继承来的一个只读属性重写为一个读写属性。但是你无法将一个继承来的读写属性重写成一个只读属性。 + +``` +NOTE +If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning `super.someProperty` from the getter, as in the `SpeedLimitedCar` example below. +``` + +``` +注意 +如果你在重写时为一个属性提供了setter,那么你必须同时要提供一个getter。如果你不想在重写版本的getter中修改继承来的属性值,你可以直接返回`super.someProperty`,正如下面`SpeedLimitedCar`的例子所示。 +``` + +The following example defines a new class called `SpeedLimitedCar`, which is a subclass of `Car`. The `SpeedLimitedCar` class represents a car that has been fitted with a speed-limiting device, which prevents the car from traveling faster than 40mph. You implement this limitation by overriding the inherited `speed` property: + +下面的例子定义了一个新的类`SpeedLimitedCar`,这个类是`Car`类的子类。`SpeedLimitedCar`类表示安装了限速装置的车来防止车的时速超过40英里。你需要通过重写`speed`属性来实现这个速度限制: + +``` +class SpeedLimitedCar: Car { + override var speed: Double { + get { + return super.speed + } + set { + super.speed = min(newValue, 40.0) + } + } +} +``` + +Whenever you set the `speed` property of a `SpeedLimitedCar` instance, the property’s setter implementation checks the new value and limits it to 40mph. It does this by setting the underlying `speed` property of its superclass to be the smaller of `newValue` and `40.0`. The smaller of these two values is determined by passing them to the `min` function, which is a global function provided by the Swift standard library. The `min` function takes two or more values and returns the smallest one of those values. + +当你设置一个`SpeedLimitedCar`实例的`speed`属性时,属性的`setter`方法会检验新设置的值并把它限制在时速40英里以内,这是通过选择新设置的值和40之间最小的值来实现的。这个比较会通过调用`min`函数实现,它是Swift标准库中的一个方法。`min`函数接受两个或更多的参数并返回其中最小的一个。 + +If you try to set the `speed` property of a `SpeedLimitedCar` instance to more than 40mph, and then print the output of its `description` method, you see that the speed has been limited: + +如果你尝试将`SpeedLimitedCar`实例中的`speed`属性设置成时速40英里以上,然后打印`description`方法的输出,你会看到车速已经被限制了: + +``` +let limitedCar = SpeedLimitedCar() +limitedCar.speed = 60.0 +println("SpeedLimitedCar: \(limitedCar.description())") +// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph +``` + +##Overriding Property Observers +##覆盖属性观察器 + +You can use property overriding to add property observers to an inherited property. This enables you to be notified when the value of the inherited property changes, regardless of how that property was originally implemented. For more information on property observers, see [Property Observers](). + +你可以在属性重写时为一个继承来的属性添加属性观察器。当继承来的属性被修改时,你就会被通知到,无论那个属性原来是如何被实现的。关于属性观察器的更多内容,请参考属[属性观察器]()。 + +``` +NOTE +You cannot add property observers to inherited constant stored properties or inherited read-only computed properties. The value of these properties cannot be set, and so it is not appropriate to provide a `willSet` or `didSet` implementation as part of an override. +Note also that you cannot provide both an overriding setter and an overriding property observer. If you want to observe changes to a property’s value, and you are already providing a custom setter for that property, you can simply observe any value changes from within the custom setter. +``` + +``` +注意 +你不可以为继承来的常量存储型属性或只读计算型属性添加属性观察器。这些属性的值是不可以被修改的,所以在重写时为它们提供`willSet`和`didSet`的实现是不恰当的。 +注意你不能同时提供重写的setter方法和属性观察期。如果你想要观察属性值的变化,并且你已经给那个属性提供了定制的setter方法,在setter方法中你就已经可以观察到任何属性值的变化了。 +``` + +The following example defines a new class called `AutomaticCar`, which is a subclass of `Car`. The `AutomaticCar` class represents a car with an automatic gearbox, which automatically selects an appropriate gear to use based on the current speed. `AutomaticCar` also provides a custom `description` method to print the current gear. + +下面的例子定义了一个新类`AutomaticCar`,它是`Car`类的子类。`AutomaticCar`类表示自动档汽车,它会根据当前的车速选择一个合适的档位。`AutomaticCar`提供了一个定制的`description`方法来输出当前的档位。 + +``` +class AutomaticCar: Car { + var gear = 1 + override var speed: Double { + didSet { + gear = Int(speed / 10.0) + 1 + } + } + override func description() -> String { + return super.description() + " in gear \(gear)" + } +} +``` + +Whenever you set the `speed` property of an `AutomaticCar` instance, the property’s `didSet` observer automatically sets the `gear` property to an appropriate choice of gear for the new speed. Specifically, the property observer chooses a gear which is the new `speed` value divided by `10`, rounded down to the nearest integer, plus `1`. A speed of `10.0` produces a gear of `1`, and a speed of `35.0` produces a gear of `4`: + +当你为一个`AutomaticCar`实例设置`speed`属性时,属性的`didSet`观察器会自动设置`gear`属性到一个合适的档位。具体的计算方法是,属性观察者将新设置的速度值除以`10`,向下取整之后再加`1`。例如速度是`10`的时候档位是`1`,速度是`35.0`的时候档位是`4`: + +``` +let automatic = AutomaticCar() +automatic.speed = 35.0 +println("AutomaticCar: \(automatic.description())") +// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4 +``` + +##Preventing Overrides +##防止重写 +You can prevent a method, property, or subscript from being overridden by marking it as *final*. Do this by writing the `@final` attribute before its introducer keyword (such as `@final var`, `@final func`, `@final class func`, and `@final subscript`). +你可以通过标记一个属性、方法或下标为*final*来防止它们被重写。具体方法是在声明它们的关键字前加上`@final` (例如`@final var`, `@final func`, `@final class func`及`@final subscript`)。 + +Any attempts to override a final method, property, or subscript in a subclass are reported as a compile-time error. Methods, properties or subscripts that you add to a class in an extension can also be marked as final within the extension’s definition. + +如果被标记为final的方法、属性或下标在子类中被尝试重写,在编译时便会报错。在扩展时被添加到类中的方法,属性或附属脚本也可以在扩展的定义中标记为final。 + +You can mark an entire class as final by writing the `@final` attribute before the `class` keyword in its class definition (`@final class`). Any attempts to subclass a final class will be reported as a compile-time error. + +你也可以通过在关键字class前添加`@final`属性(`@final class`)来把整个类标记为final。如此一来这个类就不可以被继承,否则在编译时会报错。 diff --git a/src/chapter2/14_Initialization.md b/src/chapter2/14_Initialization.md index e69de29..d278db2 100644 --- a/src/chapter2/14_Initialization.md +++ b/src/chapter2/14_Initialization.md @@ -0,0 +1,895 @@ +## 问题 + +* `initializer` 的定义?『构造方法』 +* `deinitializer` 的定义?『析构方法』 +* `Stored Properties` 的定义?『属性值』 +* `observers` 的定义?『观察者方法』 +* `external name` 的定义?『外部名称』 +* `local name` 的定义?『内部名称』 +* `constant properties` 的定义?『恒定属性』 +* `Optional Property Types` 的定义?『可选属性类型』 +* `Memberwise` 的定义?『成员式』 +* `Initializer Delegation` 的定义?『构造代理』 +* `designated initializers` 的定义?『指定构造方法』 +* `convenience initializers` 的定义?『便利构造方法』 +* `delegates up` 的定义?『向上委托』 +* `nonetheless` 的定义?『仍然』 + +## 构造过程 (Initialization) + +_构造过程_是准备实例化类、结构或枚举的过程。该过程需要为当前实例化对象的每个属性值设置初始值,并且在其准备好之前执行所需的设置或初始化。 + +> “Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready to for use.” + +你通过定义_构造方法_来实现构造过程,就好比能够创建特定类型的新实例化对象的特殊方法。与 Objective-C 不同的是,Swift 的构造方法没有返回值。其主要作用是保证一个类的新实例在其首次使用之前能够被正确地初始化。 + +> “You implement this initialization process by defining initializers, which are like special methods that can be called to create a new instance of a particular type. Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.” + +你也可以为类的实例实现一个_析构方法_,这可以让该实例在被释放前执行任意自定义的清理工作。需要了解更多关于析构方法的信息,请见『析构过程』章节。 + +> “Instances of class types can also implement a deinitializer, which performs any custom cleanup just before an instance of that class is deallocated. For more information about deinitializers, see Deinitialization.” + +### 初始化属性值 (Setting Initial Values for Stored Properties) + +类和结构都_必须_在该类(或结构)的实例被创建时为其属性值设定合适的初始值。属性值不能是一个不确定的状态。 + +> “Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.” + +你可以在构造方法中为属性值设定初始值,或者在属性值的定义中指定一个默认值。我们会在接下来的章节中描述这些操作。 + +> “You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition. These actions are described in the following sections.” + +提示: + +> 无论是你为属性值指定默认值,还是通过构造方法设定初始值,该属性值的真实值被直接设定,而不会调用属性值观察者方法。 +> > “When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.” + +### 构造方法 (Initializers) + +_构造方法_用来创建类的新实例对象。其最简单的形式就是使用 `init` 关键字,类似一个没有参数的实例方法。 + +> “Initializers are called to create a new instance of a particular type. In its simplest form, an initializer is like an instance method with no parameters, written using the init keyword.” + +在接下来的例子中定义了一个叫 `Fahrenheit` 的结构来存储用华氏表示的温度。`Fahrenheit` 结构拥有一个类型为 `Double` 的属性值 `temperature`: + +> “The example below defines a new structure called Fahrenheit to store temperatures expressed in the Fahrenheit scale. The Fahrenheit structure has one stored property, temperature, which is of type Double:” + +``` +struct Fahrenheit { + var temperature: Double + init() { + temperature = 32.0 + } +} +var f = Fahrenheit() +println("The default temperature is \(f.temperature)° Fahrenheit") +// prints "The default temperature is 32.0° Fahrenheit +``` + +该结构只定义了一个不带任何参数的构造方法 `init`,该构造方法为属性值 `temperature` 设定了初始值 `32.0`(在华氏温度中表示水的冰点温度)。 + +> “The structure defines a single initializer, init, with no parameters, which initializes the stored temperature with a value of 32.0 (the freezing point of water when expressed in the Fahrenheit scale).” + +### 默认属性值 (Default Property Values) + +如上所示,你可以在一个构造方法中设定属性值的初始值。另外,你也可以在属性值声明时为其指定_默认值_。 + +> “You can set the initial value of a stored property from within an initializer, as shown above. Alternatively, specify a default property value as part of the property’s declaration. You specify a default property value by assigning an initial value to the property when it is defined.” + +提示: + +> 如果一个属性值总是拥有相同的初始值,请为其提供默认值,而不是在构造方法中设定初始值。虽然结果是一样的,*但默认值将属性值声明和初始化更紧密地绑在一起。这样不仅更简短,也更清晰,能够让你通过默认值来判断该属性值的数据类型。默认值也能让你更容易理解默认构造方法与构造方法继承,我们会在接下来的章节中描述它们。 +> > “If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. The end result is the same, but the default value ties the property’s initialization more closely to its declaration. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value. The default value also makes it easier for you to take advantage of default initializers and initializer inheritance, as described later in this chapter.” + +你可以将上面的 `Fahrenheit` 结构简单写成在属性 `temperature` 被申明时提供一个默认值: + +>“You can write the Fahrenheit structure from above in a simpler form by providing a default value for its temperature property at the point that the property is declared:” + +``` +struct Fahrenheit { + var temperature = 32.0 +} +``` + +### 自定义构造过程 (Customizing Initialization) + +你可以自定义构造过程中的输入参数和可选属性类型,也可以在构造过程中修改常量,我们会在接下来的章节中描述它们。 + +> “You can customize the initialization process with input parameters and optional property types, or by modifying constant properties during initialization, as described in the following sections.” + +#### 初始化参数 (Initialization Parameters) + +要自定义构造过程,你可以在构造方法的定义中提供_初始化参数_,并定义其类型和名字。初始化参数的作用和语法与函数和方法的参数相同。 + +> “You can provide initialization parameters as part of an initializer’s definition, to define the types and names of values that customize the initialization process. Initialization parameters have the same capabilities and syntax as function and method parameters.” + +在接下来的例子中定义了一个 `Celsius` 的结构来存储用摄氏表示的温度。`Celsius` 结构实现了两个自定义的构造方法,分别是 `init(fromFahrenheit:)` 和 `init(fromKelvin:)`,能够通过不同的温标值分别初始化该结构的实例。 + +> “The following example defines a structure called Celsius, which stores temperatures expressed in the Celsius scale. The Celsius structure implements two custom initializers called init(fromFahrenheit:) and init(fromKelvin:), which initialize a new instance of the structure with a value from a different temperature scale:” + +``` +struct Celsius { + var temperatureInCelsius: Double = 0.0 + init(fromFahrenheit fahrenheit: Double) { + temperatureInCelsius = (fahrenheit - 32.0) / 1.8 + } + init(fromKelvin kelvin: Double) { + temperatureInCelsius = kelvin - 273.15 + } +} +let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) +// boilingPointOfWater.temperatureInCelsius is 100.0 +let freezingPointOfWater = Celsius(fromKelvin: 273.15) +// freezingPointOfWater.temperatureInCelsius is 0.0 +``` + +第一个构造方法拥有一个外部名称为 `fromFahrenheit` 内部名称为 `fahrenheit` 的初始化参数。第二个构造方法拥有一个外部名称为 `fromKelvin` 内部名称为 `kelvin` 的初始化参数。这两个构造方法都将各自的参数转换为摄氏温标值,并保存在属性值 `temperatureInCelsius` 中。 + +> “The first initializer has a single initialization parameter with an external name of fromFahrenheit and a local name of fahrenheit. The second initializer has a single initialization parameter with an external name of fromKelvin and a local name of kelvin. Both initializers convert their single argument into a value in the Celsius scale and store this value in a property called temperatureInCelsius.” + +##### 参数的外部名称和内部名称 (Local and External Parameter Names) + +如同函数和方法的参数一样,初始化参数能同时拥有在构造方法体内使用的内部名称,和构造方法被调用时的外部名称。 + +> “As with function and method parameters, initialization parameters can have both a local name for use within the initializer’s body and an external name for use when calling the initializer.” + +可是,构造方法不像函数和方法那样在其括号前有确定的方法名。因此,初始化参数的类型和名字在明确哪个构造方法被调用时起到了特别重要的作用。正因如此,如果你没有提供外部名称,Swift 会自动为构造方法中的_每个_参数提供外部名称。自动生成的外部名称与内部名称相同,就如你在每个初始化参数前写了 # 符号。 + +> “However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic external name for every parameter in an initializer if you don’t provide an external name yourself. This automatic external name is the same as the local name, as if you had written a hash symbol before every initialization parameter.” + +提示: + +> 如果你不想在构造方法的参数中提供外部名称,请将下划线(_)作为申明为该参数的外部名称以复写上面描述中的默认行为。 +> > “If you do not want to provide an external name for a parameter in an initializer, provide an underscore (_) as an explicit external name for that parameter to override the default behavior described above.” + +在接下来的例子中定义了一个 `Color` 的结构,其拥有三个恒定属性 `red`,`green` 和 `blue`。这些属性值在 `0.0` 到 `1.0` 之间,分别用来表示颜色值中的红、绿、蓝数值。 + +> “The following example defines a structure called Color, with three constant properties called red, green, and blue. These properties store a value between 0.0 and 1.0 to indicate the amount of red, green, and blue in the color.” + +`Color` 定义了一个拥有三个 `Double` 类型且参数名合适的构造方法: + +> “Color provides an initializer with three appropriately named parameters of type Double:” + +``` +struct Color { + let red = 0.0, green = 0.0, blue = 0.0 + init(red: Double, green: Double, blue: Double) { + self.red = red + self.green = green + self.blue = blue + } +} +``` + +当你创建 `Color` 实例时,你通过这三种颜色名作为外部名称调用该构造方法: + +> “Whenever you create a new Color instance, you call its initializer using external names for each of the three color components:” + +``` +let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) +``` + +请注意,想不使用外部名称直接调用该构造方法是不可能的。外部名称一旦被定义,在调用时必须使用它,否则会产生编译错误: + +> “Note that it is not possible to call this initializer without using the external names. External names must always be used in an intializer if they are defined, and omitting them is a compile-time error:” + +``` +let veryGreen = Color(0.0, 1.0, 0.0) +// this reports a compile-time error - external names are required +``` + +#### 可选属性类型 (Optional Property Types) + +如果你有一个自定义类型的属性值允许设置为『空值』——可能因为该属性值不能在构造过程中赋值,也可能因为在某些时候允许为『空值』——定义该属性值为_可选_类型。可选类型的属性值会被自动初始化为 `nil`,申明该属性值在构造过程有意设定为『空值』。 + +> “If your custom type has a stored property that is logically allowed to have “no value”—perhaps because its value cannot be set during initialization, or because it is allowed to have “no value” at some later point—declare the property with an optional type. Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.” + +在接下来的例子中定义了一个 `SurveyQuestion` 的结构,其拥有一个可选的 `String` 类型属性值 `response`: + +> “The following example defines a class called SurveyQuestion, with an optional String property called response:” + +``` +class SurveyQuestion { + var text: String + var response: String? + init(text: String) { + self.text = text + } + func ask() { + println(text) + } +} +let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?") +cheeseQuestion.ask() +// prints "Do you like cheese?" +cheeseQuestion.response = "Yes, I do like cheese. +``` + +你无法知道一个调查问题的回应直到它被回答了,因此属性值 `response` 被声明为 `String?` 类型,或称 『可选 `String`』。当一个新的 `SurveyQuestion` 实例被初始化时,它会被自动分配默认值 `nil`,表示『空字符串』。 + +> “The response to a survey question cannot be known until it is asked, and so the response property is declared with a type of String?, or “optional String”. It is automatically assigned a default value of nil, meaning “no string yet”, when a new instance of SurveyQuestion is initialized.” + +### 在构造过程中修改恒定属性 (Modifying Constant Properties During Initialization) + +你可以在构造过程中的任意时候修改恒定属性的值,只要在构造过程结束前设定为一个明确的值。 + +> “You can modify the value of a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes.” + +提示: + +> 对于类的实例,一个恒定属性值只能在申明它的类初始化期间进行修改。它不能由子类修改。 + +> > “For class instances, a constant property can only be modified during initialization by the class that introduces it. It cannot be modified by a subclass.” + +你可以修改上面的 `SurveyQuestion` 例子,使问题的 `text` 属性使用一个恒定属性而不是一个可变属性,以表明一旦 `SurveyQuestion` 的实例被创建,问题属性便不能改变。即使 `text` 属性现在是一个恒定属性,它仍然可以在类的构造方法中设定: + +> “You can revise the SurveyQuestion example from above to use a constant property rather than a variable property for the text property of the question, to indicate that the question does not change once an instance of SurveyQuestion is created. Even though the text property is now a constant, it can still be set within the class’s initializer:” + +``` +class SurveyQuestion { + let text: String + var response: String? + init(text: String) { + self.text = text + } + func ask() { + println(text) + } +} +let beetsQuestion = SurveyQuestion(text: "How about beets?") +beetsQuestion.ask() +// prints "How about beets?" +beetsQuestion.response = "I also like beets. (But not with cheese.) +``` + +### 默认构造方法 (Default Initializers) + +Swift 为每个结构或基类提供了_默认构造方法_来为它们的所有属性值提供默认值,**?并不提供一个构造方法。默认构造方法简单的创建一个设定了所有属性值为默认值的新实例。 + +> “Swift provides a default initializer for any structure or base class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.” + +在接下来的例子中定义了一个叫 `ShoppingListItem` 的类,其封装了购物清单中一件商品的名字、数量和支付状态: + +> “This example defines a class called ShoppingListItem, which encapsulates the name, quantity, and purchase state of an item in a shopping list:” + +``` +class ShoppingListItem { + var name: String? + var quantity = 1 + var purchased = false +} +var item = ShoppingListItem() +``` + +因为 `ShoppingListItem` 类中所有的属性值都拥有默认值,且这是一个没有父类的基类,`ShoppingListItem` 自动获得了一个默认构造方法,该方法创建一个设定了所有属性值为默认值的新实例。(`name` 属性是一个可选的 `String` 类型属性,因此它自动获得默认值为 `nil`,尽管这个值并没有写在代码中。)上面的例子使用默认构造方法为 `ShoppingListItem` 类创建实例化对象,构造方法语法写作 `ShoppingListItem()` 并将此实例赋值给 `item` 变量。 + +> “Because all properties of the ShoppingListItem class have default values, and because it is a base class with no superclass, ShoppingListItem automatically gains a default initializer implementation that creates a new instance with all of its properties set to their default values. (The name property is an optional String property, and so it automatically receives a default value of nil, even though this value is not written in the code.) The example above uses the default initializer for the ShoppingListItem class to create a new instance of the class with initializer syntax, written as ShoppingListItem(), and assigns this new instance to a variable called item.” + +#### 结构类型的成员式构造方法 (Memberwise Initializers for Structure Types) + +除了上面提到的默认构造方法,如果结构类型为其所有属性值提供了默认值且没有自定义构造方法,那么该结构类型自动接收一个_成员式构造方法_。 + +> “In addition to the default initializers mentioned above, structure types automatically receive a memberwise initializer if they provide default values for all of their stored properties and do not define any of their own custom initializers.” + +成员式构造方法是初始化新结构实例的成员属性的一种简写方式。新实例的属性的初始值可以通过名称传递给成员式构造方法。 + +> “The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.” + +在接下来的例子中定义了一个叫 `Size` 的结构,其拥有两个属性值分别是 `width` 和 `height`。通过分配了 `0.0` 作为它们的默认值,两个属性均被推断为 `Double` 类型。 + +> “The example below defines a structure called Size with two properties called width and height. Both properties are inferred to be of type Double by assigning a default value of 0.0.” + +由于所有属性值均拥有默认值,`Size` 这个结构自动接受一个成员式构造方法 `init(width:height:)`,你可以通过这个成员式构造方法创建新的 `Size` 实例: + +> “Because both stored properties have a default value, the Size structure automatically receives an init(width:height:) memberwise initializer, which you can use to initialize a new Size instance:” + +``` +struct Size { + var width = 0.0, height = 0.0 +} +let twoByTwo = Size(width: 2.0, height: 2.0) +``` + +### 值类型的构造代理 (Initializer Delegation for Value Types) + +构造方法可以调用其他的构造方法作为实例构造过程的一部分。这个过程称为_构造代理_,其避免了在多个构造方法中的重复代码。“ + +> “Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.” + +构造代理的工作原理,以及哪些形式的代理是被允许的,针对值类型和类类型是不同的。值类型(结构和枚举)不支持继承,因此他们的构造代理过程比较简单,因为它们只能委托给由它们自己提供的其他构造方法。但是,类类型可以从其他类继承,如『继承』这一章节所描述。这意味着类有更多的责任去确保它们继承了的所有属性值在构造过程中被分配一个合适的值。这些责任将在接下来的『类的继承和初始化』中说明。 + +> “The rules for how initializer delegation works, and for what forms of delegation are allowed, are different for value types and class types. Value types (structures and enumerations) do not support inheritance, and so their initializer delegation process is relatively simple, because they can only delegate to another initializer that they provide themselves. Classes, however, can inherit from other classes, as described in Inheritance. This means that classes have additional responsibilities for ensuring that all stored properties they inherit are assigned a suitable value during initialization. These responsibilities are described in Class Inheritance and Initialization below.” + +对于值类型,当你自定义构造方法时,你使用 `self.init` 来关联同一值类型的其他构造方法。你只能在构造方法中调用 `self.init`。 + +> “For value types, you use self.init to refer to other initializers from the same value type when writing your own custom initializers. You can only call self.init from within an initializer.” + +请注意,如果你为值类型定义一个自定义构造方法,你将不再能够访问该类型的默认构造方法(或成员式构造方法,如果它是一个结构)。这个约束防止一种情况的出现,即你提供了一个更为复杂的构造方法来执行必要的设置,但有人却不小心绕过它使用了自动生成构造方法。 + +> “Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise structure initializer, if it is a structure) for that type. This constraint prevents a situation in which you provide a more complex initializer that performs additional essential setup is circumvented by someone accidentally using one of the automatic initializers instead.” + +提示: + +> 如果你期望你自定义的值类型能够被默认构造方法和成员式构造方法初始化,并且同时也能够被自定义构造方法初始化,把你的自定义构造方法写在扩展中,而不是该值类型源生实现中的一部分。请参见『扩展』章节了解更多信息。 + +> > “If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation. For more information, see Extensions.” + +在接下来的例子中定义了一个叫 `Rect` 的结构来表示一个几何矩形。该例子需要 `Size` 和 `Point` 这两个结构的支持,两个结构均使用 `0.0` 作为它们属性值的默认值: + +> “The following example defines a custom Rect structure to represent a geometric rectangle. The example requires two supporting structures called Size and Point, both of which provide default values of 0.0 for all of their properties:” + +``` +struct Size { + var width = 0.0, height = 0.0 +} +struct Point { + var x = 0.0, y = 0.0 +} +``` +你可以通过下面三种方式中的一种来初始化 `Rect` 结构: + +* 使用 `origin` 和 `size` 的默认属性值 +* 提供一个特定的原始点和大小 +* 提供一个中心点和大小 + +这些构造过程选项由三个自定义构造方法提供,它们均定义在 `Rect` 结构中: + +> “You can initialize the Rect structure below in one of three ways—by using its default zero-initialized origin and size property values, by providing a specific origin point and size, or by providing a specific center point and size. These initialization options are represented by three custom initializers that are part of the Rect structure’s definition:” + +``` +struct Rect { + var origin = Point() + var size = Size() + init() {} + init(origin: Point, size: Size) { + self.origin = origin + self.size = size + } + init(center: Point, size: Size) { + let originX = center.x - (size.width / 2) + let originY = center.y - (size.height / 2) + self.init(origin: Point(x: originX, y: originY), size: size) + } +} +``` +第一个 `Rect` 构造方法 `init()` 在功能上和没有提供自定义构造方法的默认构造方法一样。该构造方法的方法体是空的且不执行任何构造过程,由一对空的花括号 `{}` 表示。调用该构造方法将返回一个 `Rect` 实例,它的 `origin` 和 `size` 都被初始化为默认值 `Point(x: 0.0, y: 0.0)` 和 `Size(width: 0.0, height: 0.0)`: + +> “The first Rect initializer, init(), is functionally the same as the default initializer that the structure would have received if it did not have its own custom initializers. This initializer has an empty body, represented by an empty pair of curly braces {}, and does not perfom any initialization. Calling this initializer returns a Rect instance whose origin and size properties are both initialized with the default values of Point(x: 0.0, y: 0.0) and Size(width: 0.0, height: 0.0) from their property definitions:” + +``` +let basicRect = Rect() +// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0) +``` + +第二个 `Rect` 构造方法 `init(origin:size:)` 在功能上和没有提供自定义构造方法的成员式构造方法相同。该构造方法简单地将 `origin` 和 `size` 参数分配给恰当的属性值: + +> “The second Rect initializer, init(origin:size:), is functionally the same as the memberwise initializer that the structure would have received if it did not have its own custom initializers. This initializer simply assigns the origin and size argument values to the appropriate stored properties:” + +``` +let originRect = Rect(origin: Point(x: 2.0, y: 2.0), + size: Size(width: 5.0, height: 5.0)) +// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0) +``` + +第三个 `Rect` 构造方法 `init(center:size:)` 稍微复杂一些。它从基于中心点和大小值计算相应的原点开始。然后,它调用(或委托)到 `init(origin:size:)` 构造方法,将新的原点和大小保存到合适的属性中: + +> “The third Rect initializer, init(center:size:), is slightly more complex. It starts by calculating an appropriate origin point based on a center point and a size value. It then calls (or delegates) to the init(origin:size:) initializer, which stores the new origin and size values in the appropriate properties:” + +``` +let centerRect = Rect(center: Point(x: 4.0, y: 4.0), + size: Size(width: 3.0, height: 3.0)) +// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0) +``` + +尽管构造方法 `init(center:size:)` 也能够自己将新的 `origin` 和 `size` 分配给合适的属性。可是,构造方法 `init(center:size:)` 使用已经具有相同功能的构造函数来实现更简单(逻辑清楚)。 + +> “The init(center:size:) initializer could have assigned the new values of origin and size to the appropriate properties itself. However, it is more convenient (and clearer in intent) for the init(center:size:) initializer to take advantage of an existing initializer that already provides exactly that functionality.” + +提示: + +> 如果你想通过不定义 `init()` 和 `init(origin:size:)` 构造方法来替代例子中的方法,请见『扩展』章节。 + +> > “For an alternative way to write this example without defining the init() and init(origin:size:) initializers yourself, see Extensions.” + +### 类的继承与构造过程 + +一个类的所有属性值——包括所有它从父类继承的属性——_必须_在构造过程中分配初始值。 + +> “All of a class’s stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization.” + +Swift 定义了两种类类型的构造方法来保证所有的属性值接收到初始值。它们被称为指定构造方法和便利构造方法。 + +> “Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value. These are known as designated initializers and convenience initializers.” + +#### 指定构造方法和便利构造方法 (Designated Initializers and Convenience Initializers) + +_指定构造方法_是类的主要构造方法。一个指定构造方法完全初始化所有该类申明的属性并调用合适的父类构造方法来继续向上追溯构造过程的父类链。 + +> “Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.” + +类往往只有少数指定构造方法,而且只有一个指定构造方法也是很常见的。**?指定构造方法是通过构造过程发生的『漏斗』点,并通过其继续向上追溯构造过程的父类链。 + +> “Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.” + +每个类必须至少拥有一个指定构造方法。在某些情况下,这一要求由从父类继承一个或多个指定构造方法满足,这将在接下来的『自动构造方法继承』中说明。 + +> “Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described in Automatic Initializer Inheritance below.” + +_便利构造方法_是次要的,是类的辅助构造方法。你可以在同一个类下定义一个便利构造方法来调用指定构造方法,该便利构造方法定义了指定构造方法参数的默认值。你也可以定义一个便利构造方法用于创建特定用例或**?输入值类型的实例。 + +> “Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.” + +如果你的类不需要便利构造方法,你可以不提供它。每当一个通用构造过程模型的快捷方式能够节省事件或让该类的构造过程的意图更清晰,请使用创建便利构造方法。 + +> “You do not have to provide convenience initializers if your class does not require them. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.” + +#### 构造方法链 (Initializer Chaining) + +为了简化指定构造方法和便利构造方法的关系,在构造方法委托调用中 Swift 应用了以下三条规则: + +> “To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:” + +* __规则一:__指定构造方法只能从其直接父类中调用指定构造方法。 +* __规则二:__便利构造方法只能调用_该类自身_定义的其他构造方法。 +* __规则三:__便利构造方法必须在最终调用一个指定构造方法。 + +> * __Rule 1__ “Designated initializers must call a designated initializer from their immediate superclass.” +> * __Rule 2__ “Convenience initializers must call another initializer available in the same class.” +> * __Rule 3__ “Convenience initializers must ultimately end up calling a designated initializer.” + +一个简单的方法来记住这些: + +* **?指定构造方法必须总是_向上_委托 +* **?便利构造方法必须总是_穿过_委托 + +> “A simple way to remember this is:” +> +> * “Designated initializers must always delegate up.” +> * “Convenience initializers must always delegate across.” + +这些规则如下图所示: + +> “These rules are illustrated in the figure below:” + +**?插图[1] Page 408 + +在这里,父类只有一个指定构造方法和两个便利构造方法。一个便利构造方法调用了其中另一个便利构造方法,而该构造方法调用了唯一那个便利构造方法。这符合上面的规则 2 和 3。该父类自身没有父类,因此不适用规则 1。 + +> “Here, the superclass has a single designated initializer and two convenience initializers. One convenience initializer calls another convenience initializer, which in turn calls the single designated initializer. This satisfies rules 2 and 3 from above. The superclass does not itself have a further superclass, and so rule 1 does not apply.” + +上图中的子类拥有两个指定构造方法和一个便利构造方法。这个便利构造方法必须调用那两个指定构造方法中的一个,因为便利构造方法必须调用其类自身的其他构造方法。这符合上面的规则 2 和 3。该子类中两个指定构造方法都必须调用父类中唯一那个指定构造方法,来符合上面的规则 1。 + +> “The subclass in this figure has two designated initializers and one convenience initializer. The convenience initializer must call one of the two designated initializers, because it can only call another initializer from the same class. This satisfies rules 2 and 3 from above. Both designated initializers must call the single designated initializer from the superclass, to satisfy rule 1 from above.” + +提示: + +> 这些规则并不会影响你的类用户如何_创建_各类的实例。上图中的任何构造方法都能用于创建其所在类的完整实例。这些规则只会影响你如何写这些类的实现。 + +> > “These rules don’t affect how users of your classes create instances of each class. Any initializer in the diagram above can be used to create a fully-initialized instance of the class they belong to. The rules only affect how you write the class’s implementation.” + +下面的图显示了一个更为复杂的 4 个类的层次结构。它表现了指定构造方法如何在类构造过程中用作『漏斗』点,简化了这些类层级间的相互关联。 + +> “The figure below shows a more complex class hierarchy for four classes. It illustrates how the designated initializers in this hierarchy act as “funnel” points for class initialization, simplifying the interrelationships among classes in the chain:” + +**?插图[2] Page 410 + +#### 构造过程的两个阶段 (Two-Phase Initialization) + +在 Swift 中类的构造过程有两个阶段。在第一个阶段,每个属性值被申明它的类分配一个初始值。一旦每个属性值的初始状态被确认,第二阶段便开始,每个类能够在新实例被认为是可以使用之前进一步制定其属性值。 + +> “Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.” + +构造过程的两个阶段使构造过程更安全,同时仍然给予一个类层级中每个类以完全的灵活性。构造过程的两个阶段防止属性值在它们被初始化之前被访问,并防止属性值被另一个构造方法意外地设置为其他值。 + +> “The use of a two-phase initialization process makes initialization safe, while still giving complete flexibility to each class in a class hierarchy. Two-phase initialization prevents property values from being accessed before they are initialized, and prevents property values from being set to a different value by another initializer unexpectedly.” + +提示: + +> Swift 的构造过程的两个阶段和 Objective-C 的构造过程类似。主要的不同之处在第一阶段 Objective-C 分配 0 或 null (如 `0` 或 `nil`) 值给每个属性。Swift 的构造过程更加灵活,因此它可以让你设置自定义初始值,并可以应付 `0` 或 `nil` 不是一个有效默认值的类型。 + +> > “Swift’s two-phase initialization process is similar to initialization in Objective-C. The main difference is that during phase 1, Objective-C assigns zero or null values (such as 0 or nil) to every property. Swift’s initialization flow is more flexible in that it lets you set custom initial values, and can cope with types for which 0 or nil is not a valid default value.” + +Swift 的编译器执行了四个安全检查,来保证构造过程的两个阶段过程中没有错误: + +> “Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:” + +* __安全检查一:__指定构造方法必须保证其所在类申明的属性在向上委托到父类构造方法前被初始化。 + +> * __Safety Check 1__ “A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.” + +正如上面所提到的,一个对象的内存只会被初始化一次,该对象的所有属性值的初始化状态都是已知的。为了符合这条规范,一个指定构造方法必须保证其所有属性在它脱手给继承链前是已经被初始化了的。 + +> “As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all its own properties are initialized before it hands off up the chain.” + +* __安全检查二:__一个指定构造方法在给继承的属性赋值之前必须向上委托至一个父类的构造方法。如果不是这样,该指定构造方法分配的新值会被父类的构造过程覆盖。 +* __安全检查三:__一个便利构造方法必须在给_任何_属性(包裹其所在类定义的属性)赋值前委托给另一个构造方法。如果不是这样,该便利构造方法分配的新值会被其自身类委托的构造方法覆盖。 +* __安全检查四:__直到构造过程的第一阶段完成前,任何构造方法都不能调用实例方法,不能读取任何实例属性,或引用自身为一个值。 + +> * __Safety Check 2__ “A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.” +> * __Safety Check 3__ “A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.” +> * __Safety Check 4__ “An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.” + +在第一阶段完成前,类的实例并不是完全有效的。当在第一阶段结束时,类实例被认为是有效的,属性只能被访问,方法只能被调用,“ + +> “The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.” + +下面是构造过程的两个阶段如何基于上面的 4 个安全检查实现的: + +> “Here’s how two-phase initialization plays out, based on the four safety checks above:” + +##### 第一阶段 + +* 一个指定或便利构造方法在一个类中被调用。 +* 该类的一个新实例内存被分配。但内存还未被初始化。 +* 该类的一个指定构造方法确保该类中申明的所有属性值拥有一个值。这些属性值的内存现在被初始化了。 +* 该指定构造方法脱手给父类的构造方法来为它拥有的属性值执行相同的任务。 +* 该过程持续向上追溯继承链,直到到达链条顶端。 +* 一旦达到继承链的顶端,最终的类必须保证其所有的属性值都拥有一个值,然后该实例的内存被认为是完全初始化的,第一阶段结束。 + +> * “A designated or convenience initializer is called on a class.” +> * “Memory for a new instance of that class is allocated. The memory is not yet initialized.” +> * “A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.” +> * “The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.” +> * “This continues up the class inheritance chain until the top of the chain is reached.” +> * “Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.” + +##### 第二阶段 + +* 从继承链的顶端向下工作,继承链中每个指定构造方法都能够进一步自定义该实例。现在构造方法可以访问 `self` 并且能够修改其属性,调用实例方法,等等。 +* 最后,继承链中每个便利构造方法能够自定义该实例,并且使用 `self` 操作。 + +> * “Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.” +> * “Finally, any convenience initializers in the chain have the option to customize the instance and to work with self.” + +下图显示了第一阶段在假想的子类和父类中如何寻找构造过程的调用: + +> “Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:” + +**?插图[3] Page 415 + +在这个例子中,构造过程开始于对子类中便利构造方法的调用。该便利构造方法当前还不能修改任何属性。它委托给同一类下的一个指定构造方法。 + +> “In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.” + +该指定构造方法确保所有子类中的属性都拥有一个值,如安全检查 1。然后它调用了父类的一个指定构造方法来继续向上追溯构造过程。 + +> “The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.” + +父类的指定构造方法确保了父类的所有属性都拥有一个值。在没有往上的父类可以追溯,也不需要进一步的委托。 + +> “The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.” + +在所有的父类属性都拥有值的时候,内存被认为完全初始化,第一阶段结束。 + +> “As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.” + +下图显示了第二阶段如何寻找构造过程的调用: + +> “Here’s how phase 2 looks for the same initialization call:” + +**?插图[4] Page 416 + +父类的指定构造方法已经可以进一步自定义实例(这不是必须的)。 + +> “The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).” + +一旦父类的指定构造方法结束,子类的指定构造方法便可执行额外的自定义操作(同样的,这不是必须的)。 + +> “Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).” + +最后,一旦子类的指定构造方法结束,最早被调用的便利构造方法可以执行额外的自定义操作了。 + +> “Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.” + +#### 构造方法的继承与重写 (Initializer Inheritance and Overriding) + +与 Objective-C 的子类不同,Swift 的子类会默认继承它们父类的构造方法。Swift 可以防止一种情况:一个简单的父类构造方法被一个更专业的子类自动继承,并用于创建不完整或不正确的子类实例。 + +> “Unlike subclasses in Objective-C, Swift subclasses do not not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is automatically inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.” + +如果你希望你的自定义子类能够提供一个多个与父类相同的构造方法——可能在构造过程中需要一些自定义——你可以在你自定义的子类中提供一个相同构造方法的重写来实现。 + +> “If you want your custom subclass to present one or more of the same initializers as its superclass—perhaps to perform some customization during initialization—you can provide an overriding implementation of the same initializer within your custom subclass.” + +如果你重写的构造方法是一个指定构造方法,你可以在你的子类中重写它的实现,并且能够在子类的重写版方法中调用父类父类版方法。 + +> “If the initializer you are overriding is a designated initializer, you can override its implementation in your subclass and call the superclass version of the initializer from within your overriding version.” + +如果你重写的构造方法是一个便利构造方法,你重写的方法必须调用子类自身申明的另一个指定构造方法,正如前面『构造方法链』说明的规则那样。 + +> “If the initializer you are overriding is a convenience initializer, your override must call another designated initializer from its own subclass, as per the rules described above in Initializer Chaining.” + +提示: + +> 与方法、属性和下标不同,当你重写一个构造方法时不需要写 `override` 关键字。 + +> > “Unlike methods, properties, and subscripts, you do not need to write the override keyword when overriding an initializer.” + +#### 自动继承构造方法 (Automatic Initializer Inheritance) + +正如上面所描述的,子类会默认继承它们父类的构造方法。可是,在某些情况下,父类的构造方法会被自动继承。在实践中,**?这意味着​​在许多常见情况下你不需要重写构造方法,并且可以最小影响地继承父类的构造方法,这样做是安全。 + +> “As mentioned above, subclasses do not not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.” + +假设你为你的子类中申明的任何新属性提供默认值,有下面两个规则要遵守: + +> “Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:” + +* __规则一:__如果你的子类没有定义任何指定构造方法,它会自动继承父类所有的指定构造方法。 +* __规则二:__如果你的子类提供了其父类所有的指定构造方法的实现——或者仅仅如『规则一』所说的继承它们,或者在其自身定义中提供自定义的实现——那么该子类会自动继承父类所有的便利构造方法。 + +> * __Rule 1__ “If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.” +> * __Rule 2__ “If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.” + +即使你在子类中添加了更多的便利构造方法,这两个规则依然适用。 + +> “These rules apply even if your subclass adds further convenience initializers.” + +提示: + +> 一个子类可以实现一个父类的指定构造方法为子类的一个便利构造方法,这是符合『规则二』的。 + +> > “A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.” + +#### 指定与便利构造方法的语法 (Syntax for Designated and Convenience Initializers) + +类的指定构造方法的编写方法如值类型的构造方法一样简单: + +> “Designated initializers for classes are written in the same way as simple initializers for value types:” + +``` +init(parameters) { + statements +} +``` + +便利构造方法的编写方法也是如此,但必须在 `init` 关键字前面加上 `convenience` 关键字,并以空格分隔: + +> “Convenience initializers are written in the same style, but with the convenience keyword placed before the init keyword, separated by a space:” + +``` +convenience init(parameters) { + statements +} +``` + +#### 指定与便利构造方法的行为 (Designated and Convenience Initializers in Action) + +在接下来的例子显示了指定构造方法,便利构造方法和自动继承的构造方法的行为。这个例子定义了叫 `Food`,`RecipeIngredient` 和 `ShoppingListItem` 三个类的层级关系,并示范了它们之间是如何相互作用的。 + +> “The following example shows designated initializers, convenience initializers, and automatic initializer inheritance in action. This example defines a hierarchy of three classes called Food, RecipeIngredient, and ShoppingListItem, and demonstrates how their initializers interact.” + +该层级中的基类是 `Food`,它是一个仅仅描述了食物名字的简单类。`Food` 类仅有一个 `String` 类型的属性 `name`,并有两个构造方法来创造 `Food` 的实例: + +> “The base class in the hierarchy is called Food, which is a simple class to encapsulate the name of a foodstuff. The Food class introduces a single String property called name and provides two initializers for creating Food instances:” + +``` +class Food { + var name: String + init(name: String) { + self.name = name + } + convenience init() { + self.init(name: "[Unnamed]") + } +} +``` + +下图显示了 `Food` 类的构造方法链: + +> “The figure below shows the initializer chain for the Food class:” + +**?插图[5] Page 421 + +类并没有默认的成员式构造方法,因此 `Food` 类提供了一个仅拥有一个 `name` 参数的指定构造方法。该构造方法可以用来创建指定了名字的 `Food` 实例: + +``` +let namedMeat = Food(name: "Bacon") +// namedMeat's name is "Bacon" +``` + +在 `Food` 类中的 `init(name: String)` 构造方法是一个_指定构造方法_,因为它保证了 `Food` 实例中的所有属性值是完全初始化的。`Food` 并没有父类,因此构造方法 `init(name: String)` 并不需要在其构造过程中调用 `super.init()`。 + +> “The init(name: String) initializer from the Food class is provided as a designated initializer, because it ensures that all stored properties of a new Food instance are fully initialized. The Food class does not have a superclass, and so the init(name: String) initializer does not need to call super.init() to complete its initialization.” + +该 `Food` 类也提供了一个没有参数的_便利构造方法_ `init()`。构造方法 `init()` 通过将赋值为 `[Unnamed]` 的 `name` 参数委托给 `Food` 类的 `init(name: String)`,提供了食物的默认名字: + +> “The Food class also provides a convenience initializer, init(), with no arguments. The init() initializer provides a default placeholder name for a new food by delegating across to the Food class’s init(name: String) with a name value of [Unnamed]:” + +``` +let mysteryMeat = Food() +// mysteryMeat's name is "[Unnamed]" +``` + +该层级中的第二个类是 `Food` 的一个子类,叫做 `RecipeIngredient`。该 `RecipeIngredient` 类创建了一个烹饪食谱的原料模型。它申明了一个 `Int` 类型的属性 `quantity`(从 `Food` 中继承了 `name` 属性)并定义了两个构造方法来创建 `RecipeIngredient` 实例: + +> “The second class in the hierarchy is a subclass of Food called RecipeIngredient. The RecipeIngredient class models an ingredient in a cooking recipe. It introduces an Int property called quantity (in addition to the name property it inherits from Food) and defines two initializers for creating RecipeIngredient instances:” + +``` +class RecipeIngredient: Food { + var quantity: Int + init(name: String, quantity: Int) { + self.quantity = quantity + super.init(name: name) + } + convenience init(name: String) { + self.init(name: name, quantity: 1) + } +} +``` + +下图显示了 `RecipeIngredient` 类的构造方法链: + +> “The figure below shows the initializer chain for the RecipeIngredient class:” + +**?插图[6] Page 423 + +类 `RecipeIngredient` 拥有唯一一个指定构造方法 `init(name: String, quantity: Int)`,它可以完成新 `RecipeIngredient` 实例的所有属性的填充。该构造方法开始于将传入的 `quantity` 参数赋值给在 `RecipeIngredient` 中申明的新属性 `quantity`。而后,该构造方法向上委托给 `Food` 的构造方法 `init(name: String)`。该过程符合之前『构造过程的两个阶段』章节中的安全检查 1。 + +> “The RecipeIngredient class has a single designated initializer, init(name: String, quantity: Int), which can be used to populate all of the properties of a new RecipeIngredient instance. This initializer starts by assigning the passed quantity argument to the quantity property, which is the only new property introduced by RecipeIngredient. After doing so, the initializer delegates up to the init(name: String) initializer of the Food class. This process satisfies safety check 1 from Two-Phase Initialization above.” + +同时 `RecipeIngredient` 定义了一个便利构造方法 `init(name: String)`,它用于只使用名字来创建 `RecipeIngredient` 实例。该便利构造方法假定了所有没有明确数量的 `RecipeIngredient` 实例的数量为 `1`。该构造方法的定义使创建 `RecipeIngredient` 更加便捷快速,并能够避免创造多个单数量 `RecipeIngredient` 实例时产生的代码冗余。该便利构造方法简单得委托给了其所在类的指定构造方法。 + +> “RecipeIngredient also defines a convenience initializer, init(name: String), which is used to create a RecipeIngredient instance by name alone. This convenience initializer assumes a quantity of 1 for any RecipeIngredient instance that is created without an explicit quantity. The definition of this convenience initializer makes RecipeIngredient instances quicker and more convenient to create, and avoids code duplication when creating several single-quantity RecipeIngredient instances. This convenience initializer simply delegates across to the class’s designated initializer.” + +请注意,`RecipeIngredient` 类提供的便利构造方法 `init(name: String)` 使用了与 `Food` 类中制_指定构造方法_ `init(name: String)` 一样的参数。尽管 `RecipeIngredient` 将其作为便利构造方法,`RecipeIngredient` 仍然提供了其父类所有指定构造方法的实现。因此,`RecipeIngredient` 自动继承了其父类所有的便利构造方法。 + +> “Note that the init(name: String) convenience initializer provided by RecipeIngredient takes the same parameters as the init(name: String) designated initializer from Food. Even though RecipeIngredient provides this initializer as a convenience initializer, RecipeIngredient has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient automatically inherits all of its superclass’s convenience initializers too.” + +在这个例子中,`RecipeIngredient` 的父类是 `Food`,`Food` 只有一个叫 `init()` 的便利构造方法。因此该构造方法被 `RecipeIngredient` 所继承。继承了的 `init()` 方法和 `Food` 中的一样,只是它委托的是 `RecipeIngredient` 中的 `init(name: String)` 而不是 `Food` 中的。 + +> “In this example, the superclass for RecipeIngredient is Food, which has a single convenience initializer called init(). This initializer is therefore inherited by RecipeIngredient. The inherited version of init() functions in exactly the same way as the Food version, except that it delegates to the RecipeIngredient version of init(name: String) rather than the Food version.” + +这三种构造方法均可以用于创建 `RecipeIngredient` 对象: + +> “All three of these initializers can be used to create new RecipeIngredient instances:” + +``` +let oneMysteryItem = RecipeIngredient() +let oneBacon = RecipeIngredient(name: "Bacon") +let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) +``` + +第三个并且是最后一个类是 `RecipeIngredient` 的子类,叫做 `ShoppingListItem`。该 `ShoppingListItem` 类创建了一个食谱购物清单的模型。 + +> “The third and final class in the hierarchy is a subclass of RecipeIngredient called ShoppingListItem. The ShoppingListItem class models a recipe ingredient as it appears in a shopping list.” + +购物清单中的所有商品的初始状态都是『未支付』。为了实现它,`ShoppingListItem` 申明了一个默认值为 `false` 的 `Boolean` 的属性 `purchased`。同时,`ShoppingListItem` 也申明了一个计算过的 `description` 属性,它提供了对 `ShoppingListItem` 实例的文本描述: + +> “Every item in the shopping list starts out as “unpurchased”. To represent this fact, ShoppingListItem introduces a Boolean property called purchased, with a default value of false. ShoppingListItem also adds a computed description property, which provides a textual description of a ShoppingListItem instance:” + +``` +class ShoppingListItem: RecipeIngredient { + var purchased = false + var description: String { + var output = "\(quantity) x \(name.lowercaseString)" + output += purchased ? " ✔" : " ✘" + return output + } +} +``` + +提示: + +> `ShoppingListItem` 并没有定义为 `purchased` 提供初始值的构造方法,因为在购物清单中的商品(按这里的模型)的初始状态总是未支付。 + +> > “ShoppingListItem does not define an initializer to provide an initial value for purchased, because items in a shopping list (as modeled here) always start out unpurchased.” + +因为 `ShoppingListItem` 为所有其申明的属性均提供了默认值,并且其自身没有定义任何构造方法。`ShoppingListItem` 自动继承了其父类的所有指定和便利构造方法。 + +> “Because it provides a default value for all of the properties it introduces and does not define any initializers itself, ShoppingListItem automatically inherits all of the designated and convenience initializers from its superclass.” + +下图显示了这三个类的构造方法链的概览: + +> “The figure below shows the overall initializer chain for all three classes:” + +**?插图[7] Page 427 + +这三个继承的构造方法均可以用于创建 `ShoppingListItem` 对象: + +> “You can use all three of the inherited initializers to create a new ShoppingListItem instance:” + +``` +var breakfastList = [ + ShoppingListItem(), + ShoppingListItem(name: "Bacon"), + ShoppingListItem(name: "Eggs", quantity: 6), +] +breakfastList[0].name = "Orange juice" +breakfastList[0].purchased = true +for item in breakfastList { + println(item.description) +} +// 1 x orange juice ✔ +// 1 x bacon ✘ +// 6 x eggs ✘ +``` + +至此,一个包含了三个新 `ShoppingListItem` 实例的数组 `breakfastList` 被创建出来。这个数组的类型被推断为 `ShoppingListItem[]`。在该数组被创建后,该数组的第一个 `ShoppingListItem` 实例的名字从 `[Unnamed]` 改变为 `Orange`,并且其被标记为已支付状态。输出数组中每个实例的详细描述,显示出它们的默认状态均被按预期地设置了。 + +> “Here, a new array called breakfastList is created from an array literal containing three new ShoppingListItem instances. The type of the array is inferred to be ShoppingListItem[]. After the array is created, the name of the ShoppingListItem at the start of the array is changed from "[Unnamed]" to "Orange juice" and it is marked as having been purchased. Printing the description of each item in the array shows that their default states have been set as expected.” + +### 通过闭包或方法设置默认属性 (Setting a Default Property Value with a Closure or Function) + +如果一个属性值的默认值需要一些自定义或者设置,你可以使用闭包或者全局方法来为该属性提供自定义默认值。无论该属性所在类的实例何时被初始化,闭包或方法会被调用,并且其返回值会被分配给该属性作为默认值。 + +> “If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.” + +这种类型的闭包和方法为该属性创建了一个相同类型的临时值,**?定制的该值是初始状态所期望的,然后返回这个临时值作为该属性的默认值。 + +> “These kinds of closures or functions typically create a temporary value of the same type as the property, tailor that value to represent the desired initial state, and then return that temporary value to be used as the property’s default value.” + +下面是一个如何使用闭包给属性提供默认值的概览: + +> “Here’s a skeleton outline of how a closure can be used to provide a default property value:” + +``` +class SomeClass { + let someProperty: SomeType = { + // create a default value for someProperty inside this closure + // someValue must be of the same type as SomeType + return someValue + }() +} +``` + +请注意,闭包结束的花括号后面紧跟着一堆空的圆括号。这会告诉 Swift 立即执行该闭包。如果你漏掉了圆括号,你会把闭包本身赋值给该属性,而不是这个闭包的返回值。 + +> “Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.” + +提示: + +> 如果你使用一个闭包来初始化一个属性,请记住,在该闭包被执行的时候该实例的其他部分还没有被初始化。这意味着你无法在闭包中访问任何其他属性,即使这些属性有默认值也不行。你也不能使用 `self` 属性,或调用任何实例方法。 + +> > “If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.” + +在接下来的例子中定义了一个叫 `Checkerboard` 的结构,它创建了一个_跳棋_游戏桌面的模型: + +> “The example below defines a structure called Checkerboard, which models a board for the game of Checkers (also known as Draughts):” + +**?插图[8] Page 431 + +_跳棋_游戏在一个黑白相间的 10x10 方格的桌面上进行。为了表示这个游戏桌面,`Checkerboard` 结构有唯一一个属性 `boardColors`,该属性是一个拥有 100 个 `Bool` 值的数组。数组中的 `true` 值表示一个黑色方块,`false` 表示一个白色方块。数组中的第一个对象表示桌面左上角的方块,最后一个对象表示桌面右下角的方块。 + +> “The game of Checkers is played on a ten-by-ten board, with alternating black and white squares. To represent this game board, the Checkerboard structure has a single property called boardColors, which is an array of 100 Bool values. A value of true in the array represents a black square and a value of false represents a white square. The first item in the array represents the top left square on the board and the last item in the array represents the bottom right square on the board.” + +`boardColors` 数组由一个闭包来初始化其颜色值: + +> “The boardColors array is initialized with a closure to set up its color values:” + +``` +struct Checkerboard { + let boardColors: Bool[] = { + var temporaryBoard = Bool[]() + var isBlack = false + for i in 1...10 { + for j in 1...10 { + temporaryBoard.append(isBlack) + isBlack = !isBlack + } + isBlack = !isBlack + } + return temporaryBoard + }() + func squareIsBlackAtRow(row: Int, column: Int) -> Bool { + return boardColors[(row * 10) + column] + } +} +``` + +每当一个新的 `Checkerboard` 实例创建时,该闭包被执行,`boardColors` 的默认值被计算并返回。上面这个例子中的闭包为游戏桌面的每个方块计算并设置合适的颜色值存放在叫 `temporaryBoard` 的临时数组中,并在设置结束后返回这个临时数组作为返回值。该返回的数组保存在 `boardColors` 中,并能够在实例方法 `squareIsBlackAtRow` 中使用。 + +> “Whenever a new Checkerboard instance is created, the closure is executed, and the default value of boardColors is calculated and returned. The closure in the example above calculates and sets the appropriate color for each square on the board in a temporary array called temporaryBoard, and returns this temporary array as the closure’s return value once its setup is complete. The returned array value is stored in boardColors and can be queried with the squareIsBlackAtRow utility function:” + +``` +let board = Checkerboard() +println(board.squareIsBlackAtRow(0, column: 1)) +// prints "true" +println(board.squareIsBlackAtRow(9, column: 9)) +// prints "false" +``` + + + + + diff --git a/src/chapter2/15_Deinitialization.md b/src/chapter2/15_Deinitialization.md index e69de29..e265c80 100644 --- a/src/chapter2/15_Deinitialization.md +++ b/src/chapter2/15_Deinitialization.md @@ -0,0 +1,170 @@ +======= +# 析构过程 (Deinitialization) + +A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how intializers are written with the init keyword. Deinitializers are only available on class types.” + +> 析构函数会在一个类的实例被释放之前被立即调用。用`deinit`关键词来声明析构函数,用`init`来标示类似于初始化的函数。析构函数仅在类中有效。 + +#### How Deinitialization Works + +> #### 析构过程是如何工作的 + +Swift automatically deallocates your instances when they are no longer needed, to free up resources. Swift handles the memory management of instances through automatic reference counting (ARC), as described in Automatic Reference Counting. Typically you don’t need to perform manual clean-up when your instances are deallocated. However, when you are working with your own resources, you might need to perform some additional clean-up yourself. For example, if you create a custom class to open a file and write some data to it, you might need to close the file before the class instance is deallocated. + +> Swift 会自动释放不再需要的实例以释放资源。如自动引用计数那一章描述,Swift 通过自动引用计数(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。 + + +Class definitions can have at most one deinitializer per class. The deinitializer does not take any parameters and is written without parentheses: + +> 在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号: + +```` +deinit { + // 执行析构过程 +} +```` + + +Deinitializers are called automatically, just before instance deallocation takes place. You are not allowed to call a deinitializer yourself. Superclass deinitializers are inherited by their subclasses, and the superclass deinitializer is called automatically at the end of a subclass deinitializer implementation. Superclass deinitializers are always called, even if a subclass does not provide its own deinitializer. + + +> 析构函数会自动在实例释放之前被调用,且不支持手动调用。子类会继承父类的析构函数,在子类析构函数实现的之后,将自动调用父类的析构函数。即便子类没有提供自身的析构函数,其父类的析构函数也总是被调用。 + + +Because an instance is not deallocated until after its deinitializer is called, a deinitializer can access all properties of the instance it is called on and can modify its behavior based on those properties (such as looking up the name of a file that needs to be closed). + +> 因为实例直到实例的析构函数被调用时才会被释放,所以析构函数有权访问所有被调用实例的属性,同时根据那些属性修改其行为(例如查找一个需被关闭文件的名称)。 + + + +#### Deinitializers in Action + +> #### 析构函数操作 + + +Here’s an example of a deinitializer in action. This example defines two new types, Bank and Player, for a simple game. The Bank structure manages a made-up currency, which can never have more than 10,000 coins in circulation. There can only ever be one Bank in the game, and so the Bank is implemented as a structure with static properties and methods to store and manage its current state:” + +> 下面是一个析构函数操作的例子。它是一个简单的游戏,定义了两种新类型,Bank和Player。Bank结构管理一个虚拟货币的流通,但是它不能拥有超过10,000单位的货币。游戏中只能有一个Bank存在,所以使用带有静态属性和静态方法的结构的方式来实现Bank,用于储存和管理其当前状态。 + + +```` +struct Bank { + static var coinsInBank = 10_000 + static func vendCoins(var numberOfCoinsToVend: Int) -> Int { + numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) + coinsInBank -= numberOfCoinsToVend + return numberOfCoinsToVend + } + static func receiveCoins(coins: Int) { + coinsInBank += coins + } +} +```` + + +Bank keeps track of the current number of coins it holds with its coinsInBank property. It also offers two methods—vendCoins and receiveCoins—to handle the distribution and collection of coins. + +> Bank根据它的coinsInBank属性来侦听当前存有的货币数。除此之外Bank还提供两个其它方法,分别是receiveCoins和vendCoins,用于处理货币的存取。 + + + +vendCoins checks that there are enough coins in the bank before distributing them. If there are not enough coins, Bank returns a smaller number than the number that was requested (and returns zero if no coins are left in the bank). vendCoins declares numberOfCoinsToVend as a variable parameter, so that the number can be modified within the method’s body without the need to declare a new variable. It returns an integer value to indicate the actual number of coins that were provided. + +> vendCoins方法在 bank 取出货币之前检查是否有足够的量。如果货币不足,Bank则返会所有的剩余量(如果货币量为空则返回 0)。vendCoins方法中将numberOfCoinsToVend声明为一个变量,这样就可以在其它方法中对它进行修改,而无需定义一个新的变量。最终vendCoins方法将为取出的货币返回一个整型值。 + + + +The receiveCoins method simply adds the received number of coins back into the bank’s coin store. + +> 而 receiveCoins 方法更加简单, 将 bank 的货币存量和接收到的货币量相加后再保存回 bank 的货币存量。 + + + +The Player class describes a player in the game. Each player has a certain number of coins stored in their purse at any time. This is represented by the player’s coinsInPurse property: + +> 这里Player类定义了游戏中的一个玩家。随时都可能会有若干数量的货币存入每一个玩家的钱包中。通过 player 的coinsInPurse 属性可以看到这一值: + + + +```` +class Player { + var coinsInPurse: Int + init(coins: Int) { + coinsInPurse = Bank.vendCoins(coins) + } + func winCoins(coins: Int) { + coinsInPurse += Bank.vendCoins(coins) + } + deinit { + Bank.receiveCoins(coinsInPurse) + } +} +```` + +Each Player instance is initialized with a starting allowance of a specified number of coins from the bank during initialization, although a Player instance may receive fewer than that number if not enough coins are available. + +> 每一个Player都由一个初始化函数加上指定货币量入参实例化得到,他们将在初始化的过程中从 bank 获得货币。如果 Bank 没有足够的货币存量用于支付,Player 取得的货币量可能会比指定的少。 + + + +The Player class defines a winCoins method, which retrieves a certain number of coins from the bank and adds them to the player’s purse. The Player class also implements a deinitializer, which is called just before a Player instance is deallocated. Here, the deinitializer simply returns all of the player’s coins to the bank: + +> Player类还定义了一个winCoins的方法,该方法可以让player从bank赢取一定数量的硬币存入自己钱包。同时Player类还实现了一个析构函数,它在Player实例释放前一步会被调用。这里的析构函数很简单:将所有player的货币都返还给银行: + + + + +```` +var playerOne: Player? = Player(coins: 100) +println("A new player has joined the game with \(playerOne!.coinsInPurse) coins") +// 打印 "新玩家加入游戏,为其分配100货币" +println("There are now \(Bank.coinsInBank) coins left in the bank") +// 打印 "目前银行货币存量为: 9900" +```` + + +A new Player instance is created, with a request for 100 coins if they are available. This Player instance is stored in an optional Player variable called playerOne. An optional variable is used here, because players can leave the game at any point. The optional lets you track whether there is currently a player in the game. + +> 这里创建了一个新的Player实例,如果银行存量充足他将获取100货币。由于玩家可能随时离开游戏,所以将Player实例存储在一个名为playerOne的可选Player变量中。这样您可以侦听当前是否还有玩家在游戏中。 + + + + + +Because playerOne is an optional, it is qualified with an exclamation mark (!) when its coinsInPurse property is accessed to print its default number of coins, and whenever its winCoins method is called: + +> 因为playerOne是可选的,所以由一个感叹号(!)来作前缀,每当其winCoins方法被调用时,会访问其coinsInPurse属性并打印出他的货币数目。 + + + + + +```` +playerOne!.winCoins(2_000) +println("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins") +// 打印 "PlayerOne 赢取 2000 货币,目前他拥有 2100 个货币" +println("The bank now only has \(Bank.coinsInBank) coins left") +// 打印 "目前银行货币存量为: 7900" +```` + + + + +Here, the player has won 2,000 coins. The player’s purse now contains 2,100 coins, and the bank has only 7,900 coins left. + +> 以上代码运行后,player 赢取了 2,000 货币。他的钱包目前有 2,100 个货币,bank 剩余 7,900 个货币。 + + + +```` +playerOne = nil +println("PlayerOne has left the game") +// 打印 "PlayerOne 离开了游戏" +println("The bank now has \(Bank.coinsInBank) coins") +// 打印 "目前银行货币存量为: 10000" +```` + + +The player has now left the game. This is indicated by setting the optional playerOne variable to nil, meaning “no Player instance.” At the point that this happens, the playerOne variable’s reference to the Player instance is broken. No other properties or variables are still referring to the Player instance, and so it is deallocated in order to free up its memory. Just before this happens, its deinitializer is called automatically, and its coins are returned to the bank. + +> 现在player要离开这个游戏。这将把可选的playerOne变量设置为nil,可以理解为“没有Player实例”的意思。这时playerOne变量对Player实例的引用断开。再没有其它属性或者变量还会引用player实例,为了清空内存,它将被释放掉。析构函数会在这一切发生前自动被触发调用,之后所有的货币将回归到银行,这还真是个游戏。 diff --git a/src/chapter2/16_Automatic_Reference_Counting.md b/src/chapter2/16_Automatic_Reference_Counting.md index e69de29..2f2ef4c 100644 --- a/src/chapter2/16_Automatic_Reference_Counting.md +++ b/src/chapter2/16_Automatic_Reference_Counting.md @@ -0,0 +1,748 @@ +# 自动引用计数 + +Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed. + +However, in a few cases ARC requires more information about the relationships between parts of your code in order to manage memory for you. This chapter describes those situations and shows how you enable ARC to manage all of your app’s memory. + +Swift 使用自动引用计数(ARC)机制跟踪和管理APP的内存使用,大部分情况下,此机制是自动工作的,你没有必要考虑内存管理的事情。当一个类实例不再需要的时候,ARC会自动释放其占用的内存。 + +但少数情况下,ARC需要更多关于你代码之间的关系信息来帮你管理内存。本章介绍了这些情况,并向你示范如何启用ARC来管理APP的全部内存。 + +> NOTE +> +> Reference counting only applies to instances of classes. Structures and enumerations are value types, not reference types, and are not stored and passed by reference. + +> 注意 +> +> 引用计数仅适用于类实例。结构体和枚举类型是值类型而非引用类型, 也不是按照引用的方式存储和传递的,所以不适用引用计数机制。 + +## ARC是如何工作的 +Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. This memory holds information about the type of the instance, together with the values of any stored properties associated with that instance. + +Additionally, when an instance is no longer needed, ARC frees up the memory used by that instance so that the memory can be used for other purposes instead. This ensures that class instances do not take up space in memory when they are no longer needed. + +当创建一个类实例的时候,ARC会为之分配一块内存来存储该实例的相关信息。这些信息包括:该实例的类型及相关的属性和值。当该实例不再需要的时候,ARC会释放其占用的内存留作他用。这种机制保证了不需要的类实例占用的内存会及时得到释放。 + +However, if ARC were to deallocate an instance that was still in use, it would no longer be possible to access that instance’s properties, or call that instance’s methods. Indeed, if you tried to access the instance, your app would most likely crash. + +To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists. + +然而,如果ARC释放了一个正在使用的类实例的内存,那会导致该实例的方法和属性都不能访问。而且,如果你试图访问已被ARC释放的实例,你的APP多半会崩溃。 + +为了确保使用中的类实例不会无端消失(被ARC回收),ARC会跟踪和统计每个类实例被多少属性,常量,变量所引用。即使只有一个活动引用存在,该类实例也不会被ARC回收。 + +To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong“ reference because it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains. + +为了保证ARC机制的有效运行,无论何时你将一个类实例分配给一个属性、常量或一个变量,那么这个属性、常量或变量与类实例之间将建立起一种强引用关系。之所以称为“强引用”,是它会牢牢保持与类实例的引用关系,只要指向类实例的强引用存在,该实例就是不允许被销毁的。 + +## ARC 实践 +Here’s an example of how Automatic Reference Counting works. This example starts with a simple class called Person, which defines a stored constant property called name: + +下面的例子展示了自动引用计数是如何工作的。这是一个名为Person的类,其内部定义了一个名为name的属性: + +``` +class Person { + let name: String + init(name: String) { + self.name = name + println("\(name) is being initialized") + } + deinit { + println("\(name) is being deinitialized") + } +} +``` +The Person class has an initializer that sets the instance’s name property and prints a message to indicate that initialization is underway. The Person class also has a deinitializer that prints a message when an instance of the class is deallocated. + + +Person类有一个构造函数init,负责为其实例属性name赋值并打印出一条信息表明初始化过程正在进行。 +Person类也有一个析构函数deinit,会在Person的实例被销毁时打印出一条信息。 + +The next code snippet defines three variables of type Person?, which are used to set up multiple references to a new Person instance in subsequent code snippets. Because these variables are of an optional type (Person?, not Person), they are automatically initialized with a value of nil, and do not currently reference a Person instance. + +下面的代码片段定义了三个类型为Person?的变量,在随后的代码中,它们将用于设置指向一个Person类实例的多个引用。 +由于这些变量是可选类型(类型为Person?,不是Person),他们将自动初始化为nil(即初始值为nil),而不是引用到Person的实例。 + +``` +var reference1: Person? +var reference2: Person? +var reference3: Person? +``` + +You can now create a new Person instance and assign it to one of these three variables: + +现在创建Person类的新实例,并且将它赋值给三个变量中的一个: + +``` +reference1 = Person(name: "John Appleseed") +// prints "John Appleseed is being initialized” +``` + +Note that the message "John Appleseed is being initialized" is printed at the point that you call the Person class’s initializer. This confirms that initialization has taken place. + +注意,当调用Person类的构造函数的时候,此消息:"John Appleseed is being initialized"将会被打印出来。这表明初始化已经完成。 + +Because the new Person instance has been assigned to the reference1 variable, there is now a strong reference from reference1 to the new Person instance. Because there is at least one strong reference, ARC makes sure that this Person is kept in memory and is not deallocated. + +由于该Person实例被赋值给了变量reference1,因此建立了一个由reference1指向该Person实例的强引用。 +因为至少有一个强引用,所以ARC会保证该Person实例保持在内存中不被销毁,。 + +If you assign the same Person instance to two more variables, two more strong references to that instance are established: + +如果你将该实例赋值给多个变量,那么就会建立指向该实例的多个强引用: + +``` +reference2 = reference1 +reference3 = reference1 +``` + +There are now three strong references to this single Person instance. + +现在这个Person实例已经有三个强引用了。 + +If you break two of these strong references (including the original reference) by assigning nil to two of the variables, a single strong reference remains, and the Person instance is not deallocated: + +如果你通过给任意两个变量赋值nil的方式断开两个强引用(包括原始引用),留下一个强引用,该Person实例也不会被销毁: + +``` +reference2 = nil +reference3 = nil +``` +ARC does not deallocate the Person instance until the third and final strong reference is broken, at which point it is clear that you are no longer using the Person instance: + +直到第三个也是最后一个强引用断开,即你明确不再需要该实例的时候,ARC才会销毁该Person实例。 + +``` +reference3 = nil +// prints "John Appleseed is being deinitialized" +``` + +## 类实例间的循环强引用 + +In the examples above, ARC is able to track the number of references to the new Person instance you create and to deallocate that Person instance when it is no longer needed. + +在上面的例子中,ARC能够跟踪指向Person实例的引用个数,并且在该Person实例不再需要的时候销毁它。 + +However, it is possible to write code in which an instance of a class never gets to a point where it has zero strong references. This can happen if two class instances hold a strong reference to each other, such that each instance keeps the other alive. This is known as a strong reference cycle. + +然而,我们可能会写出这样的代码,导致类实例永远不会有0个强引用。这种情况发生在两个类实例互相持有对方的强引用,以致于彼此都无法被销毁的时候。这就是所谓的循环强引用。 + +You resolve strong reference cycles by defining some of the relationships between classes as weak or unowned references instead of as strong references. This process is described in Resolving Strong Reference Cycles Between Class Instances. However, before you learn how to resolve a strong reference cycle, it is useful to understand how such a cycle is caused. + +你可以通过定义类之间的关系为弱引用或者无主引用的方式来解决循环强引用的问题。具体过程将在[解决类实例之间的循环强引用]()中详述。不管怎样,在学习怎样解决循环强引用之前,很有必要了解一下它是如何产生的。 + +Here’s an example of how a strong reference cycle can be created by accident. +This example defines two classes called Person and Apartment, which model a block of apartments and its residents: + +这是一个意外导致循环强引用的例子。例子定义了名为Person和Apartment的两个类,用来建立公寓和公寓居民的数据模型: + +``` +class Person { + let name: String + init(name: String) { self.name = name } + var apartment: Apartment? + deinit { println("\(name) is being deinitialized") } +} + +class Apartment { + let number: Int + init(number: Int) { self.number = number } + var tenant: Person? + deinit { println("Apartment #\(number) is being deinitialized") } +} +``` + +Every Person instance has a name property of type String and an optional apartment property that is initially nil. The apartment property is optional, because a person may not always have an apartment. + +每一个Person实例都有一个String类型的属性name和一个初始化为nil的可选类型属性apartment。之所以将apartment定义为可选类型,是因为某人可能不总是租公寓。 + +Similarly, every Apartment instance has a number property of type Int and has an optional tenant property that is initially nil. The tenant property is optional because an apartment may not always have a tenant. + +类似的,每一个Apartment实例都有一个Int类型的属性number,和一个初始化为nil的可选类型属性tenant。之所以将tenant定义为可选类型,是因为某公寓也不总是有租客住。 + +Both of these classes also define a deinitializer, which prints the fact that an instance of that class is being deinitialized. This enables you to see whether instances of Person and Apartment are being deallocated as expected. + +两个类都定义了析构函数,它们将在类实例被析构时输出一段信息。这能够让你清楚的知道Person和Apartment的实例是否如预期那样被销毁了。 + +This next code snippet defines two variables of optional type called john and number73, which will be set to a specific Apartment and Person instance below. Both of these variables have an initial value of nil, by virtue of being optional: + +下面的代码片段定义了两个可选类型变量john和number73,接下来,他们将被设置为具体的Apartment实例和Person实例。两个变量的初始值都是nil,因为它们都是可选类型的: + +``` +var john: Person? +var number73: Apartment? +``` +You can now create a specific Person instance and Apartment instance and assign these new instances to the john and number73 variables: + +现在创建具体的Person实例和Apartment实例,并把这两个新实例分别赋值给john和number73: + +``` +john = Person(name: "John Appleseed") +number73 = Apartment(number: 73) +``` +Here’s how the strong references look after creating and assigning these two instances. The john variable now has a strong reference to the new Person instance, and the number73 variable has a strong reference to the new Apartment instance: + +这里展示了创建和分配两个实例之后的强引用关系。john变量和新Person实例之间有一条强引用关系,number73变量和新Apartment实例之间也有一条强引用关系。 + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle01_2x.png) + +You can now link the two instances together so that the person has an apartment, and the apartment has a tenant. Note that an exclamation mark (!) is used to unwrap and access the instances stored inside the john and number73 optional variables, so that the properties of those instances can be set: + +现在将两个实例连接在一起,让john住进number73公寓,number73公寓也有了一个租客john。注意那个感叹号(!),它用于解析和访问存储于john和number73实例中的可选变量,这样做实例的属性才能被设置: + +``` +john!.apartment = number73 +number73!.tenant = john +``` + +Here’s how the strong references look after you link the two instances together: + +这里展示了两个实例连接在一起之后的强引用关系: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle02_2x.png) + +Unfortunately, linking these two instances creates a strong reference cycle between them. The Person instance now has a strong reference to the Apartment instance, and the Apartment instance has a strong reference to the Person instance. Therefore, when you break the strong references held by the john and number73 variables, the reference counts do not drop to zero, and the instances are not deallocated by ARC: + +不幸的是,连接两个实例后导致它们之间形成了循环强引用。现在Person实例有一个指向Apartment的强引用,同时Apartment实例也有一个指向Person的强引用。因此,当你断开由变量john和number73保持的强引用时,引用计数不会减为0,因此实例也不会被ARC销毁。 + +``` +john = nil +number73 = nil +``` + +Note that neither deinitializer was called when you set these two variables to nil. The strong reference cycle prevents the Person and Apartment instances from ever being deallocated, causing a memory leak in your app. + +需要注意的是,你将john和number73设为nil时两个析构函数都没有被调用。循环强引用阻止了Person 和 Apartment类实例的销毁,导致你的App发生了一个内存泄漏。 + +Here’s how the strong references look after you set the john and number73 variables to nil: + +下图展示了将john和number73设为nil后的强引用关系: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle03_2x.png) + +The strong references between the Person instance and the Apartment instance remain and cannot be broken. + +Person类实例与Apartment类实例之间的强引用关系将保持且无法被断开。 + +## 解决类实例间的循环强引用 + +Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references. + +Swift提供了两种方式:弱引用和无主引用,来解决你在处理类属性时遇到的循环强引用问题。 + +Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle. + +弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不强制保持。这样实例就能够互相引用而不产生循环强引用。 + +Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization. + +对于生命周期中会变为nil的引用使用弱引用。相反的,对于初始化赋值后再也不会被赋值为nil的引用,使用无主引用。 + +### 弱引用 + +A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. This behavior prevents the reference from becoming part of a strong reference cycle. You indicate a weak reference by placing the weak keyword before a property or variable declaration. + +弱引用不会强制保持引用的实例,并且不会阻止 ARC 销毁被引用的实例。这避免了引用成为循环强引用的组成部分。声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。 + +Use a weak reference to avoid reference cycles whenever it is possible for that reference to have “no value” at some point in its life. If the reference will always have a value, use an unowned reference instead, as described in Unowned References. In the Apartment example above, it is appropriate for an apartment to be able to have “no tenant” at some point in its lifetime, and so a weak reference is an appropriate way to break the reference cycle in this case. + +在引用的生命周期中,如果某些时候引用可能无值,就使用弱引用避免产生循环强引用。如果引用总是有值,则应使用无主引用,这将在无主引用部分详述。在上面Apartment的例子中,一个公寓的生命周期中,有时是没有“租客”的,这种情况下适合使用弱引用来打破引用循环。 + +> NOTE +> +> Weak references must be declared as variables, to indicate that their value can change at runtime. A weak reference cannot be declared as a constant. +> +> 注意 +> 弱引用必须声明为变量而不能声明为常量,以表明它们的值在运行时是可以改变的。 + +Because weak references are allowed to have “no value”, you must declare every weak reference as having an optional type. Optional types are the preferred way to represent the possibility for “no value” in Swift. + +由于弱引用类型允许没有值,因此你必须声明所有弱引用为可选类型。在Swift里,可选类型是表示可能没有值的引用的首选方式。 + +Because a weak reference does not keep a strong hold on the instance it refers to, it is possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. You can check for the existence of a value in the weak reference, just like any other optional value, and you will never end up with a reference to an invalid instance that no longer exists. + +由于弱引用与其引用的实例之间不会保持强引用关系,因此即使有弱引用指向实例,实例也有可能被销毁。弱引用指向的实例被销毁后,ARC会将该弱引用指向nil。像其他可选类型一样,你可以检查弱引用是否存在值来避免引用一个不存在的实例。 + +The example below is identical to the Person and Apartment example from above, with one important difference. This time around, the Apartment type’s tenant property is declared as a weak reference: + +下面的例子与上文提到的Person 和 Apartment 的例子类似,但有一处重要的不同。这次,Apartment的属性tenant被声明为弱引用类型: + +``` +class Person { + let name: String + init(name: String) { self.name = name } + var apartment: Apartment? + deinit { println("\(name) is being deinitialized") } +} + +class Apartment { + let number: Int + init(number: Int) { self.number = number } + weak var tenant: Person? + deinit { println("Apartment #\(number) is being deinitialized") } +} +``` +The strong references from the two variables (john and number73) and the links between the two instances are created as before: + +然后跟之前一样,创建两个变量(john和number73)之间的强引用,并关联两个实例: + +``` +var john: Person? +var number73: Apartment? + +john = Person(name: "John Appleseed") +number73 = Apartment(number: 73) + +john!.apartment = number73 +number73!.tenant = john +``` + +Here’s how the references look now that you’ve linked the two instances together: + +下图展示了现在的引用关系: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference01_2x.png) + +The Person instance still has a strong reference to the Apartment instance, but the Apartment instance now has a weak reference to the Person instance. This means that when you break the strong reference held by the john variables, there are no more strong references to the Person instance: + +Person实例依然保持对Apartment实例的强引用,但是Apartment实例只有对Person实例的弱引用。这意味着当你断开john变量所保持的强引用时,就没有指向Person实例的强引用了: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference02_2x.png) + +Because there are no more strong references to the Person instance, it is deallocated: + +由于没有指向Person实例的强引用了,所以它被销毁了: + +``` +john = nil +// prints "John Appleseed is being deinitialized" +``` +The only remaining strong reference to the Apartment instance is from the number73 variable. If you break that strong reference, there are no more strong references to the Apartment instance: + +仅存的指向Apartment实例的强引用来自变量number73。如果你断开这个强引用,也就没有强引用指向Apartment的实例了: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference03_2x.png) + +Because there are no more strong references to the Apartment instance, it too is deallocated: + +由于也没有指向Apartment类实例的强引用了,它也被销毁了: + +``` +number73 = nil +// prints "Apartment #73 is being deinitialized" +``` + +The final two code snippets above show that the deinitializers for the Person instance and Apartment instance print their “deinitialized” messages after the john and number73 variables are set to nil. This proves that the reference cycle has been broken. + +上面的两段代码展示了变量john和number73在被赋值为nil后,Person实例和Apartment实例的析构函数都打印出“销毁”的信息。这证明引用循环被打破了。 + +### 无主引用 + +Like weak references, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is assumed to always have a value. Because of this, an unowned reference is always defined as a non-optional type. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration. + +与弱引用类似,无主引用也不会与其引用的类实例间保持强引用关系。不同的是,无主引用假定一直都是有值的。因此,无主引用总是被定义为非可选类型。在属性和变量之前加上unowned关键词来声明这是无主引用。 + +Because an unowned reference is non-optional, you don’t need to unwrap the unowned reference each time it is used. An unowned reference can always be accessed directly. However, ARC cannot set the reference to nil when the instance it refers to is deallocated, because variables of a non-optional type cannot be set to nil. + +由于无主引用是非可选类型的,你不必在使用的时候解析它,它可以被直接访问。不过 ARC 无法在实例被销毁后将无主引用设为nil,因为非可选类型的变量不允许被赋值为nil。 + +> NOTE +> +> If you try to access an unowned reference after the instance that it references is deallocated, you will trigger a runtime error. Use unowned references only when you are sure that the reference will always refer to an instance. +> +> Note also that Swift guarantees your app will crash if you try to access an unowned reference after the instance it references is deallocated. You will never encounter unexpected behavior in this situation. Your app will always crash reliably, although you should, of course, prevent it from doing so. + +> 注意 +> 在无主引用指向的实例被销毁后,如果依然试图访问该无主引用,你会触发运行时错误。使用无主引用,你需要确保引用指向的实例未被销毁。 +> 需要格外注意的是,在无主引用指向的实例被销毁后,若你依然试图访问该无主引用,Swift保证,你的app会毫无意外地直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。 + +The following example defines two classes, Customer and CreditCard, which model a bank customer and a possible credit card for that customer. These two classes each store an instance of the other class as a property. This relationship has the potential to create a strong reference cycle. + +接下来的例子定义了两个类,Customer和CreditCard,模拟了银行客户和信用卡。这两个类将对方的实例保存为自己的属性。这种关系会潜在的创造循环强引用。 + +The relationship between Customer and CreditCard is slightly different from the relationship between Apartment and Person seen in the weak reference example above. In this data model, a customer may or may not have a credit card, but a credit card will always be associated with a customer. To represent this, the Customer class has an optional card property, but the CreditCard class has a non-optional customer property. + +Customer 与 CreditCard之间的关系与上文弱引用例子里提到的Apartment 和 Person之间的关系有些许不同。在这个数据模型里,一位客户可能有也可能没有信用卡,但是一张信用卡必然与某位客户关联。为了表示这种关系,Customer类声明了一个可选类型的属性card,但CreditCard类声明了一个非可选类型的属性customer。 + +Furthermore, a new CreditCard instance can only be created by passing a number value and a customer instance to a custom CreditCard initializer. This ensures that a CreditCard instance always has a customer instance associated with it when the CreditCard instance is created. + +此外,只能通过向CreditCard类构造器传递一个数值和一个Customer实例的方式创建新的CreditCard实例。这是为了保证创建CreditCard实例的时候总是有一位客户实例与之关联。 + +Because a credit card will always have a customer, you define its customer property as an unowned reference, to avoid a strong reference cycle: + +由于一张信用卡一定会有一位客户与之关联,将属性customer定义为无主类型以避免循环强引用: + +``` +class Customer { + let name: String + var card: CreditCard? + init(name: String) { + self.name = name + } + deinit { println("\(name) is being deinitialized") } +} + +class CreditCard { + let number: Int + unowned let customer: Customer + init(number: Int, customer: Customer) { + self.number = number + self.customer = customer + } + deinit { println("Card #\(number) is being deinitialized") } +} +``` + +This next code snippet defines an optional Customer variable called john, which will be used to store a reference to a specific customer. This variable has an initial value of nil, by virtue of being optional: + +如下代码片段定义了一个可选Customer类型的变量john,john将用于存储指向特定客户的引用。由于是可选类型,这个变量初始值是nil. + +``` +var john: Customer? +``` + +You can now create a Customer instance, and use it to initialize and assign a new CreditCard instance as that customer’s card property: + +现在创建Customer实例,并用它初始化CreditCard实例,同时,将CreditCard实例赋值给Customer实例的card属性。 + +``` +john = Customer(name: "John Appleseed") +john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!) +``` + +Here’s how the references look, now that you’ve linked the two instances: + +下图展示了两个实例连接起来后的引用关系: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference01_2x.png) + +The Customer instance now has a strong reference to the CreditCard instance, and the CreditCard instance has an unowned reference to the Customer instance. + +Customer实例拥有一个指向CreditCard实例的强引用,同时CreditCard实例有一个指向Customer实例的无主引用。 + +Because of the unowned customer reference, when you break the strong reference held by the john variable, there are no more strong references to the Customer instance: + +由于无主引用customer的存在,当你断开由变量john保持的强引用后,就没有强引用指向Customer实例了: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference02_2x.png) + +Because there are no more strong references to the Customer instance, it is deallocated. After this happens, there are no more strong references to the CreditCard instance, and it too is deallocated: + +由于没有强引用指向Customer实例了,它被销毁了。在此之后,由于也没有强引用指向CreditCard实例了,它也被销毁了。 + +``` +john = nil +// prints "John Appleseed is being deinitialized" +// prints "Card #1234567890123456 is being deinitialized" +``` + +The final code snippet above shows that the deinitializers for the Customer instance and CreditCard instance both print their “deinitialized” messages after the john variable is set to nil. + +上面的代码展示了变量john被赋值为nil后,Customer实例和CreditCard实例的析构函数都打印出了“销毁”的信息。 + +## 无主引用与隐式解析可选属性 + +The examples for weak and unowned references above cover two of the more common scenarios in which it is necessary to break a strong reference cycle. + +弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。 + +The Person and Apartment example shows a situation where two properties, both of which are allowed to be nil, have the potential to cause a strong reference cycle. This scenario is best resolved with a weak reference. + +Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在地产生循环强引用。这种场景最适合用弱引用来解决。 + +The Customer and CreditCard example shows a situation where one property that is allowed to be nil and another property that cannot be nil have the potential to cause a strong reference cycle. This scenario is best resolved with an unowned reference. + +Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个不允许为nil,并会潜在地产生循环强引用。这种场景最适合通过无主引用来解决。 + +However, there is a third scenario, in which both properties should always have a value, and neither property should ever be nil once initialization is complete. In this scenario, it is useful to combine an unowned property on one class with an implicitly unwrapped optional property on the other class. + +但是,还有第三种场景:就是两个属性都一直有值,并且一旦初始化完成他们就永远都不可能是nil的情况。这种情况下,需要在一个类中使用无主属性,在另一个类中使用隐式解析可选属性。 + +This enables both properties to be accessed directly (without optional unwrapping) once initialization is complete, while still avoiding a reference cycle. This section shows you how to set up such a relationship. + +只要初始化完成,这两个属性都是可以被直接访问的(不需要可选解析)同时也可以避免循环引用。这部分将向你介绍如何建立这种关系。 + +The example below defines two classes, Country and City, each of which stores an instance of the other class as a property. In this data model, every country must always have a capital city, and every city must always belong to a country. To represent this, the Country class has a capitalCity property, and the City class has a country property: + +下面的例子定义了两个类,Country 和 City,每个类将另外一个类的实例保存为属性。在这个数据模型里,国家是必须有首都的,而一个城市也必须是属于某个国家的。为了表示这种关系,Country类声明了一个capitalCity属性,City类也声明了一个country属性: + +``` +class Country { + let name: String + let capitalCity: City! + init(name: String, capitalName: String) { + self.name = name + self.capitalCity = City(name: capitalName, country: self) + } +} + +class City { + let name: String + unowned let country: Country + init(name: String, country: Country) { + self.name = name + self.country = country + } +} +``` + +To set up the interdependency between the two classes, the initializer for City takes a Country instance, and stores this instance in its country property. + +为了构建两个类之间的依赖关系,City类的构造器接收一个Country实例并把它存储在country属性里。 + +The initializer for City is called from within the initializer for Country. However, the initializer for Country cannot pass self to the City initializer until a new Country instance is fully initialized, as described in Two-Phase Initialization. + +City的构造器将在Country的构造器里被调用。但是,在Country实例已完全初始化之前,Country的构造器无法传递自身(`self`)到City的构造器。这在[两段式构造过程](http://TODO)中有介绍。 + +To cope with this requirement, you declare the capitalCity property of Country as an implicitly unwrapped optional property, indicated by the exclamation mark at the end of its type annotation (City!). This means that the capitalCity property has a default value of nil, like any other optional, but can be accessed without the need to unwrap its value as described in Implicitly Unwrapped Optionals. + +为了满足要求,将Country类的capitalCity属性声明为隐式解析可选属性(通过在capitalCity类型后加感叹号来声明)。像其他可选类型属性一样,capitalCity属性的默认值为nil, 但是它可以不经解析直接被访问。这在[隐式解析可选类型](http://TODO)中有详细介绍。 + +Because capitalCity has a default nil value, a new Country instance is considered fully initialized as soon as the Country instance sets its name property within its initializer. This means that the Country initializer can start to reference and pass around the implicit self property as soon as the name property is set. The Country initializer can therefore pass self as one of the parameters for the City initializer when the Country initializer is setting its own capitalCity property. + +由于capitalCity默认值为nil, 因此只要Country实例的name属性在构造器内被赋值,就认为初始化已全部完成。这意味着name属性一旦被赋值,Country类构造器就可以引用和传递隐式的`self`。Country构造器也因此可以在为capitalCity属性赋值时把`self`作为参数给City的构造器。 + +All of this means that you can create the Country and City instances in a single statement, without creating a strong reference cycle, and the capitalCity property can be accessed directly, without needing to use an exclamation mark to unwrap its optional value: + +上述这一切意味着可以在单一语句中同时创建Country和City的实例,并且没有形成循环强引用,同时capitalCity可以直接被访问,不必用感叹号来解析其可选值,如下所示: + +``` +var country = Country(name: "Canada", capitalName: "Ottawa") +println("\(country.name)'s capital city is called \(country.capitalCity.name)") +// prints "Canada's capital city is called Ottawa +``` + +In the example above, the use of an implicitly unwrapped optional means that all of the two-phase class initializer requirements are satisfied. The capitalCity property can be used and accessed like a non-optional value once initialization is complete, while still avoiding a strong reference cycle. + +在上面的例子中,应用“隐式解析可选属性”使得两段式类构造器所需要的条件均得到满足。一旦初始化完成,capitalCity属性可以像非可选值那样被直接访问,同时还避免了循环强引用。 + +## 闭包引起的循环强引用 +You saw above how a strong reference cycle can be created when two class instance properties hold a strong reference to each other. You also saw how to use weak and unowned references to break these strong reference cycles. + +在上文中,你已经了解了两个实例互相保持彼此的强引用是如何导致循环强引用的。你也知道了可以利用弱引用和无主引用来断开强引用循环。 + +A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle. + +如果将一个闭包分配给一个类实例的属性,同时在闭包内部又捕获了该实例,也会形成循环强引用。这种捕获之所以可能发生,是因为闭包内部访问了该实例的属性,如:self.someProperty,或是访问了该实例的方法,如:self.someMethod()。这两种类型的访问,都会导致闭包“捕获”self,造成循环强引用。 + +This strong reference cycle occurs because closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure. In essence, it’s the same problem as above—two strong references are keeping each other alive. However, rather than two class instances, this time it’s a class instance and a closure that are keeping each other alive. + +这种因闭包导致的循环强引用,和“类”的情况相似,都是引用类型的问题。当将闭包分配给属性时,你就为闭包分配了一个引用。本质上,这与上文中讲述的两个强引用互相保持对方存活的例子是同一问题。然而,与两个类实例情况不同的是:这次是类实例和闭包互相保持对方存活。 + +Swift provides an elegant solution to this problem, known as a closure capture list. However, before you learn how to break a strong reference cycle with a closure capture list, it is useful to understand how such a cycle can be caused. + +针对这类问题,Swift提供了一种优雅的解决方案:闭包捕获列表。然而,在学会如何利用捕获列表断开循环强引用之前,理解这种循环是如何导致的过程是很有用的。 + +The example below shows how you can create a strong reference cycle when using a closure that references self. This example defines a class called HTMLElement, which provides a simple model for an individual element within an HTML document: + +下面的例子展示了使用闭包是如何导致循环强引用的。例子定义了一个名为HTMLElement的类,为HTML文档中的一类元素建模: + +``` +class HTMLElement { + + let name: String + let text: String? + + @lazy var asHTML: () -> String = { + if let text = self.text { + return "<\(self.name)>\(text)" + } else { + return "<\(self.name) />" + } + } + + init(name: String, text: String? = nil) { + self.name = name + self.text = text + } + + deinit { + println("\(name) is being deinitialized") + } + +} +``` + +The HTMLElement class defines a name property, which indicates the name of the element, such as "p" for a paragraph element, or "br" for a line break element. HTMLElement also defines an optional text property, which you can set to a string that represents the text to be rendered within that HTML element. + +HTMLElement类定义了一个name属性,用于表示元素名,如:“p”是段落元素的名称,“br”是换行元素的名称。同时定义了一个可选类型的text属性,用于设置元素内需要渲染的内容。 + +In addition to these two simple properties, the HTMLElement class defines a lazy property called asHTML. This property references a closure that combines name and text into an HTML string fragment. The asHTML property is of type () -> String, or “a function that takes no parameters, and returns a String “value”. + +除了这两个普通的属性之外,HTMLElement类还定义了一个懒属性asHTML。这个属性引用了一个用于将name和text合并为HTML片段的闭包。asHTML属性的类型是 `() -> String` 或描述为“一个返回字符串的无参函数”。 + +By default, the asHTML property is assigned a closure that returns a string representation of an HTML tag. This tag contains the optional text value if it exists, or no text content if text does not exist. For a paragraph element, the closure would return "<p>some text</p>" or "<p />", depending on whether the text property equals "some text" or nil. + +默认情况下,asHTML属性被分配了一个闭包,这个闭包返回一个字符串形式的HTML标签。根据text的值是否存在来返回一个包含内容的标签或一个空标签。比如:一个段落标签,闭包会返回"<p>some text</p>" 或 "<p />",这取决于text属性的值是"some text"还是nil。 + +The asHTML property is named and used somewhat like an instance method. However, because asHTML is a closure property rather than an instance method, you can replace the default value of the asHTML property with a custom closure, if you want to change the HTML rendering for a particular HTML element. + +尽管asHTML是命名属性且用法类似实例方法,但由于asHTML是闭包属性而不是实例方法,因此若你想渲染一个特定的HTML元素,你也可以用自定义闭包替换asHTML属性的默认值。 + +> +> NOTE +> +> The asHTML property is declared as a lazy property, because it is only needed if and when the element actually needs to be rendered as a string value for some HTML output target. The fact that asHTML is a lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist. +> 注意 +> asHTML之所以被声明为懒属性,是因为它只有在满足特定输出要求且确实需要将元素渲染为字符串时才需要。事实上,声明asHTML为懒属性是为了在默认闭包内部引用self,因为懒属性只有在初始化完成且self已存在的情况下才能被访问。 + +The HTMLElement class provides a single initializer, which takes a name argument and (if desired) a text argument to initialize a new element. The class also defines a deinitializer, which prints a message to show when an HTMLElement instance is deallocated. + +HTMLElement类提供了单一的构造器,它需要传递两个参数来初始化一个新元素:name(可选) 和 text。同时定义了析构函数,当HTMLElement的实例被销毁的时候会打印出一条提示信息。 + +Here’s how you use the HTMLElement class to create and print a new instance: + +这里展示了如何创建和打印HTMLElement类的新实例: + +``` +var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") +println(paragraph!.asHTML()) +// prints "

hello, world

+``` +> +> NOTE +> +> The paragraph variable above is defined as an optional HTMLElement, so that it can be set to nil below to demonstrate the presence of a strong reference cycle. +> 注意 +> 上面的paragraph变量被定义为可选类型是为了接下来可以将其设置为nil,以便演示循环强引用。 + +Unfortunately, the HTMLElement class, as written above, creates a strong reference cycle between an HTMLElement instance and the closure used for its default asHTML value. Here’s how the cycle looks: + +不幸的是,上面的HTMLElement实例和做为asHTML默认值的闭包之间形成了循环强引用,如下所示: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle01_2x.png) + +The instance’s asHTML property holds a strong reference to its closure. However, because the closure refers to self within its body (as a way to reference self.name and self.text), the closure captures self, which means that it holds a strong reference back to the HTMLElement instance. A strong reference cycle is created between the two. (For more information about capturing values in a closure, see Capturing Values.) + +实例的asHTML属性保持了一个指向它闭包的强引用。由于在闭包内部引用了self(作为访问self.name和self.text的途径),闭包捕获了self, 这意味着闭包也保持了指回HTMLElement的强引用。二者之间形成了循环强引用。(了解更多关于闭包值捕获有关的信息,请查看[值捕获](http://TODO)有关的内容) + +> NOTE +> +> Even though the closure refers to self multiple times, it only captures one strong reference to the HTMLElement instance. +> 注意 +> 尽管闭包引用了self多次,但只会捕获一个指向HTMLElement实例的强引用。 + +If you set the paragraph variable to nil and break its strong reference to the HTMLElement instance, neither the HTMLElement instance nor its closure are deallocated, because of the strong reference cycle: + +即使将paragraph变量设置为nil来断开其与HTMLElement实例间的强引用,HTMLElement实例与其闭包也不会被销毁,因为它们之间存在循环强引用: + +> paragraph = nil + +Note that the message in the HTMLElement deinitializer is not printed, which shows that the HTMLElement instance is not deallocated. + +注意到HTMLElement析构函数的信息并没有打印出来,这说明HTMLElement实例并未被销毁。 + +## 解决闭包引起的循环强引用 +You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code. + +你可以在闭包的定义内附加定义捕获列表来解决闭包和类实例间的循环强引用问题。捕获列表为在闭包内使用一个或多个引用类型定义了一套规则。与解决两个类实例之间形成循环强引用方式一样,你应声明每个捕获的引用为弱类型或无主类型,而不是强引用类型。到底选择那种类型要看你代码不同部分之间的关系。 + +> NOTE +> +> Swift requires you to write self.someProperty or self.someMethod (rather than just someProperty or someMethod) whenever you refer to a member of self within a closure. This helps you remember that it’s possible to capture self by accident. +> 注意 +> 在闭包内引用self成员时,Swift要求以self.someProperty或self.someMethod的方式来引用,而不是以直接使用属性或方法名的方式引用(如:someProperty 或 someMethod)。这提醒你self可能意外被捕获。 + +### 定义捕获列表 +Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self or someInstance). These pairings are written within a pair of square braces, separated by commas. + +捕获列表项都是由weak或unown关键字和需引用的类实例(如:self或someInstance)成对组成。每一对由方括号包裹,以逗号分隔。 + + +Place the capture list before a closure’s parameter list and return type if they are provided: + +将捕获列表放在闭包参数列表和返回类型声明(若有返回值)之前: + +``` +@lazy var someClosure: (Int, String) -> String = { + [unowned self] (index: Int, stringToProcess: String) -> String in + // closure body goes here +} +``` + +If a closure does not specify a parameter list or return type because they can be inferred from context, place the capture list at the very start of the closure, followed by the in keyword: + +若闭包没有确切的参数列表或返回类型(因为参数或返回类型可通过上下文推断),请将捕获列表放在闭包的最前面,并在后面加上in关键字。 + +``` +@lazy var someClosure: () -> String = { + [unowned self] in + // closure body goes here +} +``` + +### 弱引用与无主引用 +Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time. + +当闭包和它捕获的实例总是互相引用且同时被销毁的时候,将闭包内的捕获定义为无主引用。 + +Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. This enables you to check for their existence within the closure’s body. + +相反的,如果捕获的实例在将来的某一时刻会变为nil,就将捕获定义为弱引用。弱引用总是可选类型,而且如果它们引用的实例被销毁了,它们会自动变为nil。因此可以很容易的在闭包内检查他们是否存在。 + +> NOTE +> +> If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference. +> 注意 +> 如果捕获的引用永远不会变为nil,就要将该捕获定义为无主引用,而不是弱引用。 + +An unowned reference is the appropriate capture method to use to resolve the strong reference cycle in the HTMLElement example from earlier. Here’s how you write the HTMLElement class to avoid the cycle: + +根据上述的判断原则,无主引用就是解决先前HTMLElement例子里循环强引用问题的合适方式。 +这是改写后的HTMLElement类,避免了引用循环: + +``` +class HTMLElement { + + let name: String + let text: String? + + @lazy var asHTML: () -> String = { + [unowned self] in + if let text = self.text { + return "<\(self.name)>\(text)" + } else { + return "<\(self.name) />" + } + } + + init(name: String, text: String? = nil) { + self.name = name + self.text = text + } + + deinit { + println("\(name) is being deinitialized") + } + +} +``` + +This implementation of HTMLElement is identical to the previous implementation, apart from the addition of a capture list within the asHTML closure. In this case, the capture list is [unowned self], which means “capture self as an unowned reference rather than a strong reference”. + +这里HTMLElement类的实现与之前的一样,除了在asHTML闭包内增加了一个捕获列表。在这个例子里,捕获列表是[unowned self],意思是:“用无主引用而不是强引用来捕获self” + +You can create and print an HTMLElement instance as before: +你依然可以像之前那样创建并打印HTMLElement实例: + +``` +var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") +println(paragraph!.asHTML()) +// prints "<p>hello, world</p>" +``` + +Here’s how the references look with the capture list in place: + +这是使用了捕获列表后的引用关系: + +![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle02_2x.png) + + +This time, the capture of self by the closure is an unowned reference, and does not keep a strong hold on the HTMLElement instance it has captured. If you set the strong reference from the paragraph variable to nil, the HTMLElement instance is deallocated, as can be seen from the printing of its deinitializer message in the example below: + +这次,被闭包捕获的self是无主引用,它不会强制保持闭包捕获的HTMLElement实例。如果你将paragraph变量设为nil,HTMLElement实例会被销毁并会看到由其析构函数打印出的销毁信息,如下所示: + +``` +paragraph = nil +// prints "p is being deinitialized +``` \ No newline at end of file diff --git a/src/chapter2/17_Optional_Chaining.md b/src/chapter2/17_Optional_Chaining.md index e69de29..85642a5 100644 --- a/src/chapter2/17_Optional_Chaining.md +++ b/src/chapter2/17_Optional_Chaining.md @@ -0,0 +1,300 @@ +> 翻译:[zearlin](https://github.com/zearlin) + +# 可选链 +----------------- + +可选链(Optional Chaining)是针对当前可能为空值(`nil`)的可选元素的属性,方法及下标调用和查寻的一种处理。 若可选元素为赋值对应的属性,方法或下标都会被成功调用;反之当可选成员为空值(`nil`)时,侧其属性,方法或下标调为会返回空值(`nil`)。多个的查寻可一起链式调用,并当链路的任意一环为nil时链路调用失败。 + +> 注意: +Swift中的可选链与Object-C中空值的方法调用相似,是一种更为全面的实现,适用于所有的类型及支持调用成功与否的判断。 + +## 可选链,强制解析的一种替代方案 + +当你想调用一个属性,方法或下标时通过在可选元素(`optional value`)后添置一个问号(?)来声明一个为可选链,当可选元素为非空时,侧与通过添置感叹号(!)来强解析其值的做法非常相似。主要的区别在于当可选元素为空时可选链会得体地以调用失败结束,而强制解析则会引发一个运行时错误。 + +考虑到可选链空值调用这一情况,可选链的返回值一定为可选值,即使你所查寻的属性,方法或下标返回的并不是是一个可选值。你可以通过返回的可选值来判断可选链的调用是成功的(返回的可选元素有值),还是因为链式中的空值元素而调用失败(返回的可选元素为空)。 + +具体来说,可选链的返回值类型与预期的返回值类型一致,但封装为了一个可选元素。原本返回为整型的属性,在可选链调用中会返回为可选整型(Int?) + +我们将在后续的几个代码片段中解释可选链与强制解析的区别及让你了解如果判断调用成功与否。 + +首先,我们先定义Person与Residence两个类: + +```swift +class Person { + var residence: Residence? +} + +class Residence { + var numberOfRooms = 1 +} +``` + +`Residence`实例拥有一个命名为`numberOfRooms`的整型属性。而`Person`实例有一个可选属性`residence`其类型为`Residence?`。 + +如果你创建一个新的`Person`实例,它的`residence`属性由于是被定义为可选型的,此属性将默认初始化为空。下面的代码中,john的residence属性值为空: + + +```swift +let john = Person() +``` + +如果你在`residence`后添置感叹号(`!`)以强制解析的方式来访问`person`的`residence`的属性`numberOfRooms`时,会引发一个运行时错误,因为`residence`为空无法被解析。 + +```swift +let roomCount = john.residence!.numberOfRooms +//将导致运行时错误 +``` +当`john.residence`为非空及为`roomCount`设一个可理的房间数时以述的代码会被成功调用。然后如上面所演示的,当`residence`为空这段代码就会触发一个运行时错误。 + +可选链提供了访问`numberOfRooms`值的另一种方式。 在这里通过以问号来取代先前的感叹号以可选链的方式调用。 + +```swift +if let roomCount = john.residence?.numberOfRooms { + println("John's residence has \(roomCount) room(s).") +} else { + println("Unable to retrieve the number of rooms.") +} +// 打印 "Unable to retrieve the number of rooms. +``` + +这将告诉Swift将可选的`residence`属于链式中的一环并得到`numberOfRooms`的值当`residence`存在时。 + +因为尝试访问`numberOfRooms`时存在失败的可能,所以可选链返回一个类型为`Int?` 或称之为“可选整型”的值。 当`residence`为空时,这个可选整型也将为空,以表示`numberOfRooms`无法被访问。 + +需要注意一点即使`numberOfRooms`为非可选整型。通来可选链的方式来调用`numberOfRooms`的值查询时返回的类型是`Int?`而不是一个常规的`Int`。 + +我们给`john.residence`分配一个`Residence`实例让它不在为一个空值: + +```swift +john.residence = Residence() +``` + +此时`john.residence`包含了一个真实的`Residence`实例而不再为空了。如果你以先前相同的可选链方式去访问`numberOfRooms`,它将返回一个包含默认值 1 的`Int?`: + +```swift +if let roomCount = john.residence?.numberOfRooms { + println("John's residence has \(roomCount) room(s).") +} else { + println("Unable to retrieve the number of rooms.") +} +// 打印 "John's residence has 1 room(s)"。 +``` + +##定义可选链的模型(实体)类 + +可选链支持多层的属性,方法及下标调用。这一特性让你可以进一步的调用关联类型的复杂模型中的子属性及判断这些子属性对应的属性,方法及下标下否可以访问。 + +在下面的代码片段中为后续的几个例子使用定义了四个实体类,这些类都是基于`Person`和`Residence`模型通过增加`Room`及`Address`类及一些相关的属性,方法及下标进行扩展的。 + +`Person`类还是于先前定义的相同: + +```swift +class Person { + var residence: Residence? +} +``` + +`Residence `类比之前复杂些。这次我们为 `Residence `声明了一个名为 `rooms `的变量,其初始值为 `Room[] `类型的空数值。 + +```swift +class Residence { + var rooms = Room[]() + var numberOfRooms: Int { + return rooms.count + } + subscript(i: Int) -> Room { + return rooms[i] + } + func printNumberOfRooms() { + println("The number of rooms is \(numberOfRooms)") + } + var address: Address? +} +``` +因为在这一版的`Residence`中存储了一个Room实例数组,所以其`numberOfRooms`属性以计算属性的方式实现。计算的`numberOfRooms`属性只是简单的返回了`rooms`数组的`count`属性。 + +为了更便捷的访问它的`rooms`数组,在这一版中`Residence`提供了一个只读下标,一开始我们假设传参的引索是有效的。如果索引是有效的,下标将返回`rooms`数组与请求索引相对应的`room`对象。 + +`Residence`还提供了一个名为`printNumberOfRooms`的方法,用于简单地打印房间数。 + +最后,`Residence`还定义了一个类型为`Address?`的可选属性`address`,对应的`Address`类会在下文中定义。 + +`rooms`数组中用到的`Room`类非常简单只拥有一个`name`属性及对应设置房间名的构造器。 + +```swift +class Room { + let name: String + init(name: String) { self.name = name } +} +``` + +模型中最后的一个类叫`Address`。类中有三个类型为`String?`的可选属性。作为地址信息中的一部分头两个属性`buildingName`及`buildingNumber`是两个可供选择的方式来定位特定的建筑物。 第三个属性,`stree`,则用来保存地址中的街道名称的: + +```swift +class Address { + var buildingName: String? + var buildingNumber: String? + var street: String? + func buildingIdentifier() -> String? { + if buildingName { + return buildingName + } else if buildingNumber { + return buildingNumber + } else { + return nil + } + } +} +``` +`Address`类中还提供了一个名为`buildingIdentifier`的方法,其返回类型为`String?`。 这个方法检查`buildingName`及`buildingName`是否有值,若`buildingName`有值则返回`buildingName`,若非则返回`buildingNumber`的值,如果两者均无则返回空。 + +##以可选链方式的属性调用 + +如先前演示的可选链做为强制解析的一个替代方式,你可以利用可选链来访问可选元素的属性,及检测属性的访问是否能成功,但你不能通过可选链的方式为属性赋值。 + +利用前先写好的类一个新的`Person`实例,并与之前先一样尝试访问它的`numberOfRooms`属性: + +```swift +let john = Person() +if let roomCount = john.residence?.numberOfRooms { + println("John's residence has \(roomCount) room(s).") +} else { + println("Unable to retrieve the number of rooms.") +} +// 打印 "Unable to retrieve the number of rooms。 +``` + +因为`john.residence`为空,所以这个可选链与先前的一样调用失败并没引错误。 + +##以可选链方式的方法调用法 + +你能以可选链的方式去调用一个可选元素的方法,及检测是否方法调用不否成功。即使在方法没有定义返回值的情况下一样可行。 + +`Residence`的`printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下: + +```swift +func printNumberOfRooms(){ + println(“The number of rooms is \(numberOfRooms)”) +} +``` +这个方法没有声明返回值,无返回值函数及方法都会带有一个隐式的返回类型为`Void`(参见Function Without Return Values)。 + +如果你通过可选链的方式去调用一个可选元素的方法,该方法返回的将是`Void?`,而非`Void`,因为通过可选链方式调用的方法返回的均为可选类型。这样让你可以通过一个`If`语句去测试`printNumberOfRooms`方法是否可调用,即使该方法本身没有定义返回值。当`printNumberOfRooms`方法通过可选链调用成功时隐式的返回值为`Void`,否非为空: + +```swift +if john.residence?.printNumberOfRooms() { + println("It was possible to print the number of rooms.") +} else { + println("It was not possible to print the number of rooms.") +} +// 打印 "It was not possible to print the number of rooms."。 +``` + +##以可选链方式的下标调用 + +你可以通过可选链的方式去获取可选元素对应下标的值,及判断下标调用是否成功,然而,你不能在可选链中去设置一个下标的值。 + +> 注意: +当你要通过可选链的试访问对应可选元素的下标时,你应该将问号置于下标所带的括号前,而非后。可选链的问号通常都是紧随表达式中的可选元素后的。 + +下面的例子中我们将通过在`Residence`类中定义的下标去获取`rooms`数组中第一间房间的名称。由于`john.residence`当前为空,所以下标的调用失败。 + +```swift +if let firstRoomName = john.residence?[0].name { + println("The first room name is \(firstRoomName).") +} else { + println("Unable to retrieve the first room name.") +} +// 打印 "Unable to retrieve the first room name."。 +``` + +下标调用时我们将可选链的问号置于`john.residence`后于下标的括号前。因为`john.residence`为可选链需要尝试调用的可选元素。 + +如果我们为`john.residence`创建并分配一个`Residence`实体且其`rooms`属性带一个或多个Room实体,你就可以通过`Residence`的下标在可选链中访问对应的成员了: + +```swift +let johnsHouse = Residence() +johnsHouse.rooms += Room(name: "Living Room") +johnsHouse.rooms += Room(name: "Kitchen") +john.residence = johnsHouse + +if let firstRoomName = john.residence?[0].name { + println("The first room name is \(firstRoomName).") +} else { + println("Unable to retrieve the first room name.") +} +// 打印 "The first room name is Living Room."。 +``` + +##多层链式 + +你可以通过多层链式来访问实体中的属性,方法及下标。多层可选链式调用并不会增加返回值的可选性/形成可选嵌套。 + +也就是说: + +通过可选链得到的值一定为可选类型,无论你想获取的目标值是否为可选。同理一个目标值原为可选元素,不过因为链式的调用而加深变可选性。 + +因此: + +当你想从可选链中得到一个`Int`时,你得到的总会是一个`Int?`类型的值,无论通过多少层的可选链。相同的,原为`Int?`类型的返回值也一样,无论经过多少层得到的还是`Int?`类型。 + +下面的例子中我们将尝试访问从`john`的`residence`属性中的`address`属于获取期其`street`属性的值。这里我们将有两层的可选链调用,链式中的两环`residence`和`address`均为可选类型的属性: + +```swift +if let johnsStreet = john.residence?.address?.street { + println("John's street name is \(johnsStreet).") +} else { + println("Unable to retrieve the address.") +} +// 打印 "Unable to retrieve the address.”。 +``` + +虽然当前的`john.residence`含有一个有效的`Residence`实例。然而`john.residence.address`的还是为空值,所以调用`john.residence?.address?.street`失败了。 + +值得注意在下面的例子中,你将尝试去获得`street`属性的值。 这个属性的类型为`String?`。所以`john.residence?.address?`的返回值也是`String?`,即使经过了两层的可选链路来访问。 + +如果你为`john.street.address`分配一个真实的`Address`实例并为其`street`赋值,你就可以通过多层可用选连的试去访问相关的值。 + +```swift +let johnsAddress = Address() +johnsAddress.buildingName = "The Larches" +johnsAddress.street = "Laurel Street" +john.residence!.address = johnsAddress +``` + +```swift +if let johnsStreet = john.residence?.address?.street { + println("John's street name is \(johnsStreet).") +} else { + println("Unable to retrieve the address.") +} +// 打印 "John's street name is Laurel Street."。 +``` + +注意我们需要通过感叹号来为`jonh.residence.address`赋值。 因为`john.residence`属性是一个可选类型,所以在为`residence`的`address`属性赋值前,我们需要用感叹得到它的实际值先。 + +##返回可选值方法的链式调用 + +先前的例子中我们展示了如果通过可选链路来获取一个可选属性的值。如有需要我们也可以用可选链的方式去调用返回值为可选类型的方法,并也可将其返回值做为链式中的一环。 + +下下面的例子中通过可选链调用了`Address`类的`buildingIdentifier`方法。 这个方法的返回类型为`String?`。 如先前所述,通过可选链调用该方法的最终返回类型也是为`String?`: + +```swift +if let buildingIdentifier = john.residence?.address?.buildingIdentifier() { + println("John's building identifier is \(buildingIdentifier).") +} +// 打印 "John's building identifier is The Larches."。 +``` + +如果你想将该方法的返回值也置于链式中,只需将可选链路的问号置于方法调用的括号后: + +```swift +if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString { + println("John's uppercase building identifier is \(upper).") +} +// 打印 "John's uppercase building identifier is THE LARCHES."。 +``` + +> 注意: +在上面的例子中,你将可选链的问号置于括后面是因为你想加入链式中的可选元素是`buildingIdentifier`的返回值,而不是该方法本身。 diff --git a/src/chapter2/18_Type_Casting.md b/src/chapter2/18_Type_Casting.md index e69de29..83837f7 100644 --- a/src/chapter2/18_Type_Casting.md +++ b/src/chapter2/18_Type_Casting.md @@ -0,0 +1,293 @@ +#Type Casting + +Type casting is a way to check the type of an instance, and/or to treat that instance as if it is a different superclass or subclass from somewhere else in its own class hierarchy. + +Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type. + +You can also use type casting to check whether a type conforms to a protocol, as described in Checking for Protocol Conformance. + +# 类型检查 +类型检查的目的是为了检查实例的类型,并且可以用来类型转换,可以让实例找到自己所在的继承树,以及在继承树中上下转换类型。 + +类型检查使用`is`和`as`操作符来实现。这两个操作符提供了一种简单明了的方式来进行类型检查和类型转换。 + +你还可以使用类型检查来检查类型是否符合接口,以及是否和接口描述保持一致 ,如[Checking for Protocol Conformance](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)小节所述。 + +##Defining a Class Hierarchy for Type Casting + +You can use type casting with a hierarchy of classes and subclasses to check the type of a particular class instance and to cast that instance to another class within the same hierarchy. The three code snippets below define a hierarchy of classes and an array containing instances of those classes, for use in an example of type casting. + +The first snippet defines a new base class called MediaItem. This class provides basic functionality for any kind of item that appears in a digital media library. Specifically, it declares a name property of type String, and an init name initializer. (It is assumed that all media items, including all movies and songs, will have a name.) + +## 为类型检查定义一个继承树 + +你可以使用类型检查来检查一个实例的具体类型,也可以把实例转化为继承树中的另一种类型。下面的三个代码片段定义一个类的继承关系和包含这些类实例的数组,作为类型转换的一个例子使用。 + +第一个片段定义了一个名为`MediaItem`的基类。这个类提供数字媒体处理库中其他类的基本功能。具体来说,它声明一个`String`类型的`name`属性,以及一个叫`init`的初始化方法。 (假设所有的媒体项目,包括所有的电影和歌曲,将有一个名称。) + + class MediaItem { + var name: String + init(name: String) { + self.name = name + } + } +The next snippet defines two subclasses of MediaItem. The first subclass, Movie, encapsulates additional information about a movie or film. It adds a director property on top of the base MediaItem class, with a corresponding initializer. The second subclass, Song, adds an artist property and initializer on top of the base class: + +接下来的代码定义了`MediaItem`的两个子类。第一个子类`Movie`,封装了有关`Movie`的附加信息。它增加了属性`director`及其初始化方法。第二个子类`Song`,增加了属性`artist`及其初始化方法: + + class Movie: MediaItem { + var director: String + init(name: String, director: String) { + self.director = director + super.init(name: name) + } + } + + class Song: MediaItem { + var artist: String + init(name: String, artist: String) { + self.artist = artist + super.init(name: name) + } + } +The final snippet creates a constant array called library, which contains two Movie instances and three Song instances. The type of the library array is inferred by initializing it with the contents of an array literal. Swift’s type checker is able to deduce that Movie and Song have a common superclass of MediaItem, and so it infers a type of MediaItem[] for the library array: + +最后的代码创建一个常量数组`library`,其中包含两个`Movie`实例和三首`Song`的实例。`library`数组的类型是由它里面包含的内容决定的。Swift能够识别出`Movie`和`Song`有MediaItem一个共同的父类,所以它会推断出`library`是一个`MediaItem[] `类型的数组。 + + let library = [ + Movie(name: "Casablanca", director: "Michael Curtiz"), + Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), + Movie(name: "Citizen Kane", director: "Orson Welles"), + Song(name: "The One And Only", artist: "Chesney Hawkes"), + Song(name: "Never Gonna Give You Up", artist: "Rick Astley") + ] + // the type of "library" is inferred to be MediaItem[] +The items stored in library are still Movie and Song instances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed as MediaItem, and not as Movie or Song. In order to work with them as their native type, you need to check their type, or downcast them to a different type, as described below. + +`library`中的子元素仍然是`Movie`或`Song`的实例。但是,如果你遍历这个数组,返回的是`MediaItem`类型的实例,而不是`Movie`或`Song`。为了使用他们自身的类型,你需要检查它们的类型,或将它们向下转换为不同的类型,如下所述。 + +##Checking Type + +Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not. + +The example below defines two variables, movieCount and songCount, which count the number of Movie and Song instances in the library array: + +## 检查类型 + +使用类型检查运算符(`is`)来检查一个实例是否是一个类的子类。如果是,则返回`true`,否则返回`false`。 + +下面的例子定义了两个变量,`movieCount`和`songCount`,用来统计数组中`Movie` 和`Song` 的数量。 + + var movieCount = 0 + var songCount = 0 + + for item in library { + if item is Movie { + ++movieCount + } else if item is Song { + ++songCount + } + } + + println("Media library contains \(movieCount) movies and \(songCount) songs") + // 打印出 "Media library 包含 2 movies and 3 songs" + +This example iterates through all items in the library array. On each pass, the for-in loop sets the item constant to the next MediaItem in the array. + +item is Movie returns true if the current MediaItem is a Movie instance and false if it is not. Similarly, item is Song checks whether the item is a Song instance. At the end of the for-in loop, the values of movieCount and songCount contain a count of how many MediaItem instances were found of each type. + +这个例子遍历了数组中的所有元素,每一次循环,都会把数组中的一个实例赋值给`item`。 + +如果`item`是`Movie`类型,则返回 `true`否则返回`false`。同样,也会检查`item`是否是`Song`类型。最后,`movieCount`和`songCount`即为每个类型的实例数。 + + +##Downcasting + +A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with the type cast operator (as). + +Because downcasting can fail, the type cast operator comes in two different forms. The optional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as, attempts the downcast and force-unwraps the result as a single compound action. + +## 向下转型 +有时候,一些实例的类型是它父类的类型,这个时候你就需要使用`as`操作符进行向下转型。 + +因为向下转型可能会失败,类型转换操作符带有两种不同形式。可选形式( optional form)` as? `返回一个你试图下转成的类型的可选值(optional value)。强制形式 `as` 则会先向下转型并把转型结果强制解包(force-unwraps)。 + +Use the optional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast. + +Use the forced form of the type cast operator (as) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type. + +当你不确定向下转型能否成功时,使用`as?`,转型失败时返回`nil `。这样你就能判断是否转型成功。 + +当你对类型确定的对象进行向下转型时,使用`as`,转型失败就会抛错。 + +The example below iterates over each MediaItem in library, and prints an appropriate description for each item. To do this, it needs to access each item as a true Movie or Song, and not just as a MediaItem. This is necessary in order for it to be able to access the director or artist property of a Movie or Song for use in the description. + +In this example, each item in the array might be a Movie, or it might be a Song. You don’t know in advance which actual class to use for each item, and so it is appropriate to use the optional form of the type cast operator (as?) to check the downcast each time through the loop: + +如下例,我们的目的是遍历`library`里的每一个` MediaItem` ,并打印出正确的描述信息。为了达到这个目的,`item`需要真正作为`Movie` 或 `Song`的类型来使用,而非仅仅是 `MediaItem`。因为我们在打印描述信息时,必须要用到`Movie`的`director`属性或 `Song`的`artist`属性。 + +数组中的每一个`item`可能是 `Movie` 或 `Song`。 事先你不知道每个`item`的真实类型,所以使用可选形式的类型转换 (`as?`)去检查每次向下转型。 + + for item in library { + if let movie = item as? Movie { + println("Movie: '\(movie.name)', dir. \(movie.director)") + } else if let song = item as? Song { + println("Song: '\(song.name)', by \(song.artist)") + } + } + + // Movie: 'Casablanca', dir. Michael Curtiz + // Song: 'Blue Suede Shoes', by Elvis Presley + // Movie: 'Citizen Kane', dir. Orson Welles + // Song: 'The One And Only', by Chesney Hawkes + // Song: 'Never Gonna Give You Up', by Rick Astley +The example starts by trying to downcast the current item as a Movie. Because item is a MediaItem instance, it’s possible that it might be a Movie; equally, it’s also possible that it might a Song, or even just a base MediaItem. Because of this uncertainty, the as? form of the type cast operator returns an optional value when attempting to downcast to a subclass type. The result of item as Movie is of type Movie?, or “optional Movie”. + +示例中,因为 `item` 是一个 `MediaItem` 类型的实例,它可能是一个`Movie`,所以我们首先试图将 `item` 下转为 `Movie`。同样,它也可能是一个 `Song`,或者仅仅是基类 `MediaItem`。所以,`as?`形式试图下转时返还一个可选值。 `item as Movie` 的返回值是`Movie?`类型或 “optional Movie”。 + +Downcasting to Movie fails when applied to the two Song instances in the library array. To cope with this, the example above uses optional binding to check whether the optional Movie actually contains a value (that is, to find out whether the downcast succeeded.) This optional binding is written “if let movie = item as? Movie”, which can be read as: + +“Try to access item as a Movie. If this is successful, set a new temporary constant called movie to the value stored in the returned optional Movie.” + +把数组`library`中的两个`Song`对象进行向下转型为`Movie`时,会转型失败。所以,使用临时赋值来检查是否转型成功。临时赋值的写法是`if let movie = item as? Movie`,意思就是“尝试把`item`转型为`Movie`,如果转型成功,则把转化后的对象存储在临时变量`movie`中“。 + +If the downcasting succeeds, the properties of movie are then used to print a description for that Movie instance, including the name of its director. A similar principle is used to check for Song instances, and to print an appropriate description (including artist name) whenever a Song is found in the library. + +如果向下转型成功,则会打印`Movie`对象相关的属性,包括导演的姓名,同样,如果`Song`转型成功,也会打印出`Song`相关的属性,例如歌手的姓名。 + +> NOTE + +> Casting does not actually modify the instance or change its values. The underlying instance remains the same; it is >simply treated and accessed as an instance of the type to which it has been cast. + +>注意 + +>实际上,类型转换并没有对实例进行修改,只是使用时,被当作一种类型来处理和访问。 + +##Type Casting for Any and AnyObject + +Swift provides two special type aliases for working with non-specific types: + +AnyObject can represent an instance of any class type. +Any can represent an instance of any type at all, apart from function types. +NOTE + +Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code. + + +## Any和AnyObject 的类型转换 + +`Swift` 提供了两种特殊类型,用来表示非特定类型。 + +`AnyObject`表示任何类类型的实例。 +`Any` 表示任何类型的实例,函数类型除外。 + +注意: +只有当你确定需要使用`Any`和`AnyObject`的特性的时候,再使用它们。一般情况下,最好还是指定具体的类型。 +###AnyObject + +When working with Cocoa APIs, it is common to receive an array with a type of AnyObject[], or “an array of values of any object type”. This is because Objective-C does not have explicitly typed arrays. However, you can often be confident about the type of objects contained in such an array just from the information you know about the API that provided the array. + +### AnyObject +使用Cocoa APIs时,返回值为AnyObject类型的数组很常见。这是因为Objective-C 没有确定类型的数组。尽管如此,如果对具体API熟悉的话,你仍然可以放心的使用这个API。 + +In these situations, you can use the forced version of the type cast operator (as) to downcast each item in the array to a more specific class type than AnyObject, without the need for optional unwrapping. + +The example below defines an array of type AnyObject[] and populates this array with three instances of the Movie class: + + +在这些情况下,您可以使用as 把AnyObject转化为一个具体的类型,而不需要进行拆包。 + +下面的例子定义了一个类型为AnyObject[]的数组,包含三个Movie类型的对象。 + + let someObjects: AnyObject[] = [ + Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), + Movie(name: "Moon", director: "Duncan Jones"), + Movie(name: "Alien", director: "Ridley Scott") + ] + +Because this array is known to contain only Movie instances, you can downcast and unwrap directly to a non-optional Movie with the forced version of the type cast operator (as): + +因为你已经知道这个数组中全都是Movie类型的对象,所以你可以直接使用as进行向下转型: + + for object in someObjects { + let movie = object as Movie + println("Movie: '\(movie.name)', dir. \(movie.director)") + } + // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick + // Movie: 'Moon', dir. Duncan Jones + // Movie: 'Alien', dir. Ridley Scott +For an even shorter form of this loop, downcast the someObjects array to a type of Movie[] instead of downcasting each item: + +上面的代码可以简写,你可以直接把数组转型而不用对数组中的每一个对象转型。 + + for movie in someObjects as Movie[] { + println("Movie: '\(movie.name)', dir. \(movie.director)") + } + // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick + // Movie: 'Moon', dir. Duncan Jones + // Movie: 'Alien', dir. Ridley Scott + +### Any + +Here’s an example of using Any to work with a mix of different types, including non-class types. The example creates an array called things, which can store values of type Any: + +### Any + +下面的例子中,使用了`Any`来处理包含了多种混合类型,甚至一些非类类型的场景。在这个例子中,我们创建了一个`things`数组用来存储任何类型的元素。 + + var things = Any[]() + + things.append(0) + things.append(0.0) + things.append(42) + things.append(3.14159) + things.append("hello") + things.append((3.0, 5.0)) + things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) + + +The things array contains two Int values, two Double values, a String value, a tuple of type (Double, Double), and the movie “Ghostbusters”, directed by Ivan Reitman. + +You can use the is and as operators in a switch statement’s cases to discover the specific type of a constant or variable that is known only to be of type Any or AnyObject. The example below iterates over the items in the things array and queries the type of each item with a switch statement. Several of the switch statement’s cases bind their matched value to a constant of the specified type to enable its value to be printed: + +`things`数组包含两个`int`值,两个`Double`值,一个字符串值,一个元组,和一个Movie对象。 + +你可以在`switch`语句中使用`is`和`as`操作符来确定数组中`Any`和`AnyObject`变量的的具体类型。如下遍历数组`things`中的每一个元素,并确定他们的类型。为了打印元素的值,一些`switch` 语句把元素赋值给一个指定的类型。 + + for thing in things { + switch thing { + case 0 as Int: + println("zero as an Int") + case 0 as Double: + println("zero as a Double") + case let someInt as Int: + println("an integer value of \(someInt)") + case let someDouble as Double where someDouble > 0: + println("a positive double value of \(someDouble)") + case is Double: + println("some other double value that I don't want to print") + case let someString as String: + println("a string value of \"\(someString)\"") + case let (x, y) as (Double, Double): + println("an (x, y) point at \(x), \(y)") + case let movie as Movie: + println("a movie called '\(movie.name)', dir. \(movie.director)") + default: + println("something else") + } + } + + // zero as an Int + // zero as a Double + // an integer value of 42 + // a positive double value of 3.14159 + // a string value of "hello" + // an (x, y) point at 3.0, 5.0 + // a movie called 'Ghostbusters', dir. Ivan Reitman +>NOTE + +>The cases of a switch statement use the forced version of the type cast operator (as, not as?) to check and cast to a >specific type. This check is always safe within the context of a switch case statement. + +>注意 + +> 这个示例使用 `as` 而不是 `as?` 来进行类型转换,因为在`switch`上下文中,类型检查是安全的。 diff --git a/src/chapter2/19_Nested_Types.md b/src/chapter2/19_Nested_Types.md index e69de29..6b0805a 100644 --- a/src/chapter2/19_Nested_Types.md +++ b/src/chapter2/19_Nested_Types.md @@ -0,0 +1,180 @@ +* ` Memberwise Initializers for Structure Types` 引自 `Initialization`,中文翻译参考了刘康的`《结构类型的成员式构造方法》` +* `Initializers`: 构造方法 +* `raw Character`: 字符? 原生字符? +* `raw Int`: 整型?原生整型? +* `computed property`: 计算属性? + +> 备注: +> 以下翻译并未遵循英文的第二人称叙事方式,整体使用第一人称讲述嵌套类型。 +> 由于中英文表达的差异,为兼顾译文的流畅性,本人擅自改动了一些原文的语言结构。更正时还请麻烦告知我,鄙人渴望多听取专家意见、希望在翻译的道路上越走越深~ 谢谢~ + + +#Nested Types +#嵌套类型 + +Enumerations are often created to support a specific class or structure’s functionality. Similarly, it can be convenient to define utility classes and structures purely for use within the context of a more complex type. To accomplish this, Swift enables you to define nested types, whereby you nest supporting enumerations, classes, and structures within the definition of the type they support. + +我们常常创建枚举类型来支持特定类或结构体的功能。同样的,枚举类型可以被方便的用于定义工具类(utility classes)和结构体,而这些工具类和结构体仅仅用在更复杂类型的上下文中。为实现这些功能,Swift可以定义嵌套类型,因此我们可以把枚举类型、类、结构体嵌套到支持他们的类型的定义中。 + +To nest a type within another type, write its definition within the outer braces of the type it supports. Types can be nested to as many levels as are required. + +要嵌套一个类型到另一个类型,只需把它的定义写在外层类型的大括号{}内,这个操作有个前提:外层类型支持嵌套内层类型。嵌套类型没有层数限制,想嵌多少层都可以。 + +##Nested Types in Action +##嵌套类型实例 + +The example below defines a structure called `BlackjackCard`, which models a playing card as used in the game of Blackjack. The `BlackJack` structure contains two nested enumeration types called `Suit` and `Rank`. + +下面的例子定义了一个`BlackjackCard`(21点牌)的结构体,来模仿21点出牌。`BlackJack`结构体包含了两个嵌套类型`Suit`和`Rank`。 + +In Blackjack, the Ace cards have a value of either one or eleven. This feature is represented by a structure called Values, which is nested within the Rank enumeration: + +在21点规则中,A牌(Ace)即可算作1点也可算作11点。这一特征用一个嵌套在枚举类型Rank中的结构体Values来代表。 + +``` +struct BlackjackCard { + + // nested Suit enumeration + enum Suit: Character { + case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" + } + + // nested Rank enumeration + enum Rank: Int { + case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten + case Jack, Queen, King, Ace + struct Values { + let first: Int, second: Int? + } + var values: Values { + switch self { + case .Ace: + return Values(first: 1, second: 11) + case .Jack, .Queen, .King: + return Values(first: 10, second: nil) + default: + return Values(first: self.toRaw(), second: nil) + } + } + } + + // BlackjackCard properties and methods + let rank: Rank, suit: Suit + var description: String { + var output = "suit is \(suit.toRaw())," + output += " value is \(rank.values.first)" + if let second = rank.values.second { + output += " or \(second)" + } + return output + } +} + +``` + +``` +struct BlackjackCard { + + // nested Suit enumeration + enum Suit: Character { + case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" + } + + // nested Rank enumeration + enum Rank: Int { + case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten + case Jack, Queen, King, Ace + struct Values { + let first: Int, second: Int? + } + var values: Values { + switch self { + case .Ace: + return Values(first: 1, second: 11) + case .Jack, .Queen, .King: + return Values(first: 10, second: nil) + default: + return Values(first: self.toRaw(), second: nil) + } + } + } + + // BlackjackCard的属性和方法 + let rank: Rank, suit: Suit + var description: String { + var output = "suit is \(suit.toRaw())," + output += " value is \(rank.values.first)" + if let second = rank.values.second { + output += " or \(second)" + } + return output + } +} + +``` + +The `Suit` enumeration describes the four common playing card suits, together with a raw `Character` value to represent their symbol. + +枚举类型`Suit`描述了扑克牌中的4种花色,每一种花色用一个`字符`(Character)代表。 + +The `Rank` enumeration describes the thirteen possible playing card ranks, together with a raw `Int` value to represent their face value. (This raw `Int` value is not used for the Jack, Queen, King, and Ace cards.) + +枚举类型`Rank`描述了相同花色的13张牌,每张牌的面值用一个`整型值`(Int)代表。(这个整型值不用再J、Q、K、A这四张牌上。) + +As mentioned above, the `Rank` enumeration defines a further nested structure of its own, called `Values`. This structure encapsulates the fact that most cards have one value, but the Ace card has two values. The `Values` structure defines two properties to represent this: + +* `first`, of type `Int` +* `second`, of type `Int?`, or “optional `Int`” + +如上所述,枚举类型`Rank`定义了一个内嵌的结构体`Values`。结构体`Values`通过定义两个属性描述了一个实事:大部分牌只有一个面值,而A牌有两个面值。属性如下: + +* `first`, `Int`型 +* `second`, `Int?`型,或者 “optional `Int`” + + +`Rank` also defines a computed property, `values`, which returns an instance of the `Values` structure. This computed property considers the rank of the card and initializes a new `Values` instance with appropriate values based on its rank. It uses special values for `Jack`, `Queen`, `King`, and `Ace`. For the numeric cards, it uses the rank’s raw `Int` value. + +`Rank`还定义了一个返回结构体`Values`实例的计算属性`values`。这个计算属性依据扑克牌的点数,初始化一个基于扑克牌点数推算出正确值的`Values`实例。计算属性`values`赋予J、Q、K和A这四张牌特定的值,赋予其他数字牌rank中定义的原始整型值(raw `Int`)。 + +The `BlackjackCard` structure itself has two properties—`rank` and `suit`. It also defines a computed property called `description`, which uses the values stored in `rank` and `suit` to build a description of the name and value of the card. The `description` property uses optional binding to check whether there is a second value to display, and if so, inserts additional description detail for that second value. + +结构体`BlackjackCard`定义了两个属性`rank`和`suit`,同时还定义了一个计算属性`description`,这个计算属性使用了`rank`和`suit`中存储的值来描述扑克牌的花色和点数。`description`使用可选绑定(optional binding)来判断是否存在第二个值,如果存在,则为第二个值插入额外的描述。 + +Because `BlackjackCard` is a structure with no custom initializers, it has an implicit memberwise initializer, as described in [Memberwise Initializers for Structure Types](). You can use this initializer to initialize a new constant called `theAceOfSpades`: + +因为结构体`BlackjackCard`没有自定义构造方法,所以它有一个默认的成员式构造方法(memberwise initializer),正如[《结构类型的成员式构造方法》]()所描述的。我们可以使用这个构造方法创建一个新的常量(constant)`theAceOfSpades`: + +``` +let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +println("theAceOfSpades: \(theAceOfSpades.description)") +// prints "theAceOfSpades: suit is ♠, value is 1 or 11 + +``` + +``` +let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +println("theAceOfSpades: \(theAceOfSpades.description)") +// 输出 "theAceOfSpades: suit is ♠, value is 1 or 11 + +``` + +Even though `Rank` and `Suit` are nested within `BlackjackCard`, their type can be inferred from context, and so the initialization of this instance is able to refer to the enumeration members by their member names (`.Ace` and `.Spades`) alone. In the example above, the `description` property correctly reports that the Ace of Spades has a value of `1` or `11`. + +即使`Rank`和`Suit`被嵌套在`BlackjackCard`里,他们依然可以被~~更外层的~~上下文引用。所以构造`BlackjackCard`实例时,可以仅通过成员名称(`.Ace` 和 `.Spades`)来引用枚举类型的成员。上面的例子中,属性`description`如我们所愿,输出了黑桃A的值`1`或`11`。 + +##Referring to Nested Types +##引用嵌套类型 + +To use a nested type outside of its definition context, prefix its name with the name of the type it is nested within: + +外部引用嵌套类型时,仅需在其名称前加上外层类型的名称即可: + +``` +let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw() +// heartsSymbol is "♡" +``` + +For the example above, this enables the names of `Suit`, `Rank`, and `Values` to be kept deliberately short, because their names are naturally qualified by the context in which they are defined. + +正如上例所示,嵌套类型的引用方式可以使`Suit`、`Rank`和`Values`的名字尽可能的简短,因为他们的名字会自然地被所定义的上下文限制。 + diff --git a/src/chapter2/20_Extensions.md b/src/chapter2/20_Extensions.md index e69de29..dcca041 100644 --- a/src/chapter2/20_Extensions.md +++ b/src/chapter2/20_Extensions.md @@ -0,0 +1,283 @@ +# 扩展(Extensions) + +扩展为已有的类,结构,枚举添加新的功能。其中包括扩展没有代码访问权限的类型(即追溯建模,retroactive modeling)。扩展和 Objective-C 当中的分类(category)很相似。(与 Objective-C 中的分类不同的是,Swift 的扩展没有名字) + +Swift 中的扩展可以: +- 增加计算属性和静态计算属性 +- 定义实例方法和类型方法 +- 提供新的构造器 +- 定义下标 +- 定义和使用新的嵌套类型 +- 将已有的类型转换为符合某一个协议 + +> 提示 +> +> 如果定义一个扩展用来在已有的类型上增加新的功能,那么新功能会应用在所有已经存在的实例上,即使是在扩展被定义之前实例化的。 +## 扩展的语法 + +使用 ``extension`` 关键字来声明扩展: + +``` +extension SomeType { + // new functionality to add to SomeType goes here +} +``` + +一个扩展可以扩展一个已有的类型,使它能适配一个或多个的协议(protocol)。如果是这种情况的话,协议的命名方式应该与类和结构体的命名方式完全一致。 + +``` +extension SomeType: SomeProtocol, AnotherProtocol { + // implementation of protocol requirements goes here +} +``` + +这种增加协议一致性(protocol conformance)的方式在 [通过扩展增加协议一致性](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_355) 一文中有记述。 + +## 计算属性(Computed Properties) + +扩展可以为已有的类型增加实例计算属性和类型计算属性。这个例子向 Swift 内建类型 ``Double`` 添加五个实例计算类型,用来提供转换为距离单位的基本功能: + +``` +extension Double { + var km: Double { return self * 1_000.0 } + var m: Double { return self } + var cm: Double { return self / 100.0 } + var mm: Double { return self / 1_000.0 } + var ft: Double { return self / 3.28084 } +} +let oneInch = 25.4.mm +println("One inch is \(oneInch) meters") +// prints "One inch is 0.0254 meters" +let threeFeet = 3.ft +println("Three feet is \(threeFeet) meters") +// prints "Three feet is 0.914399970739201 meters" +``` + +这些计算属性表达的是一个 ``Double`` 类型的值是某种长度单位下的值。尽管是计算属性,但他们仍然可以接在一个带有 dot 语法的字面值的后面,用来将字面值转换成距离。 + +在上述的例子中,``Double`` 类型的 ``1.0`` 代表 “一米” 。这是为什么计算属性 ``m`` 仅仅返回 ``self`` ——表达式 ``1.m`` 最终结果是 ``Double`` 类型的值 ``1.0`` 。 + +其他单位转换为以米为单位的数值需要进行一些转换。1千米等于1,000米,所以计算属性 ``km`` 要把数值转换成以米为单位,需要把值乘以 ''1_000.00''。同样,1英尺等于 ''3.28084'' 米,所以计算属性 ''ft'' 需要把值除以 ''3.28024'' 才能把英尺转换成米。 + +因为这些都是只读的计算属性,所以为了简便起见,不需要关键字 ''keyword'' 进行表示。他们的返回值都是 ``Double`` 类型的,所以可以用在所有可以接受 ``Double`` 类型的数学计算中: + +``` +let aMarathon = 42.km + 195.m +println("A marathon is \(aMarathon) meters long") +// prints "A marathon is 42195.0 meters long" +``` + +> 提示 +> +> 扩展可以添加新的计算属性,但是不能添加存储属性,也不能向已有属性添加属性观察器(property observer)。 + +## 构造器(Initializers) + +扩展能向已有类型添加新的构造器。这允许你用自己定义的类型作为构造器参数扩展其他的类型,或者提供原始实现没有提供的额外的初始化选项 + +扩展能向类添加新的简便构造器,但是不能添加新的指定构造器或者析构器。指定构造器和析构器必须在类的原始实现中提供。 + +> 提示 +> +> 如果使用扩展向一个值类型添加构造器,该构造器向所有存储属性提供默认值并且未定义任何其他的自定义构造器,你可以调用默认的构造和成员构造器来为你扩展的构造器当中的值类型赋值。 +> 正如 [构造器对值类型的构造委托](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_281) 一文所说的那样,如果你已经把构造器写成值类型原始实现的一部分,则不符合上述规则。 + +在下面的例子里,定义了一个用于描述几何矩形的结构体 ``Rect`` 。同时定义了两个辅助性结构体 ``Size``,``Point``,两个结构体当中的属性默认值都为 ``0.0``: + +``` +struct Size { + var width = 0.0, height = 0.0 +} +struct Point { + var x = 0.0, y = 0.0 +} +struct Rect { + var origin = Point() + var size = Size() +} +``` + +因为 结构体 ``Rect`` 为所有的属性都提供了默认值,正如默认构造器一节所说的,它可以自动接受默认构造器和成员构造器。这些构造器可以用来创建新的 ``Rect`` 实例: + +``` +let defaultRect = Rect() +let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), + size: Size(width: 5.0, height: 5.0)) +``` + +你可以使用扩展来为结构体 ``Rect`` 额外提供一个以中心点和大小作为参数的构造器: + +``` +extension Rect { + init(center: Point, size: Size) { + let originX = center.x - (size.width / 2) + let originY = center.y - (size.height / 2) + self.init(origin: Point(x: originX, y: originY), size: size) + } +} +``` + +新构造器通过中心点和大小两个参数计算出合适的原点值,然后调用结构体的自动成员构造器 ``init(origin:size:)`` ,把计算出的值存储到合适的属性上: + +``` +let centerRect = Rect(center: Point(x: 4.0, y: 4.0), + size: Size(width: 3.0, height: 3.0)) +// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0) +``` + + +> 提示 +> +> 如果使用扩展来提供新的构造器,那么你仍要确保构造过程中,每一个实例都被完全初始化了。 + + +## 方法(Methods) + +扩展可以向已有类型添加新的实例方法和类型方法。下面的例子中,向 类型 ``Int`` 中添加了新的实例方法 ``repetitions``: + +``` +extension Int { + func repetitions(task: () -> ()) { + for i in 0..self { + task() + } + } +} +``` + +``repetitions`` 方法的参数是 ``() -> ()``,说明参数是一个无参数无返回值的函数 + +扩展被定义之后,你就可以在任何整数上调用 ``repetitions`` 方法,来多次执行某个任务: + +``` +3.repetitions({ + println("Hello!") +}) +// Hello! +// Hello! +// Hello! +``` + +使用尾随闭包(trailing closure)语法可以使调用更简洁: + +``` +3.repetitions { + println("Goodbye!") +} +// Goodbye! +// Goodbye! +// Goodbye! +``` + +## 可变实例方法(Mutating Instance Methods) + +通过扩展添加的实例方法也可以修改实例本身。结构体和枚举类型的方法中改变 ``self`` 或者其中的属性,必须标记实例的方法为 ``mutating`` ,就像原始实现中的声明变异方法(mutating method)一样。 + +下面的例子为 Swift 中的 ``Int`` 类型添加了一个新的变异方法 ``square``,用来计算原始值的平方: + +``` +extension Int { + mutating func square() { + self = self * self + } +} +var someInt = 3 +someInt.square() +// someInt is now 9 +``` + +## 下标(Subscripts) + +扩展可以为已有类型添加新的下标。下面的例子为 Swift 中的内建类型 ``Int`` 添加一个整型下标。下标 ``[n]`` 返回 十进制数从右往左第 n 位上的数字: + +- 123456789[0] returns 9 +- 123456789[1] returns 8 + +……以此类推: + +``` +extension Int { + subscript(digitIndex: Int) -> Int { + var decimalBase = 1 + for _ in 1...digitIndex { + decimalBase *= 10 + } + return (self / decimalBase) % 10 + } +} +746381295[0] +// returns 5 +746381295[1] +// returns 9 +746381295[2] +// returns 2 +746381295[8] +// returns 7 +``` + +如果该 ``Int`` 值没有足够的位数与请求对应,即下标越界,则下标会返回 ``0`` ,就好像它自动在数字左边补0一样: + +``` +746381295[9] +// returns 0, as if you had requested: +0746381295[9] +``` + + +## 嵌套类型(Nested Types) + +扩展可以向已有的类,结构体和枚举类型添加新的嵌套类型: + +``` +extension Character { + enum Kind { + case Vowel, Consonant, Other + } + var kind: Kind { + switch String(self).lowercaseString { + case "a", "e", "i", "o", "u": + return .Vowel + case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", + "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": + return .Consonant + default: + return .Other + } + } +} +``` + +上面的例子为 ``Character`` 添加了新的嵌套枚举。枚举类型的 ``Kind`` 表示一个字符所属的种类。具体来说就是一个字符是拉丁字母的元音,辅音,还是其他种类的字符(不考虑口语和地方变种的情况下)。 + +这个例子中同时也为 ``Character`` 添加了一个新的实例计算属性 ``kind``,用来返回字符对应的枚举成员 ``Kind`` + +现在嵌套枚举可以在 ``Character`` 上面使用了: + +``` +func printLetterKinds(word: String) { + println("'\(word)' is made up of the following kinds of letters:") + for character in word { + switch character.kind { + case .Vowel: + print("vowel ") + case .Consonant: + print("consonant ") + case .Other: + print("other ") + } + } + print("\n") +} +printLetterKinds("Hello") +// 'Hello' is made up of the following kinds of letters: +// consonant vowel consonant consonant vowel +``` + +``printLetterKinds`` 函数迭代 ``String`` 类型的参数的每一个字母。每次迭代都根据当前字母包含的计算属性 ``kind`` 输出对应的类型描述。这样,``printLetterKinds`` 函数就输出了一个单词内所有字母的类型,正如上面例子中的单词 "word" 一样。 + + +> 提示 +> +> 因为已知 ``character.kind`` 的类型是 ``Character.Kind``,所以所有 ``Character.Kind`` 的成员值都可以在 ``switch`` 语句中使用简写形式,比如使用 ``.Vowel`` 来代替 ``Character.Kind.Vowel``。 + + diff --git a/src/chapter2/21_Protocols.md b/src/chapter2/21_Protocols.md index e69de29..ce4150f 100644 --- a/src/chapter2/21_Protocols.md +++ b/src/chapter2/21_Protocols.md @@ -0,0 +1,943 @@ +#Protocols +A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol doesn’t actually provide an implementation for any of these requirements—it only describes what an implementation will look like. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol. + + +#协议 + +一个协议(protocol)定义了适合特定的任务或功能的方法、属性和其他需求。协议并不提供这些需求的实现,只描述了这个实现应该是怎样的。 + +协议能够被类(class),结构体(structure),枚举(enumeration)适配(adopted),同时,这些类型如果了满足一个协议需求,则被称为遵循(conform to)协议 + +Protocols can require that conforming types have specific instance properties, instance methods, type methods, operators, and subscripts. + +协议可以要求遵循(conforming)的类型有特定的实例属性(instance properties),实例方法(instance methods),类型方法(type methods),操作符(operators)和下标(subscripts)。 + + +##Protocol Syntax +##协议语法 +You define protocols in a very similar way to classes, structures, and enumerations: + +定义协议,与定义类,结构体,枚举的方式非常相似,如下所示: + + protocol SomeProtocol { + // protocol definition goes here + } + +Custom types state that they adopt a particular protocol by placing the protocol’s name after the type’s name, separated by a colon, as part of their definition. Multiple protocols can be listed, and are separated by commas: + +在类型名称后添加协议名称,并以冒号`:`分割,表示自定义类型(Custom types)适配一个特定协议;实现多个协议时,各协议之间用逗号`,`分隔,如下所示: + + struct SomeStructure: FirstProtocol, AnotherProtocol { + // structure definition goes here + } +If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma: + +若某个类有父类,要把父类放在所有其适配的协议之前,且用逗号`,`分割,如下所示: + + class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { + // class definition goes here + } + +Property Requirements + +##属性要求 + +A protocol can require any conforming type to provide an instance property or type property with a particular name and type. The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettable and settable. + +协议可以要求任何遵循(conforming)类型,提供一个特定名称和类型的实例属性(instance property)或类型属性(type property)。协议不指定属性是存储型属性(stored property)还是计算型属性(calculate property)。 +协议同时指定了每个属性是否是可读(gettable)或可读写(gettable and settable) + + +If a protocol requires a property to be gettable and settable, that property requirement cannot be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it is valid for it also to be settable if this is useful for your own code. + +如果协议要求属性可读写(gettable and settable),那常量存储属性(constant stored property)或者只读计算属性(read-only computed property)都无法满足此要求。如果协议只要求属性可读(gettable),那任何类型的属性都满足这个要求,即使这些属性是可写(settable)的。 + +Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }. + +协议里的属性要求,通常都是通过使用`var`关键字来声明的变量属性。 +在类型声明之后用{ get set }表示属性为可读写的。{ get }表示属性为可读的。 + + protocol SomeProtocol { + var mustBeSettable: Int { get set } + var doesNotNeedToBeSettable: Int { get } + } + +Always prefix type property requirements with the class keyword when you define them in a protocol. This is true even though type property requirements are prefixed with the static keyword when implemented by a structure or enumeration: + +当你在一个协议中定义一个某种类型的属性要求,总要前置`class` 关键字。同样某类型属性要求使用`static`关键字前缀,来定义结构体(structure)或枚举类型(enumeration) + +> 这样翻译正确么? + + protocol AnotherProtocol { + class var someTypeProperty: Int { get set } + } + +Here’s an example of a protocol with a single instance property requirement: + +下面是协议的一个例子,这个协议只有一个实例属性(instance property)要求: + + + protocol FullyNamed { + var fullName: String { get } + } + +The FullyNamed protocol defines any kind of thing that has a fully-qualified name. It doesn’t specify what kind of thing it must be—it only specifies that the thing must be able to provide a full name for itself. It specifies this requirement by stating that any FullyNamed type must have a gettable instance property called fullName, which is of type String. + +`FullyNamed`协议可以定义任何需要一个完整`name`的类型(方法,属性或者其他需求)。这个协议并不指定什么,只有一个需求:这个类型本身必须提供一个完整的名称。这个需求通过声明一个`String`类型的实例属性`fullName`,且这个属性必须是可读的(gettable)来指定。 + + + +Here’s an example of a simple structure that adopts and conforms to the FullyNamed protocol: + +下面的例子中,一个简单的结构体适配且遵循`FullyNamed`协议 + + struct Person: FullyNamed { + var fullName: String + } + let john = Person(fullName: "John Appleseed") + // john.fullName is "John Appleseed" + +This example defines a structure called Person, which represents a specific named person. It states that it adopts the FullyNamed protocol as part of the first line of its definition. + +这个例子中定义了一个名为`Person`的结构体,代表一个有名字的人。它在第一行的结构体定义中,声明了其适配`FullyNamed`协议。 + +Each instance of Person has a single stored property called fullName, which is of type String. This matches the single requirement of the FullyNamed protocol, and means that Person has correctly conformed to the protocol. (Swift reports an error at compile-time if a protocol requirement is not fulfilled.) + +每个`Person`实例都有一个单独存储的,`String`类型的属性`fullName`。拥有这个属性代表满足了`FullyNamed`协议的要求,这意味着`Person`遵循协议。(在编译时如果不满足协议要求,swift会报告一个错误)。 + + +Here’s a more complex class, which also adopts and conforms to the FullyNamed protocol: +下面有一个更复杂的类,同样适配且遵循`FullyNamed`协议 + + class Starship: FullyNamed { + var prefix: String? + var name: String + init(name: String, prefix: String? = nil) { + self.name = name + self.prefix = prefix + } + var fullName: String { + return (prefix ? prefix! + " " : "") + name + } + } + var ncc1701 = Starship(name: "Enterprise", prefix: "USS") + // ncc1701.fullName is "USS Enterprise" + +This class implements the fullName property requirement as a computed read-only property for a starship. Each Starship class instance stores a mandatory name and an optional prefix. The fullName property uses the prefix value if it exists, and prepends it to the beginning of name to create a full name for the starship. +这个类提供了`fullName`属性,这个属性是`starship`这个类的一个只读的计算属性。 +每个`starship`类的实例存储一个强制性的名称和一个可选的前缀。当存在前缀时,`fullName`属性会把前缀添加到名称前面,从而创建了一个`starship`的全名。 + + +Method Requirements + +Protocols can require specific instance methods and type methods to be implemented by conforming types. These methods are written as part of the protocol’s definition in exactly the same way as for normal instance and type methods, but without curly braces or a method body. Variadic parameters are allowed, subject to the same rules as for normal methods. + + + +方法需求 + +若某种类型遵循协议,协议可以要求指定特定的实例方法和类型方法。这些方法作为协议定义的一部分,跟通常定义实例与类型方法的途径完全一样,但不需要书写大括号或方法的主体。方法允许含有可变参数,且与通常的方法遵循同样的规则。 + +NOTE + +Protocols use the same syntax as normal methods, but are not allowed to specify default values for method parameters. + +注意 + +协议与通常的方法语法相同,但不允许指定方法中参数的默认值 + +As with type property requirements, you always prefix type method requirements with the class keyword when they are defined in a protocol. This is true even though type method requirements are prefixed with the static keyword when implemented by a structure or enumeration: + + protocol SomeProtocol { + class func someTypeMethod() + } + +当你在一个协议中定义一个某种类型的方法要求,要前置`class` 关键字。同样某类型方法要求使用`static`关键字前缀,来定义结构体(structure)或枚举类型(enumeration) + +The following example defines a protocol with a single instance method requirement: +下面的例子中,定义了一个协议,协议要求一个实例方法。 + + protocol RandomNumberGenerator { + func random() -> Double + } +This protocol, RandomNumberGenerator, requires any conforming type to have an instance method called random, which returns a Double value whenever it is called. (Although it is not specified as part of the protocol, it is assumed that this value will be a number between 0.0 and 1.0 inclusive.) + +`RandomNumberGenerator`,该协议要求任何遵循协议的类型调用一个名为random的实例方法,这个方法无论何时调用,都会返回一个Double类型的值。(这个值在0.0与1.0之间,这一点没有在协议中指出)。 + + +The RandomNumberGenerator protocol does not make any assumptions about how each random number will be generated—it simply requires the generator to provide a standard way to generate a new random number. + +Here’s an implementation of a class that adopts and conforms to the RandomNumberGenerator protocol. This class implements a pseudorandom number generator algorithm known as a linear congruential generator: + + `RandomNumberGenerator`协议不会描述如何建立一个随机数,只需要指定一个随机数生成器,从而提供一种标准的方式来生成一个新随机数。 +   + 下面是一个类的实现,遵循`RandomNumberGenerator`协议。这个类实现了一种伪随机数发生器算法,称为线性同余(linear congruential)发生器: + + + class LinearCongruentialGenerator: RandomNumberGenerator { + var lastRandom = 42.0 + let m = 139968.0 + let a = 3877.0 + let c = 29573.0 + func random() -> Double { + lastRandom = ((lastRandom * a + c) % m) + return lastRandom / m + } + } + let generator = LinearCongruentialGenerator() + println("Here's a random number: \(generator.random())") + // prints "Here's a random number: 0.37464991998171" + println("And another one: \(generator.random())") + // prints "And another one: 0.729023776863283" + +Mutating Method Requirements + +突变方法要求 + +It is sometimes necessary for a method to modify (or mutate) the instance it belongs to. For instance methods on value types (that is, structures and enumerations) you place the mutating keyword before a method’s func keyword to indicate that the method is allowed to modify the instance it belongs to and/or any properties of that instance. This process is described in Modifying Value Types from Within Instance Methods. + +有时,实例的一个方法需要修改(或突变)实例的类型。对值类型(value types)(即结构体和枚举类型)的实例方法中,使用`mutating`关键字,写在方法的函数关键字`fun`前面,来表明实例中该方法允许修改其类型及任何属性。这个过程在 `在实例方法中修改值类型`章节中有详细的描述。 + +If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement. + +如果你定义的协议中,某个实例适配此协议,而实例方法需要改变其类型,协议定义时要在此方法前加上关键字`mutating`。这样可以让结构体及枚举类型适配协议,且满足实例方法的需求。 + +NOTE +注意 + +If you mark a protocol instance method requirement as mutating, you do not need to write the mutating keyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations. + + +如果你在一个协议中实现的突变方法是为一个类服务的,不需要为其加`上mutating`关键字。 +mutating`关键字只用在结构体与枚举类型中。 +` + +The example below defines a protocol called Togglable, which defines a single instance method requirement called toggle. As its name suggests, the toggle method is intended to toggle or invert the state of any conforming type, typically by modifying a property of that type. + +下面的例子中定义了一个`togglable`协议,包含一个名为`togger`的突变实例方法。正如名称所表达的,适配此协议的某实例中,toggle方法通常会通过改变实例的某个属性,来切换或恢复其类型。 + +The toggle method is marked with the mutating keyword as part of the Togglable protocol definition, to indicate that the method is expected to mutate the state of a conforming instance when it is called: + +`toggle`方法前面加上了`mutating`关键字,作为`Togglabel`协议定义的一部分,则表示一个适配`toggleabel`协议的实例中,这个方法会在调用时改变实例的类型。 + + protocol Togglable { + mutating func toggle() + } + +If you implement the Togglable protocol for a structure or enumeration, that structure or enumeration can conform to the protocol by providing an implementation of the toggle method that is also marked as mutating. + +当你提供的枚举或结构体遵循`Togglable`协议时,需要提供一个带有`mutating`前缀的`toggle`方法。 + + +The example below defines an enumeration called OnOffSwitch. This enumeration toggles between two states, indicated by the enumeration cases On and Off. The enumeration’s toggle implementation is marked as mutating, to match the Togglable protocol’s requirements: + + enum OnOffSwitch: Togglable { + case Off, On + mutating func toggle() { + switch self { + case Off: + self = On + case On: + self = Off + } + } + } + var lightSwitch = OnOffSwitch.Off + lightSwitch.toggle() + // lightSwitch is now equal to .On + +Protocols as Types + +协议类型 + +Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code. + +尽管协议本身并不实现任何功能,但协议可以作为完整的类型来使用。 + +Because it is a type, you can use a protocol in many places where other types are allowed, including: + +你可以把协议类型用在其他类型适用的场景里,比如: + +As a parameter type or return type in a function, method, or initializer + +在函数,方法或构造方法中作为形参类型(parameter type)或返回值类型(return type) + +As the type of a constant, variable, or property + +作为常量、变量或属性这三种类型之一 + +As the type of items in an array, dictionary, or other container + +作为数组,字典或其他容器中的元素类型 + +NOTE +注意 + +Because protocols are types, begin their names with a capital letter (such as FullyNamed and RandomNumberGenerator) to match the names of other types in Swift (such as Int, String, and Double). + +注意: 协议是一种类型,因此协议类型的名称应与Swift中其他类型(Int,Double,String)的写法相同,每一个单字的首字母都采用大写字母(大驼峰写法) + +Here’s an example of a protocol used as a type: + +下面的例子中,协议被当作类型使用: + + class Dice { + let sides: Int + let generator: RandomNumberGenerator + init(sides: Int, generator: RandomNumberGenerator) { + self.sides = sides + self.generator = generator + } + func roll() -> Int { + return Int(generator.random() * Double(sides)) + 1 + } + } + +This example defines a new class called Dice, which represents an n-sided dice for use in a board game. Dice instances have an integer property called sides, which represents how many sides they have, and a property called generator, which provides a random number generator from which to create dice roll values. + +例子中定义了一个`Dice`类,用来表示桌游中的拥有N个面的骰子。`Dice`的实例包含`sides`和`generator`两个属性,前者是整型(integer),用来表示骰子有几个面,后者提供了一个随机数生成器,来表示骰子掷出的值。 + +The generator property is of type RandomNumberGenerator. Therefore, you can set it to an instance of any type that adopts the RandomNumberGenerator protocol. Nothing else is required of the instance you assign to this property, except that the instance must adopt the RandomNumberGenerator protocol. + +`generator`属性的类型为`RandomNumberGenerator`,因此任何类型,若适配`RandomNumberGenerator`协议,其实例都可以赋值给`generator`,除此以外没有其他要求。 + +Dice also has an initializer, to set up its initial state. This initializer has a parameter called generator, which is also of type RandomNumberGenerator. You can pass a value of any conforming type in to this parameter when initializing a new Dice instance. + +`Dice`类也包含一个构造方法,用于设置其初始状态。这个方法包含一个同样是`RandomNumberGenerator`类型的参数`generator`。在构造一个新的`Dice`的实例时,可以传入任何遵循`RandomNumberGenerator`协议的类型作为`generator` + +Dice provides one instance method, roll, which returns an integer value between 1 and the number of sides on the dice. This method calls the generator’s random method to create a new random number between 0.0 and 1.0, and uses this random number to create a dice roll value within the correct range. Because generator is known to adopt RandomNumberGenerator, it is guaranteed to have a random method to call. + +Dice类提供了一个实例方法`roll`,此方法会返回一个整型值,介于1和骰子面的数量之间。这个方法调用`generator`的随机数方法,来创建一在0.0和1.0之间的随机数,并使用这个随机数来确保骰子的滚动值在正确的范围内。因为`generator`适配`RandomNumberGenerator`协议,因此它可以保证有一个`random`方法供调用。 + +Here’s how the Dice class can be used to create a six-sided dice with a LinearCongruentialGenerator instance as its random number generator: + +下面的例子中,展示了如何使用线性同余发生器(LinearCongruentialGenerator)的实例作为随机数生成器,从而创建一个六面的骰子: + + + var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) + for _ in 1...5 { + println("Random dice roll is \(d6.roll())") + } + // Random dice roll is 3 + // Random dice roll is 5 + // Random dice roll is 4 + // Random dice roll is 5 + // Random dice roll is 4 + +Delegation +代理 + +Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source. + +代理(Delegation)是一种设计模式,它允许类或结构体将一些他们负责的功能转交(代理)给其他类型的实例。 +委托模式的实现需要定义一个协议,这个协议封装了那些需要被代理的功能,而一个遵循此协议的实例则拥有这些功能 + +The example below defines two protocols for use with dice-based board games: + +下面的例子中定义了两个基于骰子游戏的协议: + + protocol DiceGame { + var dice: Dice { get } + func play() + } + protocol DiceGameDelegate { + func gameDidStart(game: DiceGame) + func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) + func gameDidEnd(game: DiceGame) + } + +The DiceGame protocol is a protocol that can be adopted by any game that involves dice. The DiceGameDelegate protocol can be adopted by any type to track the progress of a DiceGame. + +`DiceGame`协议可以被任意含有骰子的游戏所适配,`DiceGameDelegate`协议可以被任意用来追踪`DiceGame`过程的类型所适配 + +Here’s a version of the Snakes and Ladders game originally introduced in Control Flow. This version is adapted to use a Dice instance for its dice-rolls; to adopt the DiceGame protocol; and to notify a DiceGameDelegate about its progress: + +这里有一个,`Snakes and Ladders`游戏的新版本(之前的版本在流程控制这一节中介绍过)。新版本使用`Dice`类的实例作为掷骰子的需求,并且适配了`DiceGame`协议。同时通知(notify)`DiceGameDelegate`协议,用来记录游戏过程: + + class SnakesAndLadders: DiceGame { + let finalSquare = 25 + let dice = Dice(sides: 6, generator: LinearCongruentialGenerator()) + var square = 0 + var board: Int[] + init() { + board = Int[](count: finalSquare + 1, repeatedValue: 0) + board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 + board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 + } + var delegate: DiceGameDelegate? + func play() { + square = 0 + delegate?.gameDidStart(self) + gameLoop: while square != finalSquare { + let diceRoll = dice.roll() + delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll) + switch square + diceRoll { + case finalSquare: + break gameLoop + case let newSquare where newSquare > finalSquare: + continue gameLoop + default: + square += diceRoll + square += board[square] + } + } + delegate?.gameDidEnd(self) + } + } +For a description of the Snakes and Ladders gameplay, see the Break section of the Control Flow chapter. + +你可以参考流程控制章节中对此游戏的介绍 + +> 译者注 Snakes and Ladders 是蛇梯棋游戏,一种类似大富翁的游戏。 + +This version of the game is wrapped up as a class called SnakesAndLadders, which adopts the DiceGame protocol. It provides a gettable dice property and a play method in order to conform to the protocol. (The dice property is declared as a constant property because it does not need to change after initialization, and the protocol only requires that it is gettable.) + +这个版本的游戏封装为`SnakesAndLadders`类,该类适配`DiceGame`协议。这个类还提供了可读的`dice`属性和`play`方法,从而遵循了`DiceGame`协议。(`dice`属性声明为常量,因为构造之后dice属性就不在改变,且协议只要求`dice`为只读) + + + +The Snakes and Ladders game board setup takes place within the class’s init() initializer. All game logic is moved into the protocol’s play method, which uses the protocol’s required dice property to provide its dice roll values. + +`Snakes And Ladders`游戏通过构造方法(initializer)`init`初始化游戏。所有的游戏逻辑移到了`play`方法中,`play`方法使用协议要求的`dice`属性,来提供骰子掷出的值。 + +Note that the delegate property is defined as an optional DiceGameDelegate, because a delegate isn’t required in order to play the game. Because it is of an optional type, the delegate property is automatically set to an initial value of nil. Thereafter, the game instantiator has the option to set the property to a suitable delegate. + +注意,`delegate`属性并不是游戏运行所必须的,因此`delegate`被定义为`DiceGameDelegate`协议类型的可选属性,`delegate`使用nil作为初始值。此后,游戏实例可以选择将其设置为一个合适的代理。 + + + +DiceGameDelegate provides three methods for tracking the progress of a game. These three methods have been incorporated into the game logic within the play method above, and are called when a new game starts, a new turn begins, or the game ends. + +`DicegameDelegate`协议提供了三个方法来跟踪游戏过程。这三个方法存在于游戏的逻辑中,即上面的play()方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。 + + +Because the delegate property is an optional DiceGameDelegate, the play method uses optional chaining each time it calls a method on the delegate. If the delegate property is nil, these delegate calls fail gracefully and without error. If the delegate property is non-nil, the delegate methods are called, and are passed the SnakesAndLadders instance as a parameter. + +因为`delegate`属性是一个遵循`DiceGameDelegate`的可选属性,因此在`play`方法中使用了可选链(optional chaining )来在代理时调用方法。 若`delegate`属性为`nil`, 则`delegate`所调用的方法正常失败(fail gracefully)且不报错。若`delegate`不为`nil`,则方法能够被调用,且作为一个参数传入`SnakesAndLadders`实例中 + +This next example shows a class called DiceGameTracker, which adopts the DiceGameDelegate protocol: + +下一个例子展示了一个名为`DiceGameTracker`的类,适配`DiceGameDelegate`协议。 + + class DiceGameTracker: DiceGameDelegate { + var numberOfTurns = 0 + func gameDidStart(game: DiceGame) { + numberOfTurns = 0 + if game is SnakesAndLadders { + println("Started a new game of Snakes and Ladders") + } + println("The game is using a \(game.dice.sides)-sided dice") + } + func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) { + ++numberOfTurns + println("Rolled a \(diceRoll)") + } + func gameDidEnd(game: DiceGame) { + println("The game lasted for \(numberOfTurns) turns") + } + } + +DiceGameTracker implements all three methods required by DiceGameDelegate. It uses these methods to keep track of the number of turns a game has taken. It resets a numberOfTurns property to zero when the game starts; increments it each time a new turn begins; and prints out the total number of turns once the game has ended. + +DiceGameTracker提供了DiceGameDelegate所要求的三个方法。它使用这三个方法来跟踪游戏进行的轮数。当游戏开始时,将numberOfTurns属性重置为0。每轮开始后递增这个值。当游戏结束时打印游戏进行的总轮数 + +The implementation of gameDidStart shown above uses the game parameter to print some introductory information about the game that is about to be played. The game parameter has a type of DiceGame, not SnakesAndLadders, and so gameDidStart can access and use only methods and properties that are implemented as part of the DiceGame protocol. However, the method is still able to use type casting to query the type of the underlying instance. In this example, it checks whether game is actually an instance of SnakesAndLadders behind the scenes, and prints an appropriate message if so. + +在如上所示的gameDidStart方法的实现中,游戏即将开始时使用了game参数来打印一些介绍性信息。game参数拥有一个DiceGame类型,而不是SnakesAndLadders,所以gameDidStart方法只能访问和使用DiceGame协议中的方法和属性。然而,该方法仍然能够使用类型检查(type casting)查询底层实例的类型。在这个例子中,它在幕后检查游戏是否是SnakesAndLadders的一个实例,当确认后打印一个适当的消息。 + +gameDidStart also accesses the dice property of the passed game parameter. Because game is known to conform to the DiceGame protocol, it is guaranteed to have a dice property, and so the gameDidStart method is able to access and print the dice’s sides property, regardless of what kind of game is being played. + +gameDidStart方法也能够访问dice属性,这个属性存在于被传递为参数的game中。game遵循DiceGame协议,因此被赋予了dice属性,不管运行何种游戏,gameDidStart方法都可以访问且打印骰子的边数。 + + +下面是DiceGameTracker的运行过程: +Here’s how DiceGameTracker looks in action: + + let tracker = DiceGameTracker() + let game = SnakesAndLadders() + game.delegate = tracker + game.play() + // Started a new game of Snakes and Ladders + // The game is using a 6-sided dice + // Rolled a 3 + // Rolled a 5 + // Rolled a 4 + // Rolled a 5 + // The game lasted for 4 turns + +Adding Protocol Conformance with an Extension + +使用扩展添加协议一致性 + +You can extend an existing type to adopt and conform to a new protocol, even if you do not have access to the source code for the existing type. Extensions can add new properties, methods, and subscripts to an existing type, and are therefore able to add any requirements that a protocol may demand. For more about extensions, see Extensions. + +即便不能修改现有类型的代码,你也可以扩展现有类型,从而适配和遵循新协议。扩展(extensions)可以在现有的类型上添加新属性、方法和下标,因此能够满足任何一个协议的需要。更多关于扩展相关的内容,请参考扩展。 + +NOTE +注意 + +Existing instances of a type automatically adopt and conform to a protocol when that conformance is added to the instance’s type in an extension. + +当你使用一个扩展,为一个实例的类型添加上某种对协议的适配后,这个类型已存在的所有实例,都会自动适配与遵循此协议。 + +For example, this protocol, called TextRepresentable, can be implemented by any type that has a way to be represented as text. This might be a description of itself, or a text version of its current state: + +举例来讲,下面的TextRepersentable协议,可以被任何能表现为文字的类型所适配。这有可能是其自身的描述,或是其状态的文字版本。 + + protocol TextRepresentable { + func asText() -> String + } + +The Dice class from earlier can be extended to adopt and conform to TextRepresentable: + +之前的Dice类可以扩展为适配并遵循TextRepresentable协议: + + extension Dice: TextRepresentable { + func asText() -> String { + return "A \(sides)-sided dice" + } + } + +This extension adopts the new protocol in exactly the same way as if Dice had provided it in its original implementation. The protocol name is provided after the type name, separated by a colon, and an implementation of all requirements of the protocol is provided within the extension’s curly braces. + +这个扩展以完全相同的方式适配了新协议,就像骰子提供了最初(适配协议)的实现。协议名称放在类型名称后,用`:`隔开,协议所要求的实现则提供在扩展的花括号内。 + + +Any Dice instance can now be treated as TextRepresentable: + +现在Dice类型的实例可被当作是TextRepresentable类型: + + let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator()) + println(d12.asText()) + // prints "A 12-sided dice" + +Similarly, the SnakesAndLadders game class can be extended to adopt and conform to the TextRepresentable protocol: + +同样,SnakesAndLadders类也可以扩展为适配且遵循TextRepresentable协议: + + extension SnakesAndLadders: TextRepresentable { + func asText() -> String { + return "A game of Snakes and Ladders with \(finalSquare) squares" + } + } + println(game.asText()) + // prints "A game of Snakes and Ladders with 25 squares" + +Declaring Protocol Adoption with an Extension + +通过扩展声明协议适配 + +If a type already conforms to all of the requirements of a protocol, but has not yet stated that it adopts that protocol, you can make it adopt the protocol with an empty extension: + +若一个类型已经遵循了一个协议的所有要求,但未声明此类型适配这个协议,你可以使用一个空的扩展来让其适配协议。 + + struct Hamster { + var name: String + func asText() -> String { + return "A hamster named \(name)" + } + } + extension Hamster: TextRepresentable {} + +Instances of Hamster can now be used wherever TextRepresentable is the required type: + +从现在起,Hamster的实例可以作为TextRepresentable所需要的类型来使用 + + let simonTheHamster = Hamster(name: "Simon") + let somethingTextRepresentable: TextRepresentable = simonTheHamster + println(somethingTextRepresentable.asText()) + // prints "A hamster named Simon" + +NOTE +注意 + +Types do not automatically adopt a protocol just by satisfying its requirements. They must always explicitly declare their adoption of the protocol. + +注意: 即使满足了协议的所有要求,类型也不会自动适配一个协议,因此你必须显式的声明协议适配。 + +Collections of Protocol Type + +集合中的协议类型 + +A protocol can be used as the type to be stored in a collection such as an array or a dictionary, as mentioned in Protocols as Types. This example creates an array of TextRepresentable things: + +协议可以作为集合(数组,字典)内所存储的类型使用,就像之前所讲的一样,协议作为类型使用: + + let things: TextRepresentable[] = [game, d12, simonTheHamster] + +It is now possible to iterate over the items in the array, and print each item’s textual representation: + +如下所示,可以遍历things数组的元素,并打印每个元素的文本(通过asText方法) + + for thing in things { + println(thing.asText()) + } + // A game of Snakes and Ladders with 25 squares + // A 12-sided dice + // A hamster named Simon + +Note that the thing constant is of type TextRepresentable. It is not of type Dice, or DiceGame, or Hamster, even if the actual instance behind the scenes is of one of those types. Nonetheless, because it is of type TextRepresentable, and anything that is TextRepresentable is known to have an asText method, it is safe to call thing.asText each time through the loop. + +注意,thing常量是TextRepresentable类型的。它并非Dice、DiceGame或Hamster类型,即使thing所代表的实例,背后其实是其中一个类型。 +尽管如此,因为thing是TextRepresentable协议类型,此类型都包含asText方法,因此在每次循环中可以安全的调用一次asText方法 + +Protocol Inheritance +协议继承 + +A protocol can inherit one or more other protocols and can add further requirements on top of the requirements it inherits. The syntax for protocol inheritance is similar to the syntax for class inheritance, but with the option to list multiple inherited protocols, separated by commas: + +协议能够继承一或多个其他协议,也可进一步在继承的协议上添加更多的需求。语法与类的继承相似,此外可以用逗号`,`分隔表示继承多个协议 + + protocol InheritingProtocol: SomeProtocol, AnotherProtocol { + // protocol definition goes here + } + +Here’s an example of a protocol that inherits the TextRepresentable protocol from above: +下面的例子中,一个协议继承了之前的TextRepresentable协议: + + protocol PrettyTextRepresentable: TextRepresentable { + func asPrettyText() -> String + } + +This example defines a new protocol, PrettyTextRepresentable, which inherits from TextRepresentable. Anything that adopts PrettyTextRepresentable must satisfy all of the requirements enforced by TextRepresentable, plus the additional requirements enforced by PrettyTextRepresentable. In this example, PrettyTextRepresentable adds a single requirement to provide an instance method called asPrettyText that returns a String. + +这个例子定义了一个新的协议PrettyTextRepresentable,继承自TextRepresentable。若适配PrettyTextRepresentable协议,必须同时满足TextRepresentable协议的需求,且满足由PrettyTextRepresentable添加的额外需求。在这个例子中,PrettyTextRepresentable添加了另一需求,要求提供一个称为asPrettyText的实例方法,用于返回一个字符串。 +The SnakesAndLadders class can be extended to adopt and conform to PrettyTextRepresentable: +可以扩展SnakesAndLadders类,来使之适配且遵循PrettyTextRepresentable协议。 + + extension SnakesAndLadders: PrettyTextRepresentable { + func asPrettyText() -> String { + var output = asText() + ":\n" + for index in 1...finalSquare { + switch board[index] { + case let ladder where ladder > 0: + output += "▲ " + case let snake where snake < 0: + output += "▼ " + default: + output += "○ " + } + } + return output + } + } + +This extension states that it adopts the PrettyTextRepresentable protocol and provides an implementation of the asPrettyText method for the SnakesAndLadders type. Anything that is PrettyTextRepresentable must also be TextRepresentable, and so the asPrettyText implementation starts by calling the asText method from the TextRepresentable protocol to begin an output string. It appends a colon and a line break, and uses this as the start of its pretty text representation. It then iterates through the array of board squares, and appends an emoji representation for each square: + +这个扩展声明其适配PrettyTextRepresentable协议,并提供了SnakesAndLadders类型PrettyText方法的一个实现。任何PrettyTextRepresentable协议也是TextRepresentable的扩展,所以,asPrettyText方法的实现过程是这样的:首先调用来自TextRepresentable协议的asText方法开始输出字符串。方法的输出会附加一个冒号和一个换行符,使用这个作为美化文本输出的开始。然后遍历这个方格板数组中的每一个格子,用一个符号来表示: + +If the square’s value is greater than 0, it is the base of a ladder, and is represented by ▲. +If the square’s value is less than 0, it is the head of a snake, and is represented by ▼. +Otherwise, the square’s value is 0, and it is a “free” square, represented by ○. +The method implementation can now be used to print a pretty text description of any SnakesAndLadders instance: + +当遍历出的元素的值大于0时,表示一个梯子的底部,用`▲`符号表示 +当遍历出的元素的值小于0时,表示一个蛇头,用`▲`符号表示 +当遍历出的元素的值等于0时,表示一个空白方块,用`○`符号表示 + + println(game.asPrettyText()) + // A game of Snakes and Ladders with 25 squares: + // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○ + +Protocol Composition +协议合成 + +It can be useful to require a type to conform to multiple protocols at once. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions have the form protocol. You can list as many protocols within the pair of angle brackets (<>) as you need, separated by commas. + +有一种很有用的方式,来表示一种适配多个协议的类型。你可以使用协议合成(Protocol Composition)来组合多个协议。 +协议合成的格式为protocal``。你可以在尖括号(`<>`)内列出多个协议,用逗号`,`分隔。 + + + +Here’s an example that combines two protocols called Named and Aged into a single protocol composition requirement on a function parameter: + +下面的例子中,把两个协议Named与Aged组合为一个协议合成,并作为函数的参数使用 + + protocol Named { + var name: String { get } + } + protocol Aged { + var age: Int { get } + } + struct Person: Named, Aged { + var name: String + var age: Int + } + func wishHappyBirthday(celebrator: protocol) { + println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!") + } + let birthdayPerson = Person(name: "Malcolm", age: 21) + wishHappyBirthday(birthdayPerson) + // prints "Happy birthday Malcolm - you're 21!" + + +This example defines a protocol called Named, with a single requirement for a gettable String property called name. It also defines a protocol called Aged, with a single requirement for a gettable Int property called age. Both of these protocols are adopted by a structure called Person. + +这个例子中,定义了一个Named协议,要求一个可读的String类型的属性`name`。同时定义了一个Aged协议,要求一个可读的Int类型的属性`Aged`。两个协议被`Person`结构体所适配。 + +The example also defines a function called wishHappyBirthday, which takes a single parameter called celebrator. The type of this parameter is protocol, which means “any type that conforms to both the Named and Aged protocols.” It doesn’t matter what specific type is passed to the function, as long as it conforms to both of the required protocols. + +这个例子也定义了带有参数`celebrator`的函数`wishHappyBirthday`。参数的类型是`protocol`,表示任何同时遵循Named与Aged协议的类型。不管向函数传递何种参数,只要遵循两个协议即可。 + + +The example then creates a new Person instance called birthdayPerson and passes this new instance to the wishHappyBirthday function. Because Person conforms to both protocols, this is a valid call, and the wishHappyBirthday function is able to print its birthday greeting. + +这个例子也创建了一个`Person`的实例`birthdayPerson`,且将这个实例作为参数传入`wishHappyBirthday`函数。因为Person同时适配 +两个协议,因此函数可以正常调用,并打印一个生日祝福。 + +NOTE +注意 + +Protocol compositions do not define a new, permanent protocol type. Rather, they define a temporary local protocol that has the combined requirements of all protocols in the composition. + +注意: 协议合成并不会生成一个新的协议类型,而是将多个协议合成为一个临时协议。 + +Checking for Protocol Conformance + +协议遵循检查 + +You can use the is and as operators described in Type Casting to check for protocol conformance, and to cast to a specific protocol. Checking for and casting to a protocol follows exactly the same syntax as checking for and casting to a type: + +你可以使用类型检查中介绍的`is`,`as`操作符,来检测某类型是否遵循某协议, + +The is operator returns true if an instance conforms to a protocol and returns false if it does not. +The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance does not conform to that protocol. +The as version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast does not succeed. + +is操作符用于检查实例是否遵循某个协议,若不遵循则返回`false`。 +as?根据协议类型返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil +as操作符可以对协议类型进行强制向下转型。若若换失败则会报一个运行时错误。 + +> 译者注: 向下转型就是把基类转换到继承类,向上转型就是把继承类转换为基类。 + +This example defines a protocol called HasArea, with a single property requirement of a gettable Double property called area: + +下面的例子定义了协议HasArea,需要一个可读的Double类型的属性area。 + + @objc protocol HasArea { + var area: Double { get } + } + +NOTE +注意 +You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance. + +注意: 只有用`@objc`属性标注的协议,才可以做协议遵循检查。这个属性表示协议会暴露给Objective-C的代码,可以参考Using Siwft with Cocoa and Objectivei-c。即使你不打算与 Objective-C进行交互,当你需要进行协议遵循检查时,也要在协议前面添加`@objc`. + +Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to check for conformance, you will be able to apply that protocol only to class types. + +还要注意一点,`@objc`协议只适用于类,不能用于结构体或枚举。因此只有协议被应用在类上,才能使用`@objc`检查协议的遵循。 + +Here are two classes, Circle and Country, both of which conform to the HasArea protocol: + +下面两个类`Circle`与`Country`都遵循`HasArea`协议 + + class Circle: HasArea { + let pi = 3.1415927 + var radius: Double + var area: Double { return pi * radius * radius } + init(radius: Double) { self.radius = radius } + } + class Country: HasArea { + var area: Double + init(area: Double) { self.area = area } + } + +The Circle class implements the area property requirement as a computed property, based on a stored radius property. The Country class implements the area requirement directly as a stored property. Both classes correctly conform to the HasArea protocol. + +`Circle`类把`area`属性实现为基于存储属性 `radius`的计算属性, `Country`类则把`area`属性直接实现为存储型属性。这两个类都遵循`haxArea`协议。 + + +Here’s a class called Animal, which does not conform to the HasArea protocol: + +这个例子中`Animal`类不遵循`HasArea`协议: + + class Animal { + var legs: Int + init(legs: Int) { self.legs = legs } + } + +The Circle, Country and Animal classes do not have a shared base class. Nonetheless, they are all classes, and so instances of all three types can be used to initialize an array that stores values of type AnyObject: +`Circle`类,`Country`类,`Animal`类并没有一个相同的基类,可以采用`AnyObject`类型的数组来承载他们构造后的实例: + + let objects: AnyObject[] = [ + Circle(radius: 2.0), + Country(area: 243_610), + Animal(legs: 4) + ] +The objects array is initialized with an array literal containing a Circle instance with a radius of 2 units; a Country instance initialized with the surface area of the United Kingdom in square kilometers; and an Animal instance with four legs. + +The objects array can now be iterated, and each object in the array can be checked to see if it conforms to the HasArea protocol: + +objects数组使用数组字面量(array literal)初始化,数组包含一个`radius`为2的`Circle` 实例,aear属性等于英国面积的`Country`实例和一个`legs`属性为4的`Animal`实例。 + +现在objects数组可以被迭代,且可以对迭代出的每一个元素进行检查,看其是否遵循了`HasArea`协议: + + for object in objects { + if let objectWithArea = object as? HasArea { + println("Area is \(objectWithArea.area)") + } else { + println("Something that doesn't have an area") + } + } + // Area is 12.5663708 + // Area is 243610.0 + // Something that doesn't have an area + +Whenever an object in the array conforms to the HasArea protocol, the optional value returned by the as? operator is unwrapped with optional binding into a constant called objectWithArea. The objectWithArea constant is known to be of type HasArea, and so its area property can be accessed and printed in a type-safe way. + +Note that the underlying objects are not changed by the casting process. They continue to be a Circle, a Country and an Animal. However, at the point that they are stored in the objectWithArea constant, they are only known to be of type HasArea, and so only their area property can be accessed. + +迭代出的元素使用可选绑定(optional binding)将其绑定到`objectWithArea`常量上,使用as?操作符判断其是否遵循`HasArea`协议。`objectWithArea`常量是遵循HasArea协议类型的实例,因此其`area`属性是可以被访问和打印的。 + +`objects`数组中元素的类型并不会因为向下转型而改变,它们仍然是`Circle`,`Country`,`Animal`类型。然而,当它们被存储到`objectWithArea`常量后,只会被认为是`HasArea`类型,因此只有`area`属性能够被访问。 +Optional Protocol Requirements + +可选的协议要求 + +You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the @optional keyword as part of the protocol’s definition. + +您可以定义可选的协议要求,这些要求不需要类型实现符合协议。可选要求使用`@optional`关键字,作为协议的定义的一部分。 + +An optional protocol requirement can be called with optional chaining, to account for the possibility that the requirement was not implemented by a type that conforms to the protocol. For information on optional chaining, see Optional Chaining. + +You check for an implementation of an optional requirement by writing a question mark after the name of the requirement when it is called, such as someOptionalMethod?(someArgument). Optional property requirements, and optional method requirements that return a value, will always return an optional value of the appropriate type when they are accessed or called, to reflect the fact that the optional requirement may not have been implemented. + +可选协议要求通过可选链(optional chaining)进行调用,为一个类型实现不遵循协议预留可能,详细内容可以参考可选链章节。 + +你可以在实现名称后加上?来检查该实现,比如`someOptionalMethod?(someArgument)`。可选方法需求和可选属性要求,在调用或访问时都会返回一个可选值(optional value),当可选的要求可能没有被实施时,这个值也会反映出来。 + + + +NOTE + +Optional protocol requirements can only be specified if your protocol is marked with the @objc attribute. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to specify optional requirements. + +注意: 可选协议要求只能在含有`@objc`前缀的协议中指定。即使你不准备与Objective-c进行交互,当你要指定可选协议需求时,也需要添加这个前缀。 + +Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to specify optional requirements, you will only be able to apply that protocol to class types. + +还要注意,`@objc`协议只适用于类,不能用于结构体或枚举。因此只有协议被应用在类上,才能通过`@objc`指定可选协议要求。 + +The following example defines an integer-counting class called Counter, which uses an external data source to provide its increment amount. This data source is defined by the CounterDataSource protocol, which has two optional requirements: + +下面的例子定义了一个计数器类`Counter`,它使用一个外部数据源提供其增量。这个数据源是`CounterDataSource`定义的协议,它有两个可选的要求: + + @objc protocol CounterDataSource { + @optional func incrementForCount(count: Int) -> Int + @optional var fixedIncrement: Int { get } + } + +The CounterDataSource protocol defines an optional method requirement called incrementForCount and an optional property requirement called fixedIncrement. These requirements define two different ways for data sources to provide an appropriate increment amount for a Counter instance. + +`CounterDataSource`协议定义了一个可选的方法要求incrementForCount和一个可选的属性要求`fixedIncrement` 。这些要求定义了两种不同的方式的数据源,来给Counter实例提供一个适当的增量。 + + +NOTE +注意 +Strictly speaking, you can write a custom class that conforms to CounterDataSource without implementing either protocol requirement. They are both optional, after all. Although technically allowed, this wouldn’t make for a very good data source. + +严格来讲,你可以实现一个类来适配协议`CounterDataSource`,而不去实现协议中的两个要求,因为`CounterDataSource`中的属性和方法要求都是可选的。尽管这样写是可以的,但这并不是一个好的数据源的实现。 + +The Counter class, defined below, has an optional dataSource property of type CounterDataSource?: +Counter类含有`CounterDataSource?`类型的可选属性`dataSource`,如下所示: + + @objc class Counter { + var count = 0 + var dataSource: CounterDataSource? + func increment() { + if let amount = dataSource?.incrementForCount?(count) { + count += amount + } else if let amount = dataSource?.fixedIncrement? { + count += amount + } + } + } +The Counter class stores its current value in a variable property called count. The Counter class also defines a method called increment, which increments the count property every time the method is called. + +`Counter`类通过`count`属性来存储当前值,`increment`方法在每次调用后增加`count`的值。 + + +The increment method first tries to retrieve an increment amount by looking for an implementation of the incrementForCount method on its data source. The increment method uses optional chaining to try to call incrementForCount, and passes the current count value as the method’s single argument. + +`increment`方法首先通过可选链调用`incrementForCount`方法内数据源的实现,从而获取增量,并将当前的`count`值作为参数传入方法 + + + +Note two levels of optional chaining at play here. Firstly, it is possible that dataSource may be nil, and so dataSource has a question mark after its name to indicate that incrementForCount should only be called if dataSource is non-nil. Secondly, even if dataSource does exist, there is no guarantee that it implements incrementForCount, because it is an optional requirement. This is why incrementForCount is also written with a question mark after its name. + +注意,这里有两级可选链。首先,`dataSource`可能为`nil`,所以`dataSource`后使用问好,表明只有在`dataSource`不为`nil`时才调用`incrementForCount`方法。其次,即使`dataSource`存在,也不能保证它实现了`incrementForCount`方法,因为这是一个可选的要求。这就是为什么`incrementForCount`的名字后面也跟着一个问号。 + +Because the call to incrementForCount can fail for either of these two reasons, the call returns an optional Int value. This is true even though incrementForCount is defined as returning a non-optional Int value in the definition of CounterDataSource. + +以上原因表明,即使`CounterDataSource`协议中`incrementForCount`方法被定义为返回一个非可选的Int类型的值,在调用`incrementForCount`方法后,也会返回一个Int类型的可选值。 + +After calling incrementForCount, the optional Int that it returns is unwrapped into a constant called amount, using optional binding. If the optional Int does contain a value—that is, if the delegate and method both exist, and the method returned a value—the unwrapped amount is added onto the stored count property, and incrementation is complete. + +当调用完incrementForCount方法后,返回的可选值通过可选绑定,赋值给常量`amont`。 +如果可选值包含值-代表如果代理和方法都存在,方法返回了值-`amount`会赋给存储属性`count`,这个增量过程就完成了。 + +If it is not possible to retrieve a value from the incrementForCount method—either because dataSource is nil, or because the data source does not implement incrementForCount—then the increment method tries to retrieve a value from the data source’s fixedIncrement property instead. The fixedIncrement property is also an optional requirement, and so its name is also written using optional chaining with a question mark on the end, to indicate that the attempt to access the property’s value can fail. As before, the returned value is an optional Int value, even though fixedIncrement is defined as a non-optional Int property as part of the CounterDataSource protocol definition. + +如果不能从incrementForCount方法获取到值 -可能`dataSource`为`nil`,或dataSource没有实现`incrementForCount方法`-increment方法会尝试从数据源中获取fixedIncrement属性的值来代替。`fixedIncrement`属性也是一个可选的要求,所以它也使用可选链,以一个问号结束,表明试图访问属性的值时可以失败。和之前一样,即使`CounterDataSource`协议中,`fixedIncrement`属性被定义为返回一个非可选的Int类型的值,返回值也是一个可选的Int类型的值。 + +Here’s a simple CounterDataSource implementation where the data source returns a constant value of 3 every time it is queried. It does this by implementing the optional fixedIncrement property requirement: + +这里有一个简单的CounterDataSource的实现,通过实现了可选属性fixedIncrement,每次查询都会返回一个常量3。 + + class ThreeSource: CounterDataSource { + let fixedIncrement = 3 + } + +You can use an instance of ThreeSource as the data source for a new Counter instance: + +你可以使用ThreeSource的实例作为Counter实例的数据源: + + var counter = Counter() + counter.dataSource = ThreeSource() + for _ in 1...4 { + counter.increment() + println(counter.count) + } + // 3 + // 6 + // 9 + // 12 + +The code above creates a new Counter instance; sets its data source to be a new ThreeSource instance; and calls the counter’s increment method four times. As expected, the counter’s count property increases by three each time increment is called. + +上面的代码创建了一个新的Counter实例,同时把数据源设置为ThreeSource的实例,之后调用了四次计数器的increment方法。正如我们所期望的,increment方法调用一次,计数器的count属性会增加3。 + +Here’s a more complex data source called TowardsZeroSource, which makes a Counter instance count up or down towards zero from its current count value: +这里有一个更加复杂的数据源例子`TowardsZeroSource`,使计数器实例会从当前计数值向上或向下计数,直到零: + + class TowardsZeroSource: CounterDataSource { + func incrementForCount(count: Int) -> Int { + if count == 0 { + return 0 + } else if count < 0 { + return 1 + } else { + return -1 + } + } + } +The TowardsZeroSource class implements the optional incrementForCount method from the CounterDataSource protocol and uses the count argument value to work out which direction to count in. If count is already zero, the method returns 0 to indicate that no further counting should take place. + +You can use an instance of TowardsZeroSource with the existing Counter instance to count from -4 to zero. Once the counter reaches zero, no more counting takes place: + +`TowardsZeroSource`类实现了`CounterDataSource`协议中可选的`incrementForCount`方法,使用`count`参数值来决定计数方向。如果`count`已经是零,方法返回0,表明不会有进一步计算了。 +   + 你可以在`Counter`实例中使用`TowardsZeroSource`实例,从-4计数至零。一旦计数器达到零就不会在计数了: + + counter.count = -4 + counter.dataSource = TowardsZeroSource() + for _ in 1...5 { + counter.increment() + println(counter.count) + } + // -3 + // -2 + // -1 + // 0 + // 0 diff --git a/src/chapter2/22_Generics.md b/src/chapter2/22_Generics.md index e69de29..66e1a86 100644 --- a/src/chapter2/22_Generics.md +++ b/src/chapter2/22_Generics.md @@ -0,0 +1,687 @@ +#Generics +#泛型 + +Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner. + +泛型代码可以让你写出更灵活、复用性更强的的函数或者类型,这种函数或者类型可以是满足你需求的任意类型或者主题。你可以用一种清晰抽象的方式表达出来,避免重复代码。 + +Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout this Language Guide, even if you didn’t realize it. For example, Swift’s Array and Dictionary types are both generic collections. You can create an array that holds Int values, or an array that holds String values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be. + +泛型是Swift中非常强大的特性之一,而且很多Swift的标准库都是用泛型构建的。事实上,不管你有没有意识到,在学习这本swift语言指南的过程中,你已经在使用泛型了。例如,Swift中的Array和Dictionary类型都是泛型集合。你可以创建一个数组用来储存int类型的数值,也可以储存string类型的数值,事实上在Swift中你可以创建数组来储存任意类型的值。同样地,你也可以创建一个存储任意数值类型的字典类型; + +#The Problem That Generics Solve +#泛型解决的问题 + +Here’s a standard, non-generic function called `swapTwoInts`, which swaps two Int values: + +下面的代码是一个标准的、非泛型函数`swapTwoInts`,用于交换两个int值: + +``` +func swapTwoInts(inout a: Int, inout b: Int) { + let temporaryA = a + a = b + b = temporaryA +} +``` + +This function makes use of in-out parameters to swap the values of a and b, as described in [In-Out Parameters](). + +这个函数使用in-out参数交换a,b值,具体的可以参考[In-Out Parameters]()。 + +The `swapTwoInts` function swaps the original value of b into a, and the original value of a into b. You can call this function to swap the values in two Int variables: + +`swapTwoInts`函数把b的原始值赋值给a,a的原始值给b。你可以调用这个函数去交换两个整形值: + +``` +var someInt = 3 +var anotherInt = 107 +swapTwoInts(&someInt, &anotherInt) +println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") +// prints "someInt is now 107, and anotherInt is now 3 +``` + +The `swapTwoInts` function is useful, but it can only be used with Int values. If you want to swap two String values, or two Double values, you have to write more functions, such as the `swapTwoStrings` and `swapTwoDoubles `functions shown below: + +`swapTwoInts`函数非常有用,但是它只能交换int值。如果想交换两个String、或者是两个Double类型的值,你不得不再写几个函数,比如`swapTwoStrings`,`swapTwoDoublesfunctions`,如下所示: + +``` +func swapTwoStrings(inout a: String, inout b: String) { + let temporaryA = a + a = b + b = temporaryA +} +func swapTwoDoubles(inout a: Double, inout b: Double) { + let temporaryA = a + a = b + b = temporaryA +} +``` + +You may have noticed that the bodies of the `swapTwoInts`, `swapTwoStrings`, and `swapTwoDoubles` functions are identical. The only difference is the type of the values that they accept (Int, String, and Double). + +也许你已经注意到了,这三个函数的内容基本上是相同的,唯一的区别就是传参类型不一样(Int,String,Double)。 + +It would be much more useful, and considerably more flexible, to write a single function that could swap two values of any type. This is the kind of problem that generic code can solve. (A generic version of these functions is defined below.) + +如果可以只编写一个函数就能提供交换两个任意类型的值的功能的话,这种方式将更加有用和灵活。而这正是泛型所要解决的问题。(下面是使用泛型重新编写的函数)。 + + +> NOTE + +> In all three functions, it is important that the types of a and b are defined to be the same as each other. If a and b were not of the same type, it would not be possible to swap their values. Swift is a type-safe language, and does not allow (for example) a variable of type String and a variable of type Double to swap values with each other. Attempting to do so would be reported as a compile-time error. + + +> 注意 + +> 上面的三个函数中,a和b的类型必须是一样的。如果不一样,就不能交换两者的值。Swift是类型安全的语言,不允许一个`String`和`Double`类型变量互相交换值。如果试图进行这样的操作,会出现编译错误。 + +#Generic Functions +#泛型函数 + +Generic functions can work with any type. Here’s a generic version of the `swapTwoInts` function from above, called `swapTwoValues`: + +泛型函数可以和任何类型一起使用。下面就是上面的`swapTwoInts`的泛型版本,`swapTwoValues`: + +``` +func swapTwoValues(inout a: T, inout b: T) { + let temporaryA = a + a = b + b = temporaryA +} +``` +The body of the `swapTwoValues` function is identical to the body of the `swapTwoInts` function. However, the first line of `swapTwoValues` is slightly different from `swapTwoInts`. Here’s how the first lines compare: + +`swapTwoValues`和`swapTwoInts`的函数主体内容是一样的,仅仅在第一行有一些细微的区别,下面是两者的比较: + +``` +func swapTwoInts(inout a: Int, inout b: Int) +func swapTwoValues(inout a: T, inout b: T) +``` + +The generic version of the function uses a placeholder type name (called T, in this case) instead of an actual type name (such as Int, String, or Double). The placeholder type name doesn’t say anything about what T must be, but it does say that both a and b must be of the same type T, whatever T represents. The actual type to use in place of T will be determined each time the `swapTwoValues` function is called. + +泛型版本使用了占位符作为类型的名称(在这里使用T作为占位符)来代替实际的类型名称(比如Int,String,或者Double)。占位符类型定义限制a和b必须是相同类型,而不限制到底是什么类型,T可以代表任意类型。只有当`swapTwoValues`真正调用的时候,才会决定T代表什么类型。 + +The other difference is that the generic function’s name (swapTwoValues) is followed by the placeholder type name (T) inside angle brackets (). The brackets tell Swift that T is a placeholder type name within the`swapTwoValues` function definition. Because T is a placeholder, Swift does not look for an actual type called T. + +另一个区别是泛型函数后边跟着的类型(T)是放到尖括号里边的()。尖括号告诉Swift,T是`swapTwoValues`的函数定义中的一个占位符类型。由于T是一个占位符,因此swift不会去查找是否存在命名为T的类型。 + +The `swapTwoValues` function can now be called in the same way as `swapTwoInts`, except that it can be passed two values of any type, as long as both of those values are of the same type as each other. Each time `swapTwoValues` is called, the type to use for T is inferred from the types of values passed to the function. + +`swapTwoValues`函数现在可以和`swapTwoInts`一样被调用。除此之外,`swapTwoValues`可以接收两个任意类型的参数,只要这两个参数的类型相同。每次`swapTwoValues`被调用,T所代表的类型会根据实际传入的参数来确定。 + +In the two examples below, T is inferred to be Int and String respectively: + +在下面的例子中,T分别代表Int和String类型。 + +``` +var someInt = 3 +var anotherInt = 107 +swapTwoValues(&someInt, &anotherInt) +// someInt is now 107, and anotherInt is now + +var someString = "hello" +var anotherString = "world" +swapTwoValues(&someString, &anotherString) +// someString is now "world", and anotherString is now "hello" +``` + +> NOTE + +> The swapTwoValues function defined above is inspired by a generic function called swap, which is part of the Swift standard library, and is automatically made available for you to use in your apps. If you need the behavior of the swapTwoValues function in your own code, you can use Swift’s existing swap function rather than providing your own implementation. + +> 注意 + +> 上面定义的`swapTwoValues`函数是受到一个泛型函数`swap`的启发而实现的,这个函数被包含在Swift的标准库中,你可以直接在你的应用中使用它。如果在你的代码中需要一个和`swapTwoValues`类似的功能,可以直接使用Swift中已存在的`swap`方法,而不需要自己再实现一遍。 + + +#Type Parameters +#类型参数 + +In the swapTwoValues example above, the placeholder type T is an example of a type parameter. Type parameters specify and name a placeholder type, and are written immediately after the function’s name, between a pair of matching angle brackets (such as ). + +在上面的`swapTwoValues`例子中,占位符T是一个关于使用类型参数的例子。类型参数通过给定一个占位符类型的方式,放置于紧跟在函数名称之后的尖括号内(例如)。 + +Once specified, a type parameter can be used to define the type of a function’s parameters (such as the a and b parameters of the swapTwoValues function); or as the function’s return type; or as a type annotation within the body of the function. In each case, the placeholder type represented by the type parameter is replaced with an actual type whenever the function is called. (In the swapTwoValues example above, T was replaced with Int the first time the function was called, and was replaced with String the second time it was called.) + +一旦被指定,参数类型可以用来定义函数接收的参数(例如`swapTwoValues`函数中的a和b参数);或者作为函数的返回值类型;或者在函数体中作为类型注释。在每一种场景子中,被类型参数代表的占位符类型,在函数被调用时,会被真实的参数类型所代替。(在`swapTwoValues`的例子中,当函数被第一次调用时,T会被Int代替,第二次调用中T会被String代替。) + +You can provide more than one type parameter by writing multiple type parameter names within the angle brackets, separated by commas. + +你可以在尖括号中通过逗号分隔的方式指定多个类型参数名称,为函数提供多个类型参数。 +‌ +#Naming Type Parameters +#类型参数命名 + +In simple cases where a generic function or generic type needs to refer to a single placeholder type (such as the swapTwoValues generic function above, or a generic collection that stores a single type, such as Array), it is traditional to use the single-character name T for the type parameter. However, you are can use any valid identifier as the type parameter name. + +在一般的情况下,如果泛型函数或者泛型类型需要指定一个占位符(像上面的`swapTwoValues`泛型函数或者只包含单个类型的泛型集合,比如数组),通常会用一个字符T来代表参数类型。不过,你也可以用任意有效的标识符来表示参数类型。 + +If you are defining more complex generic functions, or generic types with multiple parameters, it can be useful to provide more descriptive type parameter names. For example, Swift’s Dictionary type has two type parameters—one for its keys and one for its values. If you were writing Dictionary yourself, you might name these two type parameters KeyType and ValueType to remind you of their purpose as you use them within your generic code. + +如果你正在定义更加复杂的泛型函数或者泛型类型,并需要多个参数,那么使用更具有描述性的类型参数名称就很有必要了。比如Swift中的字典类型就有两个参数,其中一个作为键,一个作为值。如果由你来实现字典类型,你可以为这两个类型分别取名为`KeyType`和`ValueType`,从而提醒你这两个参数在泛型代码中的作用。 + +> NOTE + +> Always give type parameters UpperCamelCase names (such as T and KeyType) to indicate that they are a placeholder for a type, not a value. + +> 注意 + +> 请始终使用大写字母开头的驼峰命名法(例如T和KeyType)来给类型参数命名,以表明它们是类型的占位符,而非类型值。 + +#Generic Types +#泛型类型 + +In addition to generic functions, Swift enables you to define your own generic types. These are custom classes, structures, and enumerations that can work with any type, in a similar way to Array and Dictionary. + +除了泛型函数,Swift可以让你定义自己的泛型类型。这些类型可以是自定义的类、结构和枚举,他们和数组以及字典一样,可以和其他任意类型一起工作。 + +This section shows you how to write a generic collection type called Stack. A stack is an ordered set of values, similar to an array, but with a more restricted set of operations than Swift’s Array type. An array allows new items to be inserted and removed at any location in the array. A stack, however, allows new items to be appended only to the end of the collection (known as pushing a new value on to the stack). Similarly, a stack allows items to be removed only from the end of the collection (known as popping a value off the stack). + +本节将展示如何编写堆栈这样的泛型集合类型。堆栈是一个具有特定顺序的数值的集合,和数组类似,但是和数组相比,堆栈在操作上有更多的限制。数组允许元素可以从任何位置插入或者删除。而堆栈只允许添加新元素到集合的顶部(叫做push一个新值到堆栈)。同样的,堆栈只允许从集合的顶部删除一个元素(叫做从堆栈pop出一个值)。 + +> NOTE + +> The concept of a stack is used by the UINavigationController class to model the view controllers in its navigation hierarchy. You call the UINavigationController class pushViewController:animated: method to add (or push) a view controller on to the navigation stack, and its popViewControllerAnimated: method to remove (or pop) a view controller from the navigation stack. A stack is a useful collection model whenever you need a strict “last in, first out” approach to managing a collection. + +> 注意 + +> 堆栈的概念被用在`UINavigationController`类中作为视图控制器的导航结构的数据模型。你可以通过调用`UINavigationController`的`pushViewController:animated: `方法来添加一个视图到堆栈中,调用` popViewControllerAnimated:`方法从堆栈中删除一个试图。当你需要一个严格的“先进先出”方式来管理集合时,堆栈是非常有用的。 + + +The illustration below shows the push / pop behavior for a stack: + +下面的图表展示了堆栈的进入、推出操作: + +![stack](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/stackPushPop_2x.png) + + + 1. There are currently three values on the stack. + 2. A fourth value is “pushed” on to the top of the stack. + 3. The stack now holds four values, with the most recent one at the top. + 4. The top item in the stack is removed, or “popped”. + 5. After popping a value, the stack once again holds three values. + + 1. 当前堆栈里有3个值。 + 2. 第四个值从堆栈的顶部被“push”进去。 + 3. 现在堆栈中有4个值,其中最新的值在最顶部。 + 4. 堆栈最顶部的值被移除,或者叫“popped”。 + 5. 当推出一个值后,堆栈重新变成了三个值。 + +Here’s how to write a non-generic version of a “stack”, in this case for a stack of Int values + +下面展示如何编写一个非泛型版本的堆栈,这个例子中的堆栈是Int类型: + +``` +struct IntStack { + var items = Int[]() + mutating func push(item: Int) { + items.append(item) + } + mutating func pop() -> Int { + return items.removeLast() + } +} +``` +This structure uses an Array property called items to store the values in the stack. Stack provides two methods, push and pop, to push and pop values on and off the stack. These methods are marked as mutating, because they need to modify (or mutate) the structure’s items array. + +这个结构中使用数组属性items来存储堆栈中的数据。堆栈提供了`push` 和`pop`两个方法,用于推入数据到堆栈,或者从堆栈推出数据。这些方法被标记为`mutating`,因为他们需要修改结构中的items数组的值。 + +The IntStack type shown above can only be +“used with Int values, however. It would be much more useful to define a generic Stack class, that can manage a stack of any type of value. + +上面的这个`IntStack`类型只能用于Int值。如果定义一个可以管理任何数据类型的泛型类型的堆栈类型,可以管理堆栈中的任意类型,那将非常有用。 + +Here’s a generic version of the same code: + +下面是堆栈的泛型版本: + +``` +struct Stack { + var items = T[]() + mutating func push(item: T) { + items.append(item) + } + mutating func pop() -> T { + return items.removeLast() + } +} +``` +Note how the generic version of Stack is essentially the same as the non-generic version, but with a placeholder type parameter called T instead of an actual type of Int. This “type parameter is written within a pair of angle brackets () immediately after the structure’s name. + +可以注意到,除了使用T作为占位符类型参数来代替真实int类型之外,泛型版本的堆栈结构和上面非泛型版本基本上是一样的。这个类型参数直接跟在结构的名称后并被一对尖括号包裹(). + +T defines a placeholder name for “some type T” to be provided later on. This future type can be referred to as “T” anywhere within the structure’s definition. In this case, T is used as a placeholder in three places: + +其中T作为一个占位符名称来代替后续实际会给定的“某种类型T”。这种将来类型可以在结构体定义中的任何地方使用`T`来表示。在这个例子中,`T`在如下三个位置作为占位符: + + 1. To create a property called items, which is initialized with an empty array of values of type T + 2. To specify that the push method has a single parameter called item, which must be of type T + 3. To specify that the value returned by the pop method will be a value of type T + + 1. 创建成员变量`items`,并将它初始化为包含类型T的空数组 + 2. 指定`push`方法有一个参数item,类型为T类型 + 3. 指定`pop`方法返回结果类型为T + +You create instances of Stack in a similar way to Array and Dictionary, by writing the actual type to be used for this specific stack within angle brackets after the type name when creating a new instance with initializer syntax: + +像创建Array和Dictionary一样,使用构造函数的语法来实例化Stack时,在类型名后边尖括号中写出实际用到的类型: + +``` +var stackOfStrings = Stack() +stackOfStrings.push("uno") +stackOfStrings.push("dos") +stackOfStrings.push("tres") +stackOfStrings.push("cuatro") +// the stack now contains 4 strings +``` + +Here’s how stackOfStrings looks after pushing these four values on to the stack: + +下面展示`stackOfStrings`如何把四个值push进栈: + +![进栈](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/stackPushedFourStrings_2x.png) + +Popping a value from the stack returns and removes the top value, "cuatro": + +下图是如何从栈中pop一个值的过程,移除栈最顶上结果,“cuatro”,并返回其值: + +``` +let fromTheTop = stackOfStrings.pop() +// fromTheTop is equal to "cuatro", and the stack now contains 3 strings +``` + +Here’s how the stack looks after popping its top value: + +下面是推出最顶层数据之后的堆栈效果: + +![移除堆栈效果](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/stackPoppedOneString_2x.png) + +Because it is a generic type, Stack can be used to create a stack of any valid type in Swift, in a similar manner to Array and Dictionary. + +由于`Stack`是泛型类型,所以在Swift中可以用于创建任何合法类型的栈,方式同`Array`和`Dictionary`。 + +#Type Constraints +#类型约束 + +The swapTwoValues function and the Stack type can work with any type. However, it is sometimes useful to enforce certain type constraints on the types that can be used with generic functions and generic types. Type constraints specify that a type parameter must inherit from a specific class, or conform to a particular protocol or protocol composition. + +`swapTwoValues`函数和`Stack`类型可以和任何类型一起使用,不过,有的时候对泛型函数和泛型类型中使用的类型进行相应的约束是非常有用的。类型约束指定参数类型必须继承一个指定的类或者遵循一个特定的协议或协议组合。 + +For example, Swift’s Dictionary type places a limitation on the types that can be used as keys for a dictionary. As described in Dictionaries, the type of a dictionary’s keys must be hashable. That is, it must provide a way to make itself uniquely representable. Dictionary needs its keys to be hashable so that it can check whether it already contains a value for a particular key. Without this requirement, Dictionary could not tell whether it should insert or replace a value for a particular key, nor would it be able to find a value for a given key that is already in the dictionary. + +例如,Swift中的`Dictionary`对键值做了约束。在字典的描述中,字典的键值类型必须是可散列的,就是说必须有一种方法能保证其每个键值都是唯一的。`Dictionary`对于键值可散列的要求,是为了便于检查其是否已经包含某个特定键的值。如果没有这个约束,就不能判断是否可以插入或者替换某个特定键的值,也不能查找到某个已经存储在字典中的特定值。 + +This requirement is enforced by a type constraint on the key type for Dictionary, which specifies that the key type must conform to the Hashable protocol, a special protocol defined in the Swift standard library. All of Swift’s basic types (such as String, Int, Double, and Bool) are hashable by default. + +这个需求以一个类型约束的形式被添加在Dictionary的键类型上,以此表明键的类型必须遵循Hashable协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如String,Int,Double和 Bool)默认都是可散列的。 + +You can define your own type constraints when creating custom generic types, and these constraints provide much of the power of generic programming. Abstract concepts like Hashable characterize types in terms of their conceptual characteristics, rather than their explicit type. + +你可以在创建自定义泛型类型时,定义你自己的类型约束,这些类型约束为你提供了很多泛型编程的能力。像可散列这样的抽象概念,从概念层面为类型添加特征,而不是直接指定具体的类型。 + +##Type Constraint Syntax + +##类型约束语法 + +You write type constraints by placing a single class or protocol constraint after a type parameter’s name, separated by a colon, as part of the type parameter list. The basic syntax for type constraints on a generic function is shown below (although the syntax is the same for generic types): + +你可以通过在类型参数名称的后边以冒号分割,加上类型参数约束。基本的泛型函数的类型约束语法如下(同样的,这个语法也适用于泛型类型): + +``` +func someFunction(someT: T, someU: U) { + // function body goes here +} +``` + +The hypothetical function above has two type parameters. The first type parameter, T, has a type constraint that requires T to be a subclass of SomeClass. The second type parameter, U, has a type constraint that requires U to conform to the protocol SomeProtocol. + +上面的函数有两个参数。第一个参数为类型参数为T,并要求T必须是`SomeClass`的子类;第二个参数为参数类型U,且它必须遵循`SomeClass`协议。 + +##Type Constraints in Action +##类型约束实践 + +Here’s a non-generic function called findStringIndex, which is given a String value to find and an array of String values within which to find it. The findStringIndex function returns an optional Int value, which will be the index of the first matching string in the array if it is found, or nil if the string cannot be found: + +现在有一个名为findStringIndex的非泛型函数,它接收一个字符串以及一个字符串数组作为参数,并从这个数组中对这个给定的字符串进行查找。若查找到匹配的字符串,findStringIndex函数返回该字符串在数组中的索引位置(Int),反之返回nil: + +``` +func findStringIndex(array: String[], valueToFind: String) -> Int? { + for (index, value) in enumerate(array) { + if value == valueToFind { + return index + } + } + return nil +} +``` + +The findStringIndex function can be used to find a string value in an array of strings: + +`findStringIndex`可以用于查找数组中指定的String值: + +``` +let strings = ["cat", "dog", "llama", "parakeet", "terrapin"] +if let foundIndex = findStringIndex(strings, "llama") { + println("The index of llama is \(foundIndex)") +} +// prints "The index of llama is 2" +``` + +The principle of finding the index of a value in an array isn’t useful only for strings, however. You can write the same functionality as a generic function called findIndex, by replacing any mention of strings with values of some type T instead. + +如果只是查找数组中的指定字符串用处不大,但是你可以通过使用泛型类型T来代替上面的类型`String`,写出一个具有相同功能的泛型函数`findIndex`。 + +Here’s how you might expect a generic version of findStringIndex, called findIndex, to be written. Note that the return type of this function is still Int?, because the function returns an optional index number, not an optional value from the array. Be warned, though—this function does not compile, for reasons explained after the example: + +下面就是`findStringIndex`的泛型版本`findIndex`。注意到函数的返回结果仍然是Int?,这是因为这个函数返回匹配到的数组索引值,而不是数组中的值。需要提醒的是,这个函数无法通过编译,原因后边会说明: + +``` +func findIndex(array: T[], valueToFind: T) -> Int? { + for (index, value) in enumerate(array) { + if value == valueToFind { + return index + } + } + return nil +} +``` + +This function does not compile as written above. The problem lies with the equality check, “if value == valueToFind”. Not every type in Swift can be compared with the equal to operator (==). If you create your own class or structure to represent a complex data model, for example, then the meaning of “equal to” for that class or structure is not something that Swift can guess for you. Because of this, it is not possible to guarantee that this code will work for every possible type T, and an appropriate error is reported when you try to compile the code. + +这个函数按照上面的写法无法编译。原因是等价检查部分“if value == valueToFind”。 因为不是所有的泛型类型都可以用操作符(==)进行比较。如果你创建了自己的类或者结构来表示一个复杂的数据模型,那么Swift语言没法猜测如何对这个类或者结构进行是否相等的比较。正因为如此,这个代码无法保证可以用于所有的类型T,当你尝试编译这段代码时,编译器会抛出错误指出这个问题。 + +All is not lost, however. The Swift standard library defines a protocol called Equatable, which requires any conforming type to implement the equal to operator (==) and the not equal to operator (!=) to compare any two values of that type. All of Swift’s standard types automatically support the Equatable protocol. + +当然,还是有解决方案。Swift 标准库中定义了一个Equatable协议,该协议要求任何遵循它的类型需要都实现相等符(==)和不等符(!=)来对任何两个该类型的值进行比较。所有的 Swift 标准类型都自动支持Equatable协议。 + +Any type that is Equatable can be used safely with the findIndex function, because it is guaranteed to support the equal to operator. To express this fact, you write a type constraint of Equatable as part of the type parameter’s definition when you define the function: + +任何Equatable类型都可以安全地使用在findIndex函数中,它保证可以支持相等操作。为了说明这个事实,在定义函数时,可以添加一个Equatable类型约束作为类型参数定义的一部分: + +``` +func findIndex(array: T[], valueToFind: T) -> Int? { + for (index, value) in enumerate(array) { + if value == valueToFind { + return index + } + } + return nil +} +``` + +The single type parameter for findIndex is written as T: Equatable, which means “any type T that conforms to the Equatable protocol”. + +`findIndex`的类型参数写成`T: Equatable`,表示“任意实现Equatable协议的类型”。 + +The findIndex function now compiles successfully and can be used with any type that is Equatable, such as Double or String: + +`findIndex`类型现在可以成功通过编译,并且可以适用于任何实现了`Equatable`协议的类型,比如`Double`e 或者`String`: + +``` +let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) +// doubleIndex is an optional Int with no value, because 9.3 is not in the array +let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") +// stringIndex is an optional Int containing a value of 2 +``` + +#Associated Types +#关联类型 + +When defining a protocol, it is sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name (or alias) to a type that is used as part of the protocol. The actual type to use for that associated type is not specified until the protocol is adopted. Associated types are specified with the typealias keyword. + +当定义一个协议时,有时声明一个或多个关联类型作为协议的一部分非常有用。一个关联类型为包含在协议中的某个类型提供了一个占位符名称(或别名)。一个关联类型代表的实际类型只有在这个协议真正被适配时才得到确定。关联类型通过typealias关键字来指定。 + +##Associated Types in Action +##关联类型实践 + +Here’s an example of a protocol called Container, which declares an associated type called ItemType: + +下面是一个协议`Container`,定义了关联类型`ItemType`: + +``` +protocol Container { + typealias ItemType + mutating func append(item: ItemType) + var count: Int { get } + subscript(i: Int) -> ItemType { get } +} +``` + +The Container protocol defines three required capabilities that any container must provide: + +上述协议定义了三个必须支持的兼容协议: + +1. It must be possible to add a new item to the container with an append method. +2. It must be possible to access a count of the items in the container through a count property that returns an Int value. +3. It must be possible to retrieve each item in the container with a subscript that takes an Int index value. + +1. 必须可以通过append方法添加新的元素到容器中。 +2. 必须提供一个count属性方法可以获取容器中的元素数目,以Int值返回。 +3. 必须可以通过Int索引下标检索每个值。 + +This protocol doesn’t specify how the items in the container should be stored or what type they are allowed to be. The protocol only specifies the three bits of functionality that any type must provide in order to be considered a Container. A conforming type can provide additional functionality, as long as it satisfies these three requirements. + +这个协议没有指定容器中成员该如何存储,也没有指定他们的数据类型。协议只是指定了任何遵循Container协议的类型必须具备的三个功能点。只要满足这三个条件,遵循协议的类型也可以添加额外的功能。 + +Any type that conforms to the Container protocol must be able to specify the type of values it stores. Specifically, it must ensure that only items of the right type are added to the container, and it must be clear about the type of the items returned by its subscript. + +任何遵循Container协议的类型必须能指定所存储的数据值类型。必须保证只有正确数据类型可以被存储到Container中,而且通过其下标返回的结果值的数据类型也必须是明确的。 + +To define these requirements, the Container protocol needs a way to refer to the type of the elements that a container will hold, without knowing what that type is for a specific container. The Container protocol needs to specify that any value passed to the append method must have the same type as the container’s element type, and that the value returned by the container’s subscript will be of the same type as the container’s element type. + +为了能定义这些要求,需要有一种方式,能在Container协议不知道某个具体的容器实际会存储的数据类型的前提下,还能引用到那个数据类型。Container协议需要能指明,任何通过append方法添加至容器的值的数据类型和容器中的元素的数据类型相同,并且通过容器下标返回的值的类型和容器中的元素的类型也是相同的。 + +To achieve this, the Container protocol declares an associated type called ItemType, written as typealias ItemType. The protocol does not define what ItemType is an alias for—that information is left for any conforming type to provide. Nonetheless, the ItemType alias provides a way to refer to the type of the items in a Container, and to define a type for use with the append method and subscript, to ensure that the expected behavior of any Container is enforced. + +为了达到这个目的,container协议声明了一个相关类型ItemType,记为`typealias ItemType`。该协议不会定义ItemType是谁的别名,这个信息留给任何遵循协议的类型来提供。尽管如此,ItemType别名提供了一种方法来引用容器中的元素类型,并定义一种使用在append方法和下标中的类型,从而保证任何Container期望的行为都能得到贯彻。 + +Here’s a version of the non-generic IntStack type from earlier, adapted to conform to the Container protocol: + +下面是之前的IntStack结构的非泛型版本,适用于遵循Container协议: + +``` +struct IntStack: Container { + // original IntStack implementation + var items = Int[]() + mutating func push(item: Int) { + items.append(item) + } + mutating func pop() -> Int { + return items.removeLast() + } + // conformance to the Container protocol + typealias ItemType = Int + mutating func append(item: Int) { + self.push(item) + } + var count: Int { + return items.count + } + subscript(i: Int) -> Int { + return items[i] + } +} +``` + +The IntStack type implements all three of the Container protocol’s requirements, and in each case wraps part of the IntStack type’s existing functionality to satisfy these requirements. + +`IntStack`类型实现了`Container`协议的所有要求,对于每一个要求,IntStack类型都通过包装现有的功能来满足。 + +Moreover, IntStack specifies that for this implementation of Container, the appropriate ItemType to use is a type of Int. The definition of typealias ItemType = Int turns the abstract type of ItemType into a concrete type of Int for this implementation of the Container protocol. + +此外, 在`IntStack`对于协议`Container`的实现中,对应的`ItemType`类型是Int。在这次对于Container协议实现中,通过 `typealias ItemType = Int`的定义,将抽象的`ItemType`类型转换为具体的Int类型。 + +Thanks to Swift’s type inference, you don’t actually need to declare a concrete ItemType of Int as part of the definition of IntStack. Because IntStack conforms to all of the requirements of the Container protocol, Swift can infer the appropriate ItemType to use, simply by looking at the type of the append method’s item parameter and the return type of the subscript. Indeed, if you delete the typealias ItemType = Int line from the code above, everything still works, because it is clear what type should be used for ItemType. + +实际上,借助于Swift的类型推理机制,你不用在IntStack定义部分声明具体的Int类型的ItemType。由于IntStack满足Container协议的所有要求,只需通过简单的查找append方法中的item参数类型和下标返回的类型,Swift就可以推断出合适的ItemType来使用。实际上,如果你删除了上述代码中的 typealias ItemType = Int,这段代码仍可以正常运行,因为它清楚的知道ItemType使用的是何种类型。 + +You can also make the generic Stack type conform to the Container protocol: + +你同样可以编写遵循Containner协议的泛型Stack类型: + +``` +struct Stack: Container { + // original Stack implementation + var items = T[]() + mutating func push(item: T) { + items.append(item) + } + mutating func pop() -> T { + return items.removeLast() + } + // conformance to the Container protocol + mutating func append(item: T) { + self.push(item) + } + var count: Int { + return items.count + } + subscript(i: Int) -> T { + return items[i] + } +} +``` + +This time, the placeholder type parameter T is used as the type of the append method’s item parameter and the return type of the subscript. Swift can therefore infer that T is the appropriate type to use as the ItemType for this particular container. + +这个时候,占位符T被用作append方法的item参数类型和下标的返回类型。Swift因此也就可以推断出ItemType的合适类型就是T。 + +#Extending an Existing Type to Specify an Associated Type +#通过扩展已经存在的类型来指定关联类型 + +You can extend an existing type to add conformance to a protocol, as described in Adding Protocol Conformance with an Extension. This includes a protocol with an associated type. + +你可以用[使用扩展来添加协议兼容性](../chapter2/21_Protocols.html)的方法,通过增加遵循的对协议来扩展一个已存在的类型。这些协议也可以包含关联类型。 + +Swift’s Array type already provides an append method, a count property, and a subscript with an Int index to retrieve its elements. These three capabilities match the requirements of the Container protocol. This means that you can extend Array to conform to the Container protocol simply by declaring that Array adopts the protocol. You do this with an empty extension, as described in Declaring Protocol Adoption with an Extension: + +Swift中的数据类型已经提供了`append`方法、`count`属性方法和下标索引方法。这三个功能满足`Container`协议。也就是说你只需简单声明Array适配Container协议即可。你用一个空的扩展实现了这个目的,就像[通过扩展补充协议声明](../chapter2/21_Protocols.html)中描述的一样。 + +``` +extension Array: Container {} +``` + +Array’s existing append method and subscript enable Swift to infer the appropriate type to use for ItemType, just as for the generic Stack type above. After defining this extension, you can use any Array as a Container. + +数组中存在的append方法和下标索引方法使Swift可以推断出ItemType的实际类型,像上面的泛型版`Stack`一样。当定义了这个扩展之后,你可以用任何数组作为容器。 + +#Where Clauses +#Where从句 + +Type constraints, as described in Type Constraints, enable you to define requirements on the type parameters associated with a generic function or type. + +类型约束能让你为泛型函数或者类型中相关的类型参数添加要求。 + +It can also be useful to define requirements for associated types. You do this by defining where clauses as part of a type parameter list. A where clause enables you to require that an associated type conforms to a certain protocol, and/or that certain type parameters and associated types be the same. You write a where clause by placing the where keyword immediately after the list of type parameters, followed by one or more constraints for associated types, and/or one or more equality relationships between types and associated types. + +而为关联类型添加要求也是非常有用的。你可以通过定义where从句作为类型参数的一部分来添加要求。通过where从句能要求一个关联类型遵循一个特定的协议,以及(或)指定某个特定的类型参数和关联类型必须相同。where从句以在类型参数后面添加where关键词开始,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系而结束。 + +The example below defines a generic function called allItemsMatch, which checks to see if two Container instances contain the same items in the same order. The function returns a Boolean value of true if all items match and a value of false if they do not. + +下面的例子定义了一个泛型函数`allItemsMatch`,用于判断两个`Container`实例是否包含相同的元素并具有相同的顺序。如果所有元素满足条件,函数返回true,否则返回false。 + +The two containers to be checked do not have to be the same type of container (although they can be), but they do have to hold the same type of items. This requirement is expressed through a combination of type constraints and where clauses: + +需要检查的两个容器,不需要是相同类型的容器(当然也可以是相同的),但是他们的元素必须相同。这个要求通过一个类型约束和where从句相结合来表达: + +``` +func allItemsMatch + (someContainer: C1, anotherContainer: C2) -> Bool { + // check that both containers contain the same number of items + if someContainer.count != anotherContainer.count { + return false + } + // check each pair of items to see if they are equivalent + for i in 0..someContainer.count { + if someContainer[i] != anotherContainer[i] { + return false + } + } + // all items match, so return true + return true +} +``` + +This function takes two arguments called `someContainer` and `anotherContainer`. The `someContainer` argument is of type `C1`, and the `anotherContainer` argument is of type `C2`. Both `C1` and `C2` are placeholder type parameters for two container types to be determined when the function is called. + +这个函数有两个参数分别叫`someContainer`和`anotherContainer`。`someContainer`参数是`C1`类型,`anotherContainer`参数是`C2`类型。`C1`和`C2`都是占位符,真正的参数类型在函数被调用时确定。 + +The function’s type parameter list places the following requirements on the two type parameters: + +这个函数的类型参数列紧随在两个类型参数需求的后面: + +- C1 must conform to the Container protocol (written as C1: Container). +- C2 must also conform to the Container protocol (written as C2: Container). +- The ItemType for C1 must be the same as the ItemType for C2 (written as C1.ItemType == C2.ItemType). +- The ItemType for C1 must conform to the Equatable protocol (written as C1.ItemType: Equatable). + +- C1必须遵循`Container`协议(写法`C1: Container`) +- C2必须遵循`Container`协议(写法`C2: Container`) +- C1的ItemType必须和C2的ItemType相等(写法C1.ItemType == C2.ItemType) +- C1的ItemType必须遵循`Equatable`协议 + + +The third and fourth requirements are defined as part of a where clause, and are written after the where keyword as part of the function’s type parameter list. + +第三和第四个要求在where从句中定义,并作为参数类型列表的一部分放到where关键词后。 + +These requirements mean: + +这几个要求意义是: + +- someContainer is a container of type C1. +- anotherContainer is a container of type C2. +- someContainer and anotherContainer contain the same type of items. +- The items in someContainer can be checked with the not equal operator (!=) to see if they are different from each other. + +- someContainer是C1类型的容器 +- anotherContainer是C2类型的容器 +- someContainer和anotherContainer包含相同类型的元素 +- someContainer中的元素可以通过调用不等于操作(!=)判断他们是否不同 + +The third and fourth requirements combine to mean that the items in anotherContainer can also be checked with the != operator, because they are exactly the same type as the items in someContainer. + +第三和第四个需求结合起来表明`anotherContainer`的元素同样可以调用`!=`操作,因为他们和`someContainer`中的类型是一样的。 + +These requirements enable the allItemsMatch function to compare the two containers, even if they are of a different container type. + +这些要求使得`allItemsMatch`函数可以比较两个容器,即使他们是不同的容器类型。 + +The allItemsMatch function starts by checking that both containers contain the same number of items. If they contain a different number of items, there is no way that they can match, and the function returns false. + +`allItemsMatch`函数会先检查`containers`是否包含相同数目的元素。如果元素数目不同,他们互相也就不相等,结果返回`false`。 + +After making this check, the function iterates over all of the items in someContainer with a for-in loop and the half-closed range operator (..). For each item, the function checks whether the item from someContainer is not equal to the corresponding item in anotherContainer. If the two items are not equal, then the two containers do not match, and the function returns false. + +如果具有相同的元素个数,函数通过`for-in`循环和半闭区间操作(..)来迭代`someContainer`中的所有元素。循环中,检查`someContainer`中的元素是否和`anotherContainer`中的对应元素相同。如果有不相同的,函数返回`false`。 + +If the loop finishes without finding a mismatch, the two match, and the function returns true. + +如果循环过程中没有一个不匹配的,则这两个容器相等,结果返回`true`。 + +Here’s how the `allItemsMatch` function looks in action: + +下面展示`allItemsMatch`函数的实际使用: + +``` +var stackOfStrings = Stack() +stackOfStrings.push("uno") +stackOfStrings.push("dos") +stackOfStrings.push("tres") + +var arrayOfStrings = ["uno", "dos", "tres"] + +if allItemsMatch(stackOfStrings, arrayOfStrings) { + println("All items match.") +} else { + println("Not all items match.") +} + +// prints "All items match." + +``` + +The example above creates a Stack instance to store String values, and pushes three strings onto the stack. The example also creates an Array instance initialized with an array literal containing the same three strings as the stack. Even though the stack and the array are of a different type, they both conform to the Container protocol, and both contain the same type of values. You can therefore call the allItemsMatch function with these two containers as its arguments. In the example above, the allItemsMatch function correctly reports that all of the items in the two containers match. + +上面的例子中创建了一个`Stack`实例存储字符串值,然后`push`三个字符串到堆栈中。同时又创建了一个数组,并在初始化时加入三个和堆栈中的元素相同的字符串。尽管堆栈和数组数据类型不同,但是他们都遵循Container协议,并且包含相同数据类型,相同的数据值。所以你可以调用`allItemsMatch`去比较两者。在上面的例子中,`allItemsMatch`函数正确地报告了两者包含的所有元素相匹配。 diff --git a/src/chapter2/23_Advanced_Operators.md b/src/chapter2/23_Advanced_Operators.md index e69de29..e57aaf6 100644 --- a/src/chapter2/23_Advanced_Operators.md +++ b/src/chapter2/23_Advanced_Operators.md @@ -0,0 +1,524 @@ +# 高级操作符 + +除了《基础操作符》里讲到的操作符,Swift还提供了一些高级操作符,用以完成更复杂的数值运算。比如位运算和移位操作符,其语法同C和Objective-C类似。 + +> From 周源: +移位: 我读的时候, 感觉位移更顺口一点 + +和C语言的算术操作符不同,Swift默认不支持溢出运算。数值溢出会被捕获并报错。但是,Swift提供了另一套支持溢出运算的操作符,比如可溢出加操作符(&+),可溢出操作符都以&作为前缀。 + +> From 周源: +&+ -> (&+) +So +& -> (&)? + +在自定义结构体、类或者枚举类型中,可以重载Swift操作符。通过操作符重载,可以简单地实现操作符的重定义。 + +> From 周源: +我理解的 "简单的实现" 有二义性, 可表示 实现简单 和 简单实现, +这里作者的意思应该是实现简单, 我觉得 方便的实现 也可以表达作者的意思, 你怎么看? + +Swift允许用户自定义操作符,并且可定制这些操作符的优先级和结合性。 + +> From 周源: +这一段原文较长, 省略了部分翻译, 按照会长的意思...... + + +## 位操作符 + +位操作符可以处理数据结构中的比特位,通常在图像处理和设备驱动等底层开发程序中使用。通过位操作符,还可以有效地处理外部数据源,比如使用自定义协议进行通信时用来编解码数据。 + +Swift支持C语言里所有的位操作符,如下所述: + +### 按位非 + +按位非操作符(~)对操作数每一位取反: + +![按位非](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseNOT_2x.png) + +按位非是前置操作符,紧置于操作数之前,不带空格: + +``` +let initialBits: UInt8 = 0b00001111 +let invertedBits = ~initialBits // 等于 11110000 +``` + +`UInt8` 是8位无符号整型,可以存储0-255之间的任意数。这个例子先初始化了一个`UInt8` 整型变量`initialBits`,二进制值为`00001111`,前四位为0,后四位为1,换算成十进制等于15。 + + +接着将这个变量`initialBits`进行按位非操作得到常量`invertedBits`,0变成1,1变成0,得到的二进制值为`11110000`,换算成十进制等于240。 + +### 按位与 + +按位与操作符(&)有两个操作数。按位与操作就是将两个操作数的每一位对齐,当对应位都是1时返回1,其他情况都返回0。 + +![按位与](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseAND_2x.png) + + +以下代码,`firstSixBits` 和`lastSixBits` 中间4个位都等于1,将它们按位与操作后得到二进制数`00111100`,换算成十进制为60: + +``` +let firstSixBits: UInt8 = 0b11111100 +let lastSixBits: UInt8 = 0b00111111 +let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 +``` + +### 按位或 + +按位或操作符(|)也有两个操作数。按位或操作就是将两个操作数的每一位对齐,当对应位有一个是1时就返回1,而只有两个位都是0的情况才返回0。 + +![按位或](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseOR_2x.png) + + +以下代码,`someBits` 和`moreBits` 在不同位上有1,将它们按位或操作后得到二进制数`11111110`,换算成十进制为254: + +``` +let someBits: UInt8 = 0b10110010 +let moreBits: UInt8 = 0b01011110 +let combinedbits = someBits | moreBits // 等于 11111110 +``` + +### 按位异或 + +按位异或操作符(^)比较两个操作数的对应位,当两个位不同时返回1,相同时返回0。 + +![按位异或](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseXOR_2x.png) + + +以下代码,`firstBits` 和`otherBits` 对应位相同的情况返回0,不同的情况返回1: + +``` +let firstBits: UInt8 = 0b00010100 +let otherBits: UInt8 = 0b00000101 +let outputBits = firstBits ^ otherBits // 等于 00010001 +``` + +### 按位左移、右移操作符 + +左移操作符(<<)和右移操作符(>>)将操作数的所有位向左或向右移动指定的位数。 + +按位左移和右移的效果等同于将操作数乘以或除以2的倍数。向左移动一位相当于将操作数乘以2,向右移动一位相当于将操作数除以2。 + + +#### 无符号移位操作 + +> From 周源: +移位 -> 位移? +无符号移位的规则如下: + +1. 已有的位向左或向右移动指定的位数。 +2. 舍弃超出边界的位。. +3. 移动后产生的空位用0填充。 + +这种方法称为逻辑移位。 + +下图展示了`11111111 << 1`(11111111 左移一位)和`11111111 >> 1`(11111111 右移一位)的结果。蓝色数字表示被移动位,灰色表示被丢弃位,空位用橙色的0填充。 + +![无符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftUnsigned_2x.png) + +Swift移位操作代码: + +``` +let shiftBits: UInt8 = 4 // 即二进制的00000100 +shiftBits << 1 // 00001000 +shiftBits << 2 // 00010000 +shiftBits << 5 // 10000000 +shiftBits << 6 // 00000000 +shiftBits >> 2 // 00000001 +``` + +移位操作可以对其他数据类型实现编码和解码: + +``` +let pink: UInt32 = 0xCC6699 +let redComponent = (pink & 0xFF0000) >> 16 // redComponent是0xCC, 即204 +let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent是0x66, 即102 +let blueComponent = pink & 0x0000FF // blueComponent是0x99, 即153 +``` + +这个例子中用一个`UInt32` 常量`pink` 来存储css中粉色的颜色值。CSS中颜色`#CC6699`在Swift用十六进制`0xCC6699`来表示。这个颜色值经过按位与(&)和按位右移操作后可分解出它的红色成分(CC)、绿色成分(66)和蓝色成分(99)。 + +对`0xCC6699` 和`0xFF0000`进行按位与操作就可以得到红色成分。`0xFF0000` 里的0类似于遮罩,将`0xCC6699`的第二和第三字节过滤掉后返回`0xCC0000` 。 + +然后,将`0xCC0000` 向右移动16位。因为十六进制中每两个字符占8个比特位,所以移动16位的结果是把`0xCC0000` 变成`0x0000CC`,等同于0xCC,换算成十进制是204。 + +同理,对`0xCC6699` 和`0x00FF00`进行按位与操作可以得到绿色成分。将结果值`0x006600`再向右移动8位得到0x66,换算成十进制是102。 + +最后,对`0xCC6699` 和`0x0000FF`进行按位与操作可以得到蓝色成分。结果值`0x000099`不需要再做移位操作,因为`0x000099` 等价于0x99,换算成十进制是153。 + +#### 有符号移位操作 + +有符号的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(下面举的例子虽然都是8位的,但原理是通用的。) + +有符号整型的第一位为符号位,0代表正数,1代表负数,其余的为数值位。有符号和无符号正整数的存储结构是相同的,比如数值4的二进制结构图: + +![有符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedFour_2x.png) + +符号位为0,代表正数,其余7个数值位用二进制表示十进制的4。 + +负数的存储不太一样,它存储的是2的n次方减去它的绝对值,n为数值位的位数。比如一个8位的数有7个数值位,所以是2的7次方,即128。我们来看下数值-4的二进制结构图: + +![有符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFour_2x.png) + +这里符号位为1,代表负数,其余7个数值位的值是124(即128-4): + +![有符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFourValue_2x.png) + +负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。 + +> From 周源: +The encoding for negative numbers is known as a two’s complement representation. +负数的编码方式称为二进制补码表示 -> 负数的编码方式用二进制补码表示? + +首先,对全部8个比特位(包括符号位)做标准的二进制加法就可以完成-1 加 -4 的操作,加法过程中丢弃超出的比特位。 + +![有符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedAddition_2x.png) + +第二,使用二进制补码表示方式,我们可以和正数一样对负数进行按位左移或右移,同样也是左移1位时乘于2,右移1位时除于2。但是,对有符号整型的右移有一个特别的要求: + +> From 周源: +使用二进制补码表示方式 -> 使用二进制补码? + ++ 有符号和无符号整型按位右移时规则相同,但有符号整型移位后出现的空位使用符号位来填充,而不是0。 + +![有符号移位操作](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png) + +这就确保了按位右移后,有符号整型的符号不会发生变化。这称为算术移位。 + +因为正数和负数特殊的存储方式,向右移位都会使它们更接近于0。移位过程中保持符号位不变,所以负数向右移位时值虽然接近于0但始终是负数。 + +## 溢出操作符 + +当给整型常量或变量赋值溢出时,Swift默认会报错,这就保证了操作过大或过小数据时的安全性。 + +举个例子,`Int16`整型能表示-32768 到 32767之间任意的有符号整数,但如果给它 赋值超出该范围则会导致错误: + +``` +var potentialOverflow = Int16.max +// potentialOverflow 等于 32767, 即Int16能表示的最大值 +potentialOverflow += 1 +// 噢,出错了 +``` + +值溢出时提供错误处理机制可以使编程更灵活。 + +然而,需要判断溢出条件时,你可以采用溢出运算,而不是触发错误。Swfit为整型计算提供了5个以&符号开头的溢出操作符。 + ++ 溢出加法(&+) ++ 溢出减法(&-) ++ 溢出乘法(&*) ++ 溢出除法(&/) ++ 溢出取余(&%) + +### 上溢出 + +下面的例子展示了溢出加法(&+)的用法: + +``` +var willOverflow = UInt8.max +// willOverflow 等于 255, 即UInt8 能表示的最大值 +willOverflow = willOverflow &+ 1 +// 现在willOverflow 等于 0 +``` + +`willOverflow` 等于`UInt8` 所能表示的最大值255(二进制`11111111`),使用溢出加法&+加1,如下图所示因为上溢出`UInt8` 无法表示出这个新值了。溢出后,有效位为`00000000`,也就是0。 + +![上溢出](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowAddition_2x.png) + + +### 下溢出 + +数值也有可能因为太小而越界。举个例子: + +`UInt8`能表示的最小值是0(二进制为`00000000`)。对`00000000`使用溢出减法&-减1,就会得到二进制数`11111111`,即十进制的255。 + +![下溢出](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowUnsignedSubtraction_2x.png) + +代码如下: + +``` +var willUnderflow = UInt8.min +// willUnderflow 等于 0, 即UInt8能表示的最小值 +willUnderflow = willUnderflow &- 1 +// 现在willUnderflow 等于255 +``` + +有符号整型也有类似的下溢出,它所有的减法都是对包括符号位在内的二进制数进行二进制减法,这在 "按位左移、右移操作符" 一节提到过。`Int8` 能表示的最小整数是-128,即二进制的`10000000`。用溢出减法减去1后,变成了`01111111`,即`Int8` 能表示的最大整数127。 + +![下溢出](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowSignedSubtraction_2x.png) + +代码如下: + +``` +var signedUnderflow = Int8.min +// signedUnderflow 等于 -128, 即Int8 能表示的最小值 +signedUnderflow = signedUnderflow &- 1 +// 现在signedUnderflow 等于 127 +``` + +有符号和无符号整型的上溢出总是从最大有效值变成最小值,下溢出总是从最小有效值变成最大值。 + +### 除零溢出 + +将一个数除以0或者对0取余都会导致错误: + +``` +let x = 1 +let y = x / 0 +``` + +但是使用可溢出版本的操作符&/和&%会返回0值。 + +``` +let x = 1 +let y = x &/ 0 +// y 等于 0 +``` + +## 优先级和结合性 + +操作符的优先级有高低之分,高优先级的操作符会先被计算。 + +结合性规定了具有相同优先级的操作符如何分组——向左还是向右。意思就是,到底是和左边的表达式结合,还是和右边的表达式结合。 + +在复合表达式中,操作符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为4? + +``` +2 + 3 * 4 % 5 +// 结果等于 4 +``` + +如果严格地从左到右计算,计算过程会是这样: + ++ 2 + 3 = 5; ++ t 5 * 4 = 20; ++ 20 %5 = 0 + +但是正确答案是4而不是0。优先级高的操作符要先计算,在Swift和C语言中,都是先乘除后加减的。所以,执行完乘法和取余运算才能执行加法运算。 + +乘法和取余拥有相同的优先级,在运算过程中,我们还需要考虑结合性。乘法和取余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始: + +``` +2 + ((3 * 4) % 5) +``` + +(3 * 4)等于 12,相当于 + +``` +2 + (12 % 5) +``` + +(12 % 5) 等于 2,相当于 + +``` +2 + 2 +``` + +最后计算结果为 4。 + +Swift操作符的优先级和结合性的完整规则,请看表达式。 + +> 注意: +> +> Swift操作符的优先级和结合性的规则跟C系语言不太一样,相对于C语言和Objective-C更加简单且保守。所以在移植已有代码到Swift时,注意确认操作数的计算顺序。 + +## 操作符函数 + +类和结构体重新自定义已有操作符的功能,这称为操作符重载。 + +下面的例子展示了一个自定义结构体的加法运算。加操作符是一个二元操作符,因为它有两个操作数,而且是中置操作符,必须出现在两个操作数之间。 + +例子中定义了一个名为`Vector2D` 的结构体,表示二维坐标向量`(x, y)`。随后定义了`Vector2D` 实例对象相加的操作符函数。 + +``` +struct Vector2D { + var x = 0.0, y = 0.0 +} +@infix func + (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y + right.y) +} +``` + +该操作符函数定义了一个全局的+函数,参数是两个`Vector2D` 类型的实例,返回值也是一个`Vector2D` 类型。函数声明中,在关键字fun之前用@infix 属性定义一个中置操作符。 + +在这个代码实现中,参数被命名为left和right,代表+左边和右边的两个`Vector2D`对象。函数返回了一个新的`Vector2D`对象,这个对象的x和y分别等于两个参数对象的x和y的和。 + +这个函数是全局的,而不是`Vector2D`结构的成员方法,所以任意两个`Vector2D`对象都可以使用这个中置运算符: + +``` +let vector = Vector2D(x: 3.0, y: 1.0) +let anotherVector = Vector2D(x: 2.0, y: 4.0) +let combinedVector = vector + anotherVector +// combinedVector 是一个Vector2D 实例 ,值为(5.0, 5.0) +``` + +这个例子将向量 `(3.0,1.0)` 和 `(2.0,4.0)` 相加,得到向量 `(5.0,5.0)`,如下图所示: + +![操作符函数](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/vectorAddition_2x.png) + +### 前置和后置操作符 + +上个例子演示了一个二元中置操作符的自定义实现,同样类和结构体也可以重载标准的一元操作符。一元操作符只有一个操作数,在操作数之前为前置操作符(比如-a),在操作数之后为后置操作符(比如i++)。 + +函数声明中,在关键字fun之前用`@ prefix` 属性定义前置操作符,`@postfix` 定义后置操作符。 + +``` +@prefix func - (vector: Vector2D) -> Vector2D { + return Vector2D(x: -vector.x, y: -vector.y) +} +``` + +这段代码实现了`Vector2D`对象的一元减操作符(-a),`@prefix`表明它是前置的。 + +对于数值,一元减操作符可以把正数变负数,把负数变正数。对于`Vector2D`对象,一元减操作符将其x和y都进行一元减运算。 + +``` +let positive = Vector2D(x: 3.0, y: 4.0) +let negative = -positive +// negative 是 Vector2D实例,值为(-3.0, -4.0) +let alsoPositive = -negative +// alsoPositive 也是 Vector2D实例,值为(3.0, 4.0) +``` + +### 复合赋值操作符 + +复合赋值是其他操作符和赋值操作符一起执行的运算。如+=把加运算和赋值运算组合成一个操作。实现一个复合赋值操作符需要使用`@assignment`属性,操作符左边的参数作为函数输入,函数内再修改它的值。 + +下面的例子实现了`Vector2D` 对象的+=操作符: + +``` +@assignment func += (inout left: Vector2D, right: Vector2D) { + left = left + right +} +``` + +加法运算之前定义过了,这里无需重新定义。加赋操作符函数使用已有的加法运算将左值加上右值: + +> From 周源 +> 加赋操作符函数使用已有的加法运算将左值加上右值: -> 加法操作符... ? + +``` +var original = Vector2D(x: 1.0, y: 2.0) +let vectorToAdd = Vector2D(x: 3.0, y: 4.0) +original += vectorToAdd +// 运算后original 等于 (4.0, 6.0) +``` + +可以将 `@assignment` 属性和 `@prefix` 或 `@postfix` 属性组合起来,比如像下面`Vector2D`对象的前置运算符(++a): + +``` +@prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D { + vector += Vector2D(x: 1.0, y: 1.0) + return vector +} +``` + +这个自加操作符函数使用了前面定义过的加赋运算,将自己加上一个值为 (1.0,1.0) 的对象然后将返回值赋给自己。 + +> From 周源 +> 加赋运算 -> 加法运算... ? + +``` +var toIncrement = Vector2D(x: 3.0, y: 4.0) +let afterIncrement = ++toIncrement +// toIncrement 等于(4.0, 5.0) +// afterIncrement 也等于 (4.0, 5.0) +``` + +> 注意: +> +> 默认的赋值符(=)是不可重载的。只有复合赋值符可以重载。条件操作符 a?b:c 也是不可重载的。 + +### 比较操作符 + +自定义的类和结构体默认没有相等(==)和不等(!=)操作符,因为Swift无法知道自定义类型怎样算相等,怎样算不等。 + +定义相等操作符跟定义其他中置操作符类似: + +``` +@infix func == (left: Vector2D, right: Vector2D) -> Bool { + return (left.x == right.x) && (left.y == right.y) +} +@infix func != (left: Vector2D, right: Vector2D) -> Bool { + return !(left == right) +} +``` + +上述代码实现了相等操作符==来判断两个`Vector2D`对象是否相等,相等的概念就是它们有相同的x值和y值。将相等操作符==的结果取反就实现了不等运算符!=。 + +现在我们可以使用这两个操作符来判断两个`Vector2D`对象是否相等。 + +``` +let twoThree = Vector2D(x: 2.0, y: 3.0) +let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) +if twoThree == anotherTwoThree { +println("这两个向量相等") +} +// 输出结果 "这两个向量相等" +``` + +## 自定义操作符 + +除了标准的操作符,你还可以声明一些个性的操作符,但自定义操作符只能使用这些字符`/ = - + * % < >!& | ^ . ~` + +> From 周源: +> 个性的操作符 -> 自定义的操作符 ? + +新的操作符需要在全局域使用`operator`关键字声明,可以声明为前置,中置或后置的。 + +``` +operator prefix +++ {} +``` + +这段代码定义了一个新的前置操作符+++,此前Swift并不存在这个操作符,此处针对`Vector2D` 对象的这个操作符具有个性化的含义。+++被定义为双自增操作符,它使用之前定义的加赋运算将自已加上自己然后返回。 + +> From 周源: +> 个性化的含义 ? + + +``` +@prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D { +vector += vector +return vector +} +``` + +`Vector2D` 的 +++ 和 ++ 很类似, 唯一不同的是前者加自己, 后者是加值为 `(1.0, 1.0)` 的向量。 + +``` +var toBeDoubled = Vector2D(x: 1.0, y: 4.0) +let afterDoubling = +++toBeDoubled +// toBeDoubled 等于 (2.0, 8.0) +// afterDoubling 也等于 (2.0, 8.0) +``` + +### 自定义中置操作符的优先级和结合性 + +可以为自定义的中置操作符指定优先级和结合性。可以回头看看优先级和结合性中解释的,这两个因素是如何影响复合表达式的求值顺序的。 + +结合性有left,right和none三种情况。左结合操作符跟其他优先级相同的左结合操作符写在一起时,会跟左边的操作数结合。同理,右结合操作符会跟右边的操作数结合。而非结合操作符不能跟其他优先级相同的操作符写在一起。 + +结合性默认为none,优先级默认为100。 + +下面的例子定义了一个左结合且优先级为140的中置操作符+-。 + +``` +operator infix +- { associativity left precedence 140 } +func +- (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y - right.y) +} +let firstVector = Vector2D(x: 1.0, y: 2.0) +let secondVector = Vector2D(x: 3.0, y: 4.0) +let plusMinusVector = firstVector +- secondVector +// plusMinusVector 是 Vector2D实例,等于 (4.0, -2.0) +``` + +> From 周源: +> plusMinusVector 是 Vector2D实例 -> plusMinusVector 是 Vector2D的实例 ? + +这个操作符把两个向量的x相加, y相减。因为它实际上属于加减运算,所以让它保持了和加减法一样的结合性和优先级(左结合,优先级为140)。查阅完整的Swift默认优先级和结合性的设置,请移步[表达式](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-XID_655). + + + + + diff --git a/src/chapter3/01_About_the_Language_Reference.md b/src/chapter3/01_About_the_Language_Reference.md index e69de29..7779aa3 100644 --- a/src/chapter3/01_About_the_Language_Reference.md +++ b/src/chapter3/01_About_the_Language_Reference.md @@ -0,0 +1,39 @@ +# 语言参考 + +## 关于语言参考 + +本书的该部分描述了Swift编程语言的正式语法。在此描述语法的目的是为了帮助你更详细的理解该语言,而不是让你直接实现一个解析器或编译器。 + +Swift语言相对较小,因为那些无处不在出现在Swift代码中的很多常见的类型,函数,和运算符实际上已经定义在了Swift的标准库中。虽然这些类型,函数,和运算符不是Swift语言本身的一部分,但它们被广泛应用于该书本部分的探讨和代码示例中。 + +## 如何阅读语法 + +用于描述Swift编程语言正规语法的标记有如下几个约定: + +* 箭头(→)用于标记文法和可以理解为“可包括” + +* 语法范围以斜体文字表示,显示在语法规范的两侧。 + +* 文字和标点由固定宽度的粗体文字显示,并只出现在一个标记文法的右侧. + +* 替代标记文法式由竖线(|)分隔。当替代文法过长而不易阅读时,它们将在新行中变换为多个标记文法规范。 + +* 在一些案例中,常规字体文本将用于描述右侧的标记文法规则。 + +* 可选的句法范围和字面量则以尾部下标opt作为标识。 + +比如,getter-setter 区的语法定义如下: + +> GETTER-SETTER区语法 + +> getter-setter-block → { getter-clause setter-clause opt } |{ setter-clause getter clause} + +该定义表示,一个getter-setter方法块可由一个getter子句后跟一个可选的setter语句,括在大括号中。或者一个setter子句后跟一个getter子句,括在大括号中。上述语法产生式等价于下面的两个替代处已经明确拼写出的产生式。 + +> GETTER-SETTER区语法 + +> getter-setter-block → { getter-clause setter-clause opt } + +> getter-setter-block → { setter-clause getter-clause } + + diff --git a/src/chapter3/02_Lexical_Structure.md b/src/chapter3/02_Lexical_Structure.md index e69de29..dea3c6f 100644 --- a/src/chapter3/02_Lexical_Structure.md +++ b/src/chapter3/02_Lexical_Structure.md @@ -0,0 +1,290 @@ +# 词法结构 + +Swift的词法结构描述了如何在该语言中用字符序列构建合法标记。这些合法标识构成了该语言最底层的基石,并将会被用于描述后续章节的剩余部分。 + +在大多数情况下,在后续指定的语法约束内,标识将从Swift源文件的输入文本中提取尽可能长的子字符串生成。这种方法称为“最长匹配项”,或者“最大适合”。 + +## 空白与注释 + +空白有两个用途:区分源文件中的标识符和帮助确定一个操作符是前缀还是后缀(参见:运算符),否则忽略。以下字符会被认为是空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空(U+0000)。 + +注释会被编译器当作空白处理。单行注释以 // 开始直到该行结束。多行注释由 /\* 开始,以 */ 结束。注释允许嵌套,但注释标记必须相匹配。 + + +## 标识符 + +标识符以大写或小写字母A到Z,下划线(_),基本多语言文种平面 中的非组合Unicode字符或者基本多语言文种平面以外的私有使用区的字符开始。在首字符之后,数字和 Unicode 字符组合也是可以允许的。 + +要使用保留字作为标识符,需要在其前后增加一个反引号(\`)。例如:类不是一个有效的标识符,但`class`是有效的。反引号不会被当做标识符的一部分;`x` 和 x 意义相同。 + +闭包中如果没有明确的参数名称,参数将被隐式命名为 $0、$1、$2... 这些命名在闭包作用域内是有效的标识符。 + + +> 标识符语法 + +> 标识符 → 标识符头 标识符字符 [可选] + +> 标识符 → \` 标识符头 标识符字符 [可选] \` + +> 标识符 → 隐式参数名 + +> 标识符列表 → 标识符 标识符 , 标识符列表 + +> 标识符头 → 大写或小写字母A到Z + +> 标识符头 → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, 或 U+00B7–U+00BA + +> 标识符头 → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, 或 U+00F8–U+00FF + +> 标识符头 → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, 或 U+180F–U+1DBF + +> 标识符头 → U+1E00–U+1FFF + +> 标识符头 → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, 或 U+2060–U+206F + +> 标识符头 → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, 或 U+2776–U+2793 + +> 标识符头 → U+2C00–U+2DFF 或 U+2E80–U+2FFF + +> 标识符头 → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, 或 U+3040–U+D7FF + +> 标识符头 → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, 或 U+FE30–U+FE44 + +> 标识符头 → U+FE47–U+FFFD + +> 标识符头 → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, 或 U+40000–U+4FFFD + +> 标识符头 → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, 或 U+80000–U+8FFFD + +> 标识符头 → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, 或 U+C0000–U+CFFFD + +> 标识符头 → U+D0000–U+DFFFD 或 U+E0000–U+EFFFD + +> 标识符字符 → 数字 0 到 9 + +> 标识符字符 → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, 或 U+FE20–U+FE2F + +> 标识符字符 → 标识符头 + +> 标识符字符 → 标识符字符 标识符字符列表 [可选] + +> 隐式参数名 → $十进制数字 + + +## 关键字 + +下述被保留的关键字不允许用作标识符,除非它们被反引号转义,参见上面的标识符。 + +* 用作声明的关键字:class、deinit、enum、extension、func、imp或 t、init、let、protocol、static、struct、subscript、typealias、var + +* 用作语句的关键字:break、case、continue、default、do、else、fallthrough、if、in、f或 、return、switch、where、while + +* 用作表达和类型的关键字:as、dynamicType、is、new、super、self、Self、Type、__COLUMN__、__FILE__、__FUNCTION__、__LINE__ + +* 特定上下文中被保留的关键字:associativity、didSet、get、infix、inout、left、mutating、none、nonmutating、operat或 、override、postfix、precedence、prefix、right、set、unowned、unowned(safe)、unowned(unsafe)、weak、willSet,这些关键字在特定上下文之外可以被用于标识符。 + +## 字面量 + +字面量在源代码中表示一个整数,浮点数或字符串类型的值。以下是示例: + +> 42 //整形字面量 + +> 3.14159 //浮点型字面量 + +> "Hello, w或 ld!" //字符串型字面量 + + +> 字面量语法 + +> 字面量 → 整型字面量 | 浮点数字面量 | 字符串字面量 + +## 整型字面量 + +整型字面量表示未指定精度的整型值。整型字面量默认为十进制;你可以通过加前缀来改变基数。二进制字面量以 0b 开始,八进制字面量以 0o 开始,十六进制字面量以 0x 开始。 + +十进制字面量包含数字 0 到 9。二进制字面量只包含 0 和 1,八进制字面量包含数字 0 到 7,十六进制字面量包含数字 0 到 9 以及大写或小写的字母 A 到 F。 + +负整型字面量的字面量需要在整型字量面前加减号 -,比如 -42。 + +数字间允许使用下划线 _ 来增加可读性,但下划线会被忽略而不会影响字面量的值。整型字面量可以以0开始,但同样会被忽略而不会影响其基数或字面量的值。 + +除非另有指定,整型字面量的默认类型为 Swift 标准库类型中的 Int。Swift 标准库还定义了各种不同长度的有符号和无符号的整数类型,请参见 整型。 + +> 整型字面量语法 + + +> 整型字面量 → 二进制字面量 + +> 整型字面量 → 八进制字面量 + +> 整型字面量 → 十进制字面量 + +> 整型字面量 → 十六进制字面量 + +> 二进制字面量 → 0b 二进制数字 二进制字面量字符列表 可选 + +> 二进制数字 → 数字 0 到 1 + +> 二进制字面量字符 → 二进制数字 | _ + +> 二进制字面量字符列表 → 二进制字面量字符 二进制字面量字符列表 可选 + +> 八进制字面量 → 0o 八进字数字 八进制字符列表 可选 + +> 八进字数字 → 数值 0 到 7 + +> 八进制字符 → 八进字数字 | _ + +> 八进制字符列表 → 八进制字符 八进制字符列表 可选 + +> 十进制字面量 → 十进制数字 十进制字符列表 可选 + +> 十进制数字 → 数值 0 到 9 + +> 十进制数字列表 → 十进制数字 十进制数字列表 可选 + +> 十进制字符 → 十进制数字 | _ + +> 十进制字符列表 → 十进制字符 十进制字符列表 可选 + +> 十六进制字面量 → 0x 十六进制数字 十六进制字面量字符列表 可选 + +> 十六进制数字 → 数值 0 到 9, a 到 f,或 A 到 F + +> 十六进制字符 → 十六进制数字 | _ + +> 十六进制字面量字符列表 → 十六进制字符 十六进制字面量字符列表 可选 + + +## 浮点型字面量 + +浮点型字面量表示未指定精度浮点数的值。 + +浮点型字面量默认用十进制表示(无前缀),但也可以用十六进制表示(加前缀 0x)。 + +十进制浮点型字面量由十进制数字后跟小数部分或指数部分组成,或两者都有。十进制小数部分由小数点 . 后跟十进制数字组成。指数部分由大写或小写字母的前缀e 后跟十进制数字组成,这串数字表示 e 之前的数量乘以 10 的几次方。例如:1.25e2 表示 1.25 * 10^2,也就是 125.0;同样,1.25e-2 表示 1.25 * 10^-2,也就是 0.0125。 + +十六进制浮点型字面量由前缀 0x 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字组成。指数部分由大写或小写字母的前缀p 后跟十进制数字串组成,这串数字表示 p 之前的数量乘以 2 的几次方。例如:0xFp2 表示15 * 2^2,也就是 60;同样,0xFp-2 表示 15 * 2^-2,也就是 3.75。 + +与整型字面量不同,负的浮点型字面量由一元运算符减号 - 和浮点型字面量组成,例如 -42.0。这代表一个表达式,而不是一个浮点整型字面量。 + +下划线 _ 允许被用于增强可读性,但会被忽略而不影响字面量的值。浮点型字面量也可以在数字前加 0,但同样会被忽略而不影响字面量的值。 + +除非另外指定,浮点型字面量的默认类型为 Swift 标准库类型中的 Double,表示64位浮点数。Swift 标准库也定义 Float 类型,表示32位浮点数。 + +> 浮点型字面量语法 + +> 浮点数字面量 → 十进制字面量 十进制分数 可选 十进制指数 可选 + +> 浮点数字面量 → 十六进制字面量 十六进制分数 可选 十六进制指数 + +> 十进制分数 → . 十进制字面量 + +> 十进制指数 → 浮点数e 正负号 可选 十进制字面量 + +> 十六进制分数 → . 十六进制字面量 可选 + +> 十六进制指数 → 浮点数p 正负号 可选 十六进制字面量 + +> 浮点数e → e | E + +> 浮点数p → p | P + +> 正负号 → + | - + +## 字符串型字面量 + +字符串型字面量是双引号括起来的一个字符序列,形式如下: + + " characters " + +字符串型字面量中不能包含未转义的双引号 "、未转义的反斜线\、回车符(carriage return)或换行符(line feed)。 + +特殊符号经如下转义后可在字符串型字面量中使用: + +* 空字符 \0 +* 反斜线 \\ +* 水平制表符 \t +* 换行符 \n +* 回车符 \r +* 双引号 \" +* 单引号 \' + +字符也可以用以下方式表示:\x 后跟两位十六进制数字,\u 后跟四位十六进制数字,或 \U 后跟八位十六进制数字。在这些转义序列后跟的数字表示一个Unicode码。 + +字符串字面量可以在反斜线后面的小括号中插入表达式\()。插入的表达式必须不能包含未的双引号(”),反斜线(\),回车符或换行符。 + +表达式的计算结果必须是String类的一个有初始化的类型的值。 + +比如,下面所有的字符串型字面量拥有同样的值: + +> "1 2 3" + +> "1 2 \(3)" + +> "1 2 \(1 + 2)" + +> var x = 3; "1 2 \(x)" + + +字符串型字面量的默认类型为 String。组成字符串的字符的类型为 Character,更多关于String和Character类型,请参考 字符串和字符。 + +> 字符型字面量语法 + +> 字符串型字面量 → " 引用文本 " + +> 引用文本 → 引用文本条目 引用文本 可选 + +> 引用文本条目 → 转义字符 + +> 引用文本条目 → ( 表达式 ) + +> 引用文本条目 → 除了"¬, \¬, U+000A, 或 U+000D的所有Unicode的字符 + +> 转义字符 → \0 | \ | \t | \n | \r | \" | \' + +> 转义字符 → \x 十六进制数字 十六进制数字 + +> 转义字符 → \u 十六进制数字 十六进制数字 十六进制数字 十六进制数字 + +> 转义字符 → \U 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 + +## 运算符 + +Swift标准库定义了许多供你使用的运算符,其中大部分将在 基础运算符 和 高级运算符 中进行阐述。本章节将描述了哪些字符可被用作运算符。 + +运算符由一个或多个如下字符组成:/、=、-、+、!、*、%、<、>、&、|、^、~、.。也就是说,标记 =,->、//、/*、*/、. 以及一元前缀运算符 & 属于保留字,这些标记不能被重写或用于定义自定义的运算符。 + +运算符两侧的空白被用来区分一个运算符是否被用为前缀运算符(prefix operat或 )、后缀运算符(postfix operat或 )或二元运算符(binary operat或 )。规则总结如下: + +* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:a+b 和 a + b 中的运算符+ 被看作二元运算符。 + +* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 a ++b 中的 ++ 被看作前缀一元运算符。 + +* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 a++ b 中的 ++ 被看作后缀一元运算符。 + +* 如果运算符左侧没有空白并紧跟 .,将被看作后缀一元运算符。例如 a++.b 中的 ++ 被看作后缀一元运算符(同理, a++ . b 中的 ++ 是后缀一元运算符而 a ++ .b 中的 ++ 不是). + +鉴于这些规则的目的,运算符前的字符 (、[ 和 { ;运算符后的字符 )、] 和 } 以及字符 ,、; 和: 同样被认为空白。 + +以上规则有一点需要注意。如果运算符 ! 或 ? 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 ? 用作修饰可选类型(optional type),左侧必须无空白。如果用于条件运算符 ? :,必须两侧都有空白。 + +在特定构成中 ,以 < 或 > 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 Dictionary> 中没有必要添加空白来消除闭合字符 > 的歧义。在这个例子中, 闭合字符 > 被看作单字符标记,而不会被误解为移位运算符 >>。 + +要学习如何自定义新的,自定义的运算符,请参考 自定义操作符 和 运算符声明。学习如何重写现有运算符,请参考 运算符方法。 + + +> 运算符语法语法 + +> 运算符 → 运算符字符 运算符 可选 + +> 运算符字符 → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | . + +> 二元运算符 → 运算符 + +> 前置运算符 → 运算符 + +> 后置运算符 → 运算符 + + + diff --git a/src/chapter3/03_Types.md b/src/chapter3/03_Types.md index e69de29..4581ed3 100644 --- a/src/chapter3/03_Types.md +++ b/src/chapter3/03_Types.md @@ -0,0 +1,338 @@ +# 类型 +在swift里,有两种类型:命名类型和复合类型。命名类型是指在定义的时候能够给一个指定名字的类型。命名类型包含类、结构、枚举和协议。例如,一个用户自定义的名为MyClass的类的实例,其类型就是MyClass。除了用户定义的命名类型,swift标准库还定义了很多常用命名类型,如一些数组,字典,可选值。 + +一些被其它语言视为是最基础或最原始的数据类型,例如数字、字符、字符串,实际上都是命名类型,swift标准库使用结构去定义和实现他们。由于他们是命名类型的,你可以用扩展声明来扩展他们的功能,来满足你的程序需求,具体请参考 “扩展和扩展声明“。 + +复合类型是一个没有名字的类型,由swift内部自己定义。swift有两种复合类型:函数类型和元组类型。一个复合数据类型可以包含命名类型和其他复合类型。例如,一个元组类型(Int, (Int, Int))包含两个元素:第一个的命名类型是Int,第二个是其他复合类型(Int, Int)。 + +本章讨论swift语言本身定义的类型,并描述在swift中类型推断的方式。 + +> 类型的语法 + +> 类型 -> 数组类型|函数类型|类型标识|元组类型|可选类型|隐式去包装可选类型|协议构成类型|元型类型 + + +## 类型注解 + + +类型注解明确的指定一个变量或者表达式的类型。类型注解以冒号(:)开始,以类型结束,如下面的例子: + + 1. let someTuple:(Double, Double) = (3.14159, 2.71828) + + 2. func someFunction(a:Int) { /**/} + +在第一个例子中,表达式someTuple是被定义为元组类型(Double, Double)。在第二个例子中,函数someFuncion中的参数a被定义为Int类型。 + + +类型注解可以在类型前面包含一个可选的类型属性的列表。 + +> 类型注解的语法 + +> 类型注解 -> :属性[可选]类型 + + +## 类型标识符 + + +类型标识符是指一个命名类型或者命名类型/复合类型的别名。 + + +大多数情况下,类型标识符直接指向和标示符命名相同的命名类型。例如,类型标识符Int指向命名类型Int,类型标识符Dictionary指向命名类型Dictionary。 + + +命名标识符和类型标识符不同名有两种情况。第一种情况,命名标识符指向命名类型或者复合类型的别名。例如,在下面的例子中,类型标识符使用Point指向元组类型(Int, Int)。 + + typealias Point = (Int, Int); + let origin: Point = (0, 0); + +第二种情况,类型标识符用点(.)指向在其它模块中声明或嵌套在其它类型中的命名类型。例如,在下面的代码中,类型标识符引用在模块ExampleModule中声明的命名类型MyType。 + + var someValue: ExampleModule.MyType + + +> 命名标识符的语法 + +> 命名标识符 -> 类型名称 泛型参数子句[可选]|类型名称 泛型参数子句[可选].类型标识符 + +> 类型标识符 -> 标识符 + + +## 元组类型 + +元组类型是指在括号中,用逗号分隔的零到多个类型的列表。 + + +你可以用元组类型作为函数的返回值类型,这样函数就能返回包含多个值的单元组。你也可以给元组类型中的元素命名,用这些名字来指代单个元素的值。元素的名字由标识符和紧跟着的冒号(:)组成。关于这两种特性的具体用法,请看 “多个返回值的函数“。 + +Void是空元组类型的别名,表示为()。如果在括号里面只有一个元素,那么这个类型就是这个元素的类型。例如,(Int)的类型是Int,不是(Int)。因此,你可以认为仅当元组类型包含两个或者更多元素的时候才是元组元素。 + + +> 元组类型的语法 + + +> 元组类型 -> (元组类型体[可选]) + + +> 元组类型体 -> 元组类型元素列表...[可选] + + +> 元组类型元素列表 -> 元组类型元素|元组类型元素,元组类型元素列表 + + +> 元组类型元素 -> 属性[可选]inout[可选]类型|inout[可选]元素名称 类型注解 + + +> 元素名称 -> 标识符 + + +## 函数类型 + + +函数类型表示一个函数,方法或者闭包的类型,它由参数和返回类型组成,中间通过箭头(->)分隔: + +由于参数类型和返回类型都可以为元组类型,所以函数类型支持含有多个参数和多个返回值的函数和方法。 + +你可以把自动闭包(auto_closure)的属性归为有一个参数类型为(),返回值为表达式类型(请看 “类型属性“)的函数类型。一个自动闭包函数捕获的是指定表达式上的隐式闭包而不是表达式本身。下面的例子用auto_closure属性来定义一个简单的assert函数: + + func simpleAssert(condition: @auto_closure () -> Bool, message: String){ + if !condition(){ + println(message) + } + } + let testNumber = 5 + simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.") + // prints "testNumber isn't an even number." + + + +一个函数类型在参数类型中可以让一个可变参数作为其最后一个参数。从语法上来说,可变参数可以由一个基础类型名称和紧跟着的三个点(...)组成,例如Int...。可变参数被认为是一个包含基础类型名称的数组。例如,可变参数Int... 被认为是Int[]。使用可变参数的例子,请参考 “可变参数“。 + + +指定一个in-out参数,需要给参数类型加上inout的前缀。可变参数和返回类型不能使用inout标记。in-out参数在’In-Out参数‘中有讨论。 + + +柯里化函数(curried function)类型相当于嵌套函数类型。例如,下面的柯里化函数addTwoNumbers()()的类型是Int -> Int -> Int: + + func addTwoNumbers(a: Int)(b: Int) -> Int{ + return a + b + } + + addTwoNumbers(4)(5) // returns 9 + + +柯里化函数的函数类型从右到左形成一组。例如,函数类型Int -> Int -> Int被理解为Int -> (Int -> Int) -- 指函数传入一个Int,然后返回另外一个输入输出都是Int的函数。例如,你可以把柯里化函数addTwoNumbers()()写成如下的嵌套函数形式: + + func addTwoNumbers(a: Int) -> (Int -> Int){ + func addTheSecondNumber(b: Int) -> Int{ + return a + b + } + return addTheSecondNumber + } + + addTwoNumbers(4)(5) // Returns 9 + + +> 函数类型的语法 + +> 函数类型 → 类型 -> 类型 + + +## 数组类型 + + +在swift中类型紧跟着可括号[]作为标准库定义的命名类型Array的简写。换句话说,下面两个声明是相等的: + + let someArray: String[] = ["Alex", "Brian", "Dave"] + let someArray: Array = ["Alex", "Brian", "Dave"] + + +在这两种情况下,常量someArray被定义为字符串数组。数组元素也可以用中括号访问:someArray[0] 指向index为0的元素,即‘Alex’。 + +如上所示,你可以用数组自变量字面量?和[]创建一个数组。空数组自变量用[]表示,也可以创建特定类型的空数组。 + + var emptyArray: Double[] = [] + + +你可以用多组中括号相连来创建多维数组。例如,你可以用三组中括号来创建一个三维整数数组: + + var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] + + +当访问多维数组里面的元素时,最左边的下标指向数组最外层对应位置的元素,接下来往右的下标指向第一层嵌套的数组相应位置的元素。依此类推。根据上面的定义,则array3D[0]指向[[1, 2], [3, 4]],array3D[0][1]指向[3, 4],array3D[0][1][1]的值是4。 + + +想要更多了解swift标准库中关于数组类型的详细讨论,请看 “数组“。 + +> 数组类型的语法 + +> 数组类型 → 类型[] 数组类型[] + + +## 可选类型 + + +Swfit语言定义后缀?作为命名类型Optional的简写,换句话说,以下两种声明是相等的: + + var optionalInteger: Int? + var optionalInteger: Optional + +在这两种情况下,变量optionalInteger都是可选整数类型。注意,在类型和?之间没有空格。 + + +Optional 是一个含有两种情况的枚举,None和Some(T),用来表示可能有或可能没有值。任何类型都可以明确声明为(或者隐式转换)可选类型。当声明一个可选类型的时候,要确保用括号给?操作符一个合适的范围。例如,声明可选整数数组,应该写成(Int[])?;写成Int[]?会报错。 + + +当你声明一个可选变量或者可选属性的时候没有提供初始值,它的值会默认为nil。 + + +可选项遵照LogicValue协议,因此可以出现在布尔环境中。在这种情况下,如果可选类型T?包含类型为T的任何值(也就是说它的值是Optional.Some(T)),这个可选类型等于true,反之为false。 + + +如果一个可选类型的实例包含一个值,你可以用后缀操作符!来访问这个值,如下所示: + + optionalInteger = 42 + optionalInteger! // 42 + +使用操作符!去获取值为nil的可选变量会有运行时错误。 + + +你可以用可选链接和可选绑定选择性执行可选表达式上的操作。如果值为nil,任何操作都不会执行,也不会有运行报错。 + + +更过关于可选类型的信息和例子,请看 “可选“。 + + +> 可选类型语法 + + +> 可选类型 → 类型 ? + + + +## 隐式解析可选类型 + +在swift中定义后缀!为标准库定义的名类型ImplicitlyUnwrappedOptional的简写。换句话说,以下两种声明是相等的: + + var implicitlyUnwrappedString: String! + var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional + + +在这两种情况下,变量implicitlyUnwrappedString被声明为隐式可选类型字符串。注意,在类型和!之间没有空格。 + + +你代码中用到可选的地方都可以用隐式解析可选。例如,你可以设置隐式可选类型的值为变量、常量、可选属性,反之亦然。 + + +有了可选,当你声明一个隐式解析可选变量或者属性的时候没有赋初始值,它的值默认为nil。 + + +隐式解析可选的值会在使用的时候自动解析,所以不需要用!去解析它。也就是说,如果你用值为nil的隐式解析可选,那么将会导致运行错误。 + + +使用可选链能够选择性的执行隐式解析可选表达式上的操作。用可选链接在隐式解析可选的表达式上做一定的操作。如果值是nil,没有操作会被执行,也不会有运行错误。 + +更多关于隐式解析可选类型,请看 “隐式解析可选”。 + + +> 隐式解析可选类型语法 + +> 隐式解析可选类型 -> 类型! + + +## 协议组合类型 + +协议组合类型是指符合指定协议列表里每个协议的类型。协议组合类型可以用在类型注解和泛型参数中。 + +协议组合类型的格式如下: + + protocol + +一个协议组合类型的类型符合多个协议的要求,不需定义新的命名协议,它继承了从每个协议符合的类型。例如,指定一个协议组合类型protocol相当于定义一个新的协议ProtocolD,它继承了ProtocolA,ProtocolB和 ProtocolC,但是没有引入一个新的名字。 + +协议组合列表中的每一项必须是协议名或者是协议组合类型的别名。如果列表是空的,它会指定一个空的协议组合类型,任何类型都可以匹配。 + + +> 协议组合类型语法 + +> 协议组合类型 -> 协议<协议标示符列表[可选]> + +> 协议标示符列表 -> 协议标示符 | 协议标示符,协议标示符列表 + +> 协议标示符 -> 类型标示符 + + +## 元类型 + + +元类型是指所有类型的类型,包括类、结构、枚举、协议。 + + +类、结构、枚举的元类型是相应的类型名称后面跟着的名字.Type。协议类型的元类型 -- 不是具体的类型,而是根据协议运行时来适配 -- 是该协议后面跟着的名字.Protocol。例如,类SomeClass的元类型是SomeClass.Type,协议SomeProtocol的元类型是SomeProtocol.Protocol。 + + +你可以用后缀self的方式获取类型。例如,SomeClass.self返回 SomeClass本身,不是SomeClass的实例。SomeProtocol.self返回SomeProtocol本身, 不是运行时SomeProtocol的实例。你可以用dynamicType表达式类获取实例运行时的类型,如下面的例子所示: + + class SomeBaseClass { + class func printClassName() { + println("SomeBaseClass") + } + } + class SomeSubClass:SomeBaseClass { + override class func printClassName() { + println("SomeSubClass") + } + } + let someInstance: SomeBaseClass = SomeSubClass() + // someInstance is of type SomeBaseClass at compile time, but + // someInstance is of type SomeSubClass at runtime + + someInstance.dynamicType.printClassName() + + // prints "SomeSubClass" + + +> 元类型语法 + +> 元类型 → 类型.Type | 类型.Protocol + + + +## 类型继承子句 + + +类型继承子句被用来指定一个命名类型继承哪个类,适配哪些协议。类型继承子句以冒号(:)开始,紧跟着以逗号分割的类型标示符列表。 + + +类类型可能继承单个超类,适配多个协议。当定义一个类的时候,超类的名称必须出现在类型标示符列表首位,接着是类必须适配的一些协议。如果类不继承其他类,那么列表就是以协议开头。类机继承的扩展讨论和例子,请看 “继承”。 + + +其他命名协议可能仅继承或适配一个协议列表。协议类型可能继承多个其它协议。当一个协议类型继承其它协议的时候,其它协议的条件会被集合在一起,任何继承当前协议的类型必须适配所有的这些条件。 + + +在枚举类型里面定义的类型继承子句可以是一个协议列表,或者指定原始值的枚举实例,或一个单独的指定原始值类型的命名型类型。在枚举定义中用类型继承子句来指定原始值类型的例子,请看 “原始类型”。 + + +> 类型继承子句语法 + +> 类型继承子句 → : 类型继承列表 + +> 类型继承列表 → 类型标示符 | 类型表示符,类型继承列表 + + + +## 类型推断 + + +swift广泛使用类型推断,它允许你在代码里忽略很多变量和表达式的类型或者部分类型。例如,var x: Int = 0可以完全忽略类型,简写成 var x = 0 -- 编译器能够正确的推测出x的类型名称是Int。同样,当完整的类型能够通过上下文推断出来的时候,你可以忽略部分类型。例如, dict: Dictionary = ["A": 1],编译器推断出dict的类型是Dictionary。 + + +在上面的两个例子中,类型信息从表达树的叶子传向根。也就是说,x在var x: Int = 0的类型通过首先判断0的类型,然后再传递类型信息到根(即变量x)。 + + +在swift里面,类型推断可以反方向推断 -- 从根传递到叶子。下面这个例子就是这种情况,常量eFloat显示类型注解(:Float)导致数字2.71828的类型是Float而不是Double。 + + let e = 2.71828 // The type of e is inferred to be Double. + let eFloat: Float = 2.71828 // The type of eFloat is Float. + +swift中的类型推断在单个表达式或者语句上操作。这意味着推测忽略类型或者部分类型信息必须从表达式或者其子表达式类型检测中获取。 + diff --git a/src/chapter3/04_Expressions.md b/src/chapter3/04_Expressions.md index e69de29..6dc7fb3 100644 --- a/src/chapter3/04_Expressions.md +++ b/src/chapter3/04_Expressions.md @@ -0,0 +1,774 @@ + +# 表达式 + +在swift中有四种表达式:前缀表达式,二元表达式,主表达式和后缀表达式。对表达式求值可以得到一个返回值、或完成某些逻辑运算,或同时完成这两件事。 + +前缀和二元表达式可以让你在更短小的表达式中使用运算符。主表达式从概念上来看是最简单的表达式,并提供了一种求值的方式。后缀表达式,与前缀表达式和二元表达式相似,都可以让你建立更为复杂的表达方式,比如函数调用和成员访问。我们将在本节中详细解释每种表达式。 + +> 表达式语法 + +> 表达式 → 前置表达式 二元表达式列表 可选 + +> 表达式列表 → 表达式 | 表达式 **,** 表达式列表 + + +## 前缀表达式 + +前缀表达式由一个任意前缀运算符和表达式构成。前缀表达式只接受一个参数。 + +Swift标准库提供了如下前缀运算符: + +* ++ 自增 +* -- 自减 +* ! 逻辑否 +* ~ 按位否 +* \+ 一元加 +* \- 一元负 + + +关于这些运算符的信息,请参见:“Basic Operators and Advanced Operators.”。 + + +除了上面标准库的运算符列表,你可以在调用函数的参数变量名前使用‘&’。?? +除了上述标准库运算符外,你可以在作为某个函数参数的变量名前使用‘&’运算符。更多信息和例子,请见:In-Out Parameters。 + +> 前缀表达式语法 + +> 前置表达式 → 前置运算符 可选 后置表达式 + +> 前置表达式 → 写入写出表达式 +> 写入写出表达式 → **&** 标识符 + + + +## 二元表达式 + +二元表达式由‘左边表参数’+‘中缀二元操作符’+‘右边参数’组成。形式如下: + + left-hand argument operator right-hand argument + +Swift标准库提供了以下的二元运算符: + +* 指数(无结合性,优先级160) + * << 按位左移 + * \>> 按位右移 + +* 乘法(左结合,优先级150) + * \* 乘 + * / 除 + * % 求余 + * &* 乘,ignoring overflow + * &/ 除,ignoring overflow + * &% 求余,ignoring overflow + * & 按位与 + +* 加法(左结合,优先级140) + * \+ 加 + * \- 减 + * &+ 加,with overflow + * &- 减,with overflow + * | 按位或 + * ^ 按位异或 + +* 值域(无结合性,优先级135) + * .. 半闭值域 + * ... 闭值域 + + +* 类型转换(无结合性,优先级132) + * is 类型检查 + * as 类型转换 + +* 比较(无结合性,优先级130) + * < 小于 + * <= 小于等于 + * \> 大于 + * \>= 大于等于 + * == 等于 + * != 不等于 + * === 恒等于 + * !== 不恒等 + * ~= 模式匹配 + +* 合取性(conjunctive)(左结合,优先级120) + * && 逻辑与 + +* 析取性(disjunction)(左结合,优先级110) + * || 逻辑或 + +* 三元条件(右结合,优先级100) + * ?: 三元条件 + +* 赋值(右结合,优先级90) + * = 赋值 + * *= 乘等 + * /= 除等 + * %= 余等 + * += 加等 + * -= 减等 + * <<= 左移等 + * \>>= 右移等 + * &= 按位与等 + * ^= 按位异或等 + * |= 按位非等 + * &&= 逻辑与等 + * ||= 逻辑或等 + +关于这些运算符的使用信息,请参见:Basic Operators and Advanced Operators。 + +> 注解 + +> 在解析时,由二元操作符构成的表达式被视为一个简单列表。这个列表按照运算符的优先级被转换成树(tree),比如“2 + 3 * 5”首先被理解为一个5个元素的列表:'2'、'+'、'3'、'+'、'5',随后被转换成树(2 + (3 * 5))。 + + + +> 二元表达式语法 + +> 二元表达式 → 二元运算符 前置表达式 + +> 二元表达式 → 赋值运算符 前置表达式 + +> 二元表达式 → 条件运算符 前置表达式 + +> 二元表达式 → 类型转换运算符 + +> 二元表达式列表 → 二元表达式 二元表达式列表 可选 + + +## 赋值运算符 + +赋值运算符会给某个给定的表达式赋值。形式如下: + + expression = value + +表达式的意思就是计算`value`的值并赋给`expression`。如果`expression`是个元组,那么`value`必须是含有同等数量元素的元祖。(嵌套元祖亦可。)元祖赋值会把`value`中相对应的部分分别赋给`expression`。例如: + + > (a, _, (b, c)) = ("test", 9.45, (12, 3)) + + > // a is "test", b is 12, c is 3, and 9.45 is ignored + + +赋值运算符不返回任何值。 + +> 赋值运算符的语法 + +> 赋值运算符 → **=** + + +## 三元条件运算符 + +三元条件运算符是根据一个条件判断来求值。形式如下: + + condition ? expression used if true : expression used if false + +如果condition的值为true时,那么就对第一个表达式求值并返回。否则就对第二个表达式求值并返回。没有用到的表达式将不会被调用。 + +更多三元条件运算符的例子,请参见Ternary Conditional Operator。 + + +> 条件运算符语法: + +> 三元条件运算符 → **?** 表达式 **:** + + +## 类型转换运算符 + +类型转换运算符有两种:as运算符和is运算符。形式如下: + + + expression as type + + expression as? type + + expression is type + + +as 运算符会把目标表达式`expression`转换成指定的类型`type`。方式如下: + + +* 如果能够保证成功转换为指定类型,那么目标表达式`expression`就会返回指定类型的实例。典型的例子就是子类转换为超类。 + +* 如果转换到指定类型肯定失败,则抛出编译错误。 + + +如果在编译的时候不知道转换是否成功,那么转换表达式的类型是指定类型的可选类型。在运行时,如果转换成功,表达式会被包装成一个可选类型并返回。 +否则,返回nil。对应的例子就是子类转换为超类: + +> class SomeSuperType {} + +> class SomeType: SomeSuperType {} + +> class SomeChildType: SomeType {} + +> let s = SomeType() + +> let x = s as SomeSuperType // known to succeed; type is SomeSuperType + +> let y = s as Int // known to fail; compile-time error + +> let z = s as SomeChildType // might fail at runtime; type is SomeChildType? + + +使用as指定类型与使用类型注释对编译器来说是一样的,看下面这个例子: + +> let y1 = x as SomeType // Type information from 'as' + +> let y2: SomeType = x // Type information from an annotation + + +is运算符会在运行时检查`expression`是否指定了类型。如果是,则返回true, 否则 返回false。 + + +在编译时编译器不检查它们是true还是false,下面例子是无效的: + +> "hello" is String +> + "hello" is Int + + +更多关于类型转换和使用类型转换操作符的例子,请参见: Type Casting. + +> 类型转换的语法 + +> 类型转换运算符 → **is** 类型 | **as ?** 可选 类型 + + +## 主表达式 + +主表达式是种最基础的表达式。它可以单独作为表达式使用,也可以和其他符号一起使用组合成前缀表达式、二元表达式和后缀表达式。 + +> 主表达式的语法 + +> 主表达式 → 标识符 泛型参数子句 可选 +> 主表达式 → 字面量表达式 +> 主表达式 → self表达式 +> 主表达式 → 超类表达式 +> 主表达式 → 闭包表达式 +> 主表达式 → 圆括号表达式 +> 主表达式 → 隐式成员表达式 +> 主表达式 → 通配符表达式 + + +## 字面量表达式 + +一个字面量表达式可以由普通文本(比如一个字符串或一个数字)、数组或字典字面量,或以下指定字面量组成: + + +Literal | Type | Value +------------ | --------| ------------ +\__FILE__ | String | The name of the file in which it appears. +\__LINE__ | Int | The line number on which it appears. +\__COLUMN__ | Int | The column number in which it begins. +\__FUNCTION__| String | The name of the declaration in which it appears. + +Literal | Type | Value +------------ | --------| ------------ +\__FILE__ | String | 当前文件的文件名 +\__LINE__ | Int | 当前行数 +\__COLUMN__ | Int | 当前列数 +\__FUNCTION__| String | 当前声明的名字 + + +在函数中,__FUNCTION__的值是当前函数的名字。在方法中,它的值是当前方法的名字。在内部getter/setter属性中,它就是属性的名字。在init和subscript这样特殊成员中,它的值是关键字的名字。在文件顶层(at the top level of a file),它是前模块的名字。 + + +数组字面量是一个有序的值的集合。形式如下: + + [value 1, value 2, ...] + + +数组中的、最后一个表达式后面可以后跟一个逗号。一个空数组可以写成[].。数组字面量的类型是T[],这个T就是数组中表达式的类型。如果数组含多个类型的表达式,T则是他们的最近公共超类型(closest common supertype)。 + + +字典字面量是无序键值对的集合。形式如下: + + [key 1: value 1, key 2: value 2, ...] + + + +字典中的最后一个表达式后面也可以跟一个逗号。 [:]组成了一个空的字典字面量。字典字面量的类型是Dictionary,这里KeyType、ValueType就是key和value的类型。如果包含多个类型的表达式,KeyType 和ValueType分别取他们相应的最近的公共超类型(closest common supertype)。 + +> 字面量表达式的语法 + +> 字面量表达式 → 字面量 + +> 字面量表达式 → 数组字面量 | 字典字面量 + +> 字面量表达式 → \_\_FILE\_\_ | \_\_LINE\_\_ | \_\_COLUMN\_\_ | \_\_FUNCTION\_\_ + +> 数组字面量 → [ 数组字面量项列表 可选 ] + +> 数组字面量项列表 → 数组字面量项 , 可选 | 数组字面量项 , 数组字面量项列表 + +> 数组字面量项 → 表达式 +> 字典字面量 → [ 字典字面量项列表 ] | [ : ] +> 字典字面量项列表 → 字典字面量项 , 可选 | 字典字面量项 , 字典字面量项列表 +> 字典字面量项 → 表达式 : 表达式 +## self表达式 + + +self表达式是对当前类型或当前实例的直接引用。形式如下: + + self + + self.member name + + self[subscript index] + + self(initializer arguments) + + self.init(initializer arguments) + + + +如果在初始设定式、子脚本、实例方法中,self指向当前类型实例的引用。在静态方法和类方法中,self指向前类型的引用。 + + +self表达式用于在访问成员变量时指定作用域,消除作用域中有重名变量的冲突,例如函数的参数。例如: + + class SomeClass { + var greeting: String + init(greeting: String) { + self.greeting = greeting + } + } + + + +在派生方法中,你可以把一个那个类型的新实例赋值给self。例如: + + struct Point { + var x = 0.0, y = 0.0 + mutating func moveByX(deltaX: Double, y deltaY: Double) { + self = Point(x: x + deltaX, y: y + deltaY) + } + } + + +> self表达式的语法 + +> self表达式 → **self** + +> self表达式 → **self** **.** 标识符 + +> self表达式 → **self** [ 表达式 ] + +> self表达式 → **self . init** + + +## 超类表达式 + + +超类表达式可以让子类和超类相互访问,形式如下: + + super.`member name` + + super[`subscript index`] + + super.init(`initializer arguments`) + + + +第一种形式用来访问超类的某个成员。第二种形式用来访问超类的子脚本实现(subscript implementation)。第三种用来访问该超类的初始设定式。 + + +子类可以利用超类的实现,并通过使用超类表达式来实现它们的成员、下标和初始值。 + +> 超类表达式的语法 + +> 超类表达式 → 超类方法表达式 | 超类下标表达式 | 超类构造器表达式 + +> 超类方法表达式 → **super . **标识符 + +> 超类下标表达式 → **super [** 表达式 **]** + +> 超类构造器表达式 → **super . init** + +## 闭包表达式 + + +闭包表达式可以创建一个闭包,就好像其他语言中的lambda或者匿名函数。跟函数声明一样,闭包包含了要执行的语句,接收作用域中的变量。形式如下: + + { (parameters) -> return type in + statements + } + + +闭包的参数声明跟函数的参数一样,具体请参见Function Declaration。 + + +有好几种特殊形式,让闭包写起来更加简洁: + + +* 闭包可以省略参数和返回值的类型。如果省略了参数和参数类型,语句前的in也要省略。如果省略的类型不能被推断出来,那么就会抛出编译错误。 + + +* 闭包可以省略参数名。如果省略参数名,它们会隐式地命名为:`$0`,`$1`,`$2`,以此类推。 + + +如果闭包中只包含一个表达式,那么默认返回表达式的值。同时表达式的内容在进行类型推断的时候也会参考周围的表达式。 + + +以下几个闭包表达式是等价的: + + myFunction { + (x: Int, y: Int) -> Int in + return x + y + } + + myFunction { + (x, y) in + return x + y + } + + myFunction { return $0 + $1 } + + myFunction { $0 + $1 } + + +关于将闭包作为函数参数的更多内容,请参见:Function Call Expression。 + + +闭包表达式可以明确指定从作用域中通过捕获列表来指定值。捕获列表是在参数列表前使用中括号加逗号分隔的形式组成的。一旦使用了捕获列表,即使省略了参数名,参数类型和返回值类型,也要使用in关键字。 + + +捕获列表中的每一项都要标记为weak或unowned来捕获弱引用和无主引用。 + + myFunction{ print(self.title) } + // strong capture + myFunction { [weak self] in print(self!.title) } + // weak capture + myFunction { [unowned self] in print(self.title) } + // unowned capture + + + +在捕获列表中,你能给命名值绑定任意表达式。在闭包执行的时候表达式被计算并捕获。例如: + + // Weak capture of "self.parent" as "parent" + myFunction { [weak parent = self.parent] in print(parent!.title) } + + +更多关于闭包表达式信息和例子,请参见 Closure Expressions。 + +> 闭包表达式的语法 + +> 闭包表达式 → **{** 闭包签名 可选 多条语句 **}** +> 闭包签名 → 参数子句 函数结果 可选 **in** +> 闭包签名 → 标识符列表 函数结果 可选 **in** +> 闭包签名 → 捕获列表 参数子句 函数结果 可选 **in** +> +> 闭包签名 → 捕获列表 标识符列表 函数结果 可选 **in** +> 闭包签名 → 捕获列表 **in** +> 捕获列表 → **[** 捕获说明符 表达式 **]** +> 捕获说明符 → **weak** |**unowned** | **unowned(safe)** | **unowned(unsafe)** + + +# 隐式成员表达式 + +隐式成员表达式是一个访问类型成员变量的简写,例如枚举、类,通过上下文能够推断出隐式类型。形式如下: + .member name + + +例子: + + var x = MyEnumeration.SomeValue + x = .AnotherValue + + +> 隐式成员表达式的语法: + +> 隐式成员表达式 → **.** 标识符 + + +## 圆括号表达式 + + +圆括号表达式由圆括号包裹、逗号分隔的子表达式列表组成。每个子表达式前面可以有一个可选标识符,由`:`分隔。形式如下: + + (identifier 1: expression 1, identifier 2: expression 2, ...) + + + +圆括号表达式可以用来创建元祖,然后传递参数给函数。如果圆括号表达式中只有一个值,那么这个表达式的类型就是值的类型,例如表达式`(1)`的类型为`Int`不是`(Int)`。 + +> 圆括号表达式的语法 + +> 圆括号表达式 → **(**表达式元素列表 可选 **)** +> +> 表达式元素列表 → 表达式元素 | 表达式元素 , 表达式元素列表 +> +> 表达式元素 → 表达式 | 标识符 **:** 表达式 + +## 通配符表达式 + + +通配符表达式用来在赋值的时候显式地忽略某个值。比如下面的赋值语句中,`10`传递给`x`,`20`则被忽略。 + + (x, _) = (10, 20) + // x is 10, 20 is ignored + + + +> 通配符表达式的语法 + +> 通配符表达式 → _ + + +## 后缀表达式 + + + +后缀表达式由一个表达式后面加上一个后缀操作符或其他后缀语法组成。单纯从语法上讲,每个主表达式也是一个后缀表达式。 + + +* ++ Increment +* -- Decrement + +swift标准库提供了以下后缀操作符: + +* ++ 自增 +* -- 自减 + + +更多关于这些操作符的使用信息,请参见“Basic Operators and Advanced Operators.” + + +> 后缀表达式的语法 +> +> 后置表达式 → 主表达式 +> +> 后置表达式 → 后置表达式 后置运算符 +> +> 后置表达式 → 函数调用表达式 +> +> 后置表达式 → 构造器表达式 +> +> 后置表达式 → 显示成员表达式 +> +> 后置表达式 → 后置self表达式 +> +> 后置表达式 → 动态类型表达式 +> +> 后置表达式 → 下标表达式 +> +> 后置表达式 → 强制取值表达式 +> +> 后置表达式 → 可选表达式 + +## 函数调用表达式 + + +函数调用表达式由函数名后加圆括号组成,圆括号里面为逗号分隔的函数参数列表。形式如下: + + function name(argument value 1, argument value 2) + + +函数名称可以是任何返回值为函数类型的表达式。 + + +如果函数声明中包含了参数名,那么函数调用时必须在参数值前加上参数名,并以分号分隔。这种函数调用表达式形式如下: + + function name(argument name 1: argument value 1, argument name 2: argument value 2) + + +函数调用表达式在括号后面可以包含闭包表达式的后缀闭包。后缀闭包会被当做函数的最后一个参数。下面两种写法相同: + + // someFunction takes an integer and a closure as its arguments + + someFunction(x, {$0 == 13}) + + someFunction(x) {$0 == 13} + + +如果这个闭包是函数的唯一参数,那么圆括号可以省略。 + + // someFunction takes a closure as its only argument + + myData.someMethod() {$0 == 13} + + myData.someMethod {$0 == 13} + + +> 函数调用表达式语法 + +> 函数调用表达式 → 后置表达式 圆括号表达式 + +> 函数调用表达式 → 后置表达式 圆括号表达式 可选 后置闭包 +> +> 后置闭包 → 闭包表达式 + + +## 构造器表达式 + +构造器表达式提供类型初始化。形式如下: + + expression.init(initializer arguments) + + + +你可以在函数调用表达式中使用构造器表达式来初始化一个类型的实例。构造器函数不像函数,它不能有返回值,例如: + + var x = SomeClass.someClassFunction // ok + + var y = SomeClass.init // error + + +构造器表达式还可以用作对超类初始化函数的代理。 + + class SomeSubClass: SomeSuperClass { + init() { + // subclass initialization goes here + super.init() + } + } + + +> 构造器表达式的语法 + +> 构造器表达式 → 后置表达式 **. init** + + +## 显式成员表达式 + + +显示成员表达式允许我们访问命名类型、元祖或模块的成员。它由元素、点(`.`)、成员的标识符三者组成。 + + expression.member name + +对于命名类型可作为类型定义或扩展的一部分。例如: + + class SomeClass { + var someProperty = 42 + } + let c = SomeClass() + let y = c.someProperty // Member access + + +元祖成员通过从零开始的有序整数隐式命名。例如: + + var t = (10, 20, 30) + t.0 = t.1 + // Now t is (20, 20, 30) + + +模块成员可以访问模块的顶级(top-level)声明。 + + +> 显示成员表达式的语法 +> 显示成员表达式 → 后置表达式 . 十进制数字 +> 显示成员表达式 → 后置表达式 . 标识符 泛型参数子句 可选 + + +## 后缀self表达式 + + +后缀self表达式由表达式或类型名,紧跟着`.self`组成。形式如下: + + expression.self + type.self + + +第一种形式计算出表达式(`expression`)的值。例如`x.self`就等于`x`。 + + +第二种形式计算出对应类型(`type`)的值。这种形式可以将某类型作为一个值来访问。例如,由于`SomeClass.self`等于`SomeClass`本身,所以你可以将其传给接受这种类型参数的函数或方法。 + + +> 后缀self表达式的语法 + +> 后缀self表达式 → 后缀表达式 **. self** + +## 动态类型表达式 + + + +动态类型(`dynamicType`)表达式由表达式和`.dynamicType`组成。形式如下: + + expression.dynamicType + + +表达式不能是类型的名字。整个动态类型表达式计算出表达式运行时的值,如下面的例子: + + class SomeBaseClass { + class func printClassName() { + println("SomeBaseClass") + } + } + class SomeSubClass: SomeBaseClass { + override class func printClassName() { + println("SomeSubClass") + } + } + let someInstance: SomeBaseClass = SomeSubClass() + // someInstance is of type SomeBaseClass at compile time, but + // someInstance is of type SomeSubClass at runtime + someInstance.dynamicType.printClassName() + // prints "SomeSubClass + + +> 动态类型表达式的语法 + +> 动态类型表达式 → 后置表达式 **. dynamicType** + +## 下标表达式 + + +下标表达式提供用getter/setter方法访问下标声明。形式如下: + + expression[index expressions] + + +下标表达式可以通过传递下标参数(`index expressions`)计算getter的值,setter也可以通过同样的方式。 + + +更多关于下标声明的信息,参见Protocol Subscript Declaration。 + +> 下标表达式语法: +> +> 下标表达式 → 后置表达式 **[** 表达式列表 **]** + +## 强制取值表达式 + + +强制取值表达式用于对非空(not `nil`)的可选值进行强行拆包装。形式如下: + + expression! + + +上式中,如果一个`expression`的值不是`nil`,那么该可选值会被拆包装并返回相应的类型。否则抛出运行时错误。 + +> 强制取值表达式的语法: +> +> 强制取值表达式 → 后置表达式 **!** + +## 可选链式表达式 + + +可选链式表达式提供一种在后缀表达式中使用可选值的简化的语法。形式如下: + + expression? + + +后缀`?`表达式就是简单的将所传参数作为可选值返回。 + + +包含可选链式表达式的后缀表达式通过特殊的方式计算。如果可选链式表达式为nil,所有在此后缀表达式中的操作符都将被忽略,整个后缀表达式返回nil。如果不为nil,则可选链式表达式被拆包装,然后用于其他的后最表达式的计算。在这两种情况下,该后缀表达式仍然是一个可选类型。 + + +如果一个包含可选链式表达式的后缀表达式嵌套在其他后缀表达式中,只有最外层的返回一个可选类型。下面例子中,当c不为nil时,它将被拆包然后用于.property和.performAction()的计算,整个表达式`c?.property.performAction() `拥有一个可选类型的值。 + + var c: SomeClass? + var result: Bool? = c?.property.performAction() + + +如果不使用可选链表达式,那么上面例子的代码等价于: + + if let unwrappedC = c { + result = unwrappedC.property.performAction() + } + + +> 可选链表达式的语法 + +> 可选链表达式 → 后置表达式 ? \ No newline at end of file diff --git a/src/chapter3/05_Declarations.md b/src/chapter3/05_Declarations.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/chapter3/05_Statements.md b/src/chapter3/05_Statements.md new file mode 100644 index 0000000..8d982e9 --- /dev/null +++ b/src/chapter3/05_Statements.md @@ -0,0 +1,577 @@ +> 翻译:玩家 + + +#Statements# +--------- +# 语句章节 # +---------- + +本页包含内容 + - [循环语句](#loop_statements) + - [分支语句](#branch_statements) + - [带标签的语句](#labeled_statement) + - [控制传递语句](#control_transfer_statements) + +In Swift, there are two kinds of statements: simple statements and control flow statements. Simple statements are the most common and consist of either an expression or a declaration. Control flow statements are used to control the flow of execution in a program. There are three types of control flow statements in Swift: loop statements, branch statements, and control transfer statements. + +swift和其他语言一样,语句可大致分为简单语句和控制流语句,控制流语句也可分为循环语句:用来重复的执行代码块;分支语句:用来执行满足特定条件的代码块;控制语句用来决定代码的执行顺序。后续会详细阐述各控制流语句的使用。 + +Loop statements allow a block of code to be executed repeatedly, branch statements allow a certain block of code to be executed only when certain conditions are met, and control transfer statements provide a way to alter the order in which code is executed. Each type of control flow statement is described in detail below. + +A semicolon (;) can optionally appear after any statement and is used to separate multiple statements if they appear on the same line. + +和javascript类似,在一行语句的结束尾可以不添加分号(;),但是如果一行有多个独立语句必须要添加。在语法上讲,在语句末尾是否添加分好都是可以的,但是从团队协助以及减少错误方面来讲,最好统一加上,一般而言大公司的svn服务器上都会添加一个钩子,用来减少出错的可能,所以最好还是养成添加的习惯。 + +> statement -> [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression);opt +> statement -> [declaration](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration);opt +> statement -> [loop-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement);opt +> statement -> [branch-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/branch-statement);opt +> statement -> [labeled-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/branch-statement) +> statement -> [control-transfer-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/control-transfer-statement);opt +> statement -> [statement statements](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/control-transfer-statement);opt + + + --------- + + +> 语句语法 +> *语句* → [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) **;** _可选_ +> *语句* → [*声明*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration) **;** _可选_ +> *语句* → [*循环语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement) **;** _可选_ +> *语句* → [*分支语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/branch-statement) **;** _可选_ +> *语句* → [*标记语句(Labeled Statement)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/labeled-statement) +> *语句* → [*控制转移语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/control-transfer-statement) **;** _可选_ +> *多条语句(Statements)* → [*语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement) [*多条语句(Statements)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) _可选_ + + + +##Loop Statements## +##循环语句## + +Loop statements allow a block of code to be executed repeatedly, depending on the conditions specified in the loop. Swift has four loop statements: a for statement, a for-in statement, a while statement, and a do-while statement. + +Control flow in a loop statement can be changed by a break statement and a continue statement and is discussed in Break Statement and Continue Statement below. + +> loop-statement -> [for-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-statement) +> loop-statement -> [for-in-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-in-statement) +> loop-statement -> [while-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-statement) +> loop-statement -> [do-while-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/do-while-statement) + +各个编程语言从语法上讲,基本的变量,语句都是类似的,swift循环语句也提供四种方式来循环语句:`for`语句 `for in`语句 `while`语句 `do-shile`语句 +通过`break`和`continue`可以改变循环语句的控制流,和其他语言类似,具体可参考break和continue + +> 循环语句语法 +> *循环语句* → [*for语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-statement) +> *循环语句* → [*for-in语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-in-statement) +> *循环语句* → [*while语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-statement) +> *循环语句* → [*do-while语句*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/do-while-statement) + + +##For Statement## +##for语句## + +A for statement allows a block of code to be executed repeatedly while incrementing a counter, as long as a condition remains true. + +A for statement has the following form: + + for initialization; condition; increment { + statements + } + +`for`语句可以重复的执行一端代码,并且可以递增一个计数器 +`for`语句的形式如下 + + for `initialzation`; `condition`; `increment` { + `statements` + } + +The semicolons between the initialization, condition, and increment are required. The braces around the statements in the body of the loop are also required. + +A for statement is executed as follows: + +The initialization is evaluated only once. It is typically used to declare and initialize any variables that are needed for the remainder of the loop. +The condition expression is evaluated. +If true, the program executes the statements, and execution continues to step 3. If false, the program does not execute the statements or the increment expression, and the program is finished executing the for statement. +The increment expression is evaluated, and execution returns to step 2. +Variables defined within the initialization are valid only within the scope of the for statement itself. + +The value of the condition expression must have a type that conforms to the LogicValue protocol. + +> for-statement -> for [for-init](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init);[expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt; [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) +> for-statement -> for ([for-init](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init)opt;[expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt; [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt) [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) +> for-init -> [variable-declaration](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/variable-declaration) | [expression-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression-list) + + +`initialzation` `condition`和`increment`之间的分号 不可少,`{`也不可少 + + 1. `initialzation`只是被执行一次,通常用于声明和初始化在接下来循环中需要的变量 + 2. `condition`:如果为真(`true`)`statements`将会执行,进行第3步,如果为`false`则`statements`和`increment`都不会被执行,for至此执行完毕 + 3. 计算`increment`表达式,然后转到第2步。 + +定义在`initialzation`中的变量仅在`for`语句的作用域以内有效。`condition`表达式的值的类型必须遵循LogicValue协议。 + +> For 循环语法 +> *for语句* → for [*for初始条件*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init) _可选_ **;** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) _可选_ **;** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) _可选_ [*代码块*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) +> for语句 → for [*for初始条件*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init) _可选_ **;** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) _可选_ **;** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) _可选_ **)** [*代码块*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) +> *for初始条件* → [*变量声明*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/variable-declaration) | [*表达式列表*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression-list) + + +##For-In Statement## +##For-In 语句## + +A for-in statement allows a block of code to be executed once for each item in a collection (or any type) that conforms to the Sequence protocol. + +A for-in statement has the following form: + + for item in collection { + statements + } + +`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循Sequence协议的任意类型)中的每一项。 + +`for-in`语句的形式如下: + + for `item` in `collection` { + `statements` + } + +The generate method is called on the collection expression to obtain a value of a generator type—that is, a type that conforms to the Generator protocol. The program begins executing a loop by calling the next method on the stream. If the value returned is not None, it is assigned to the item pattern, the program executes the statements, and then continues execution at the beginning of the loop. Otherwise, the program does not perform assignment or execute the statements, and it is finished executing the for-in statement + +> GRAMMAR OF A FOR-IN STATEMENT +> for-in-statement -> for [pattern](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) in [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) + +`for-in`语句在循环开始前会调用`collection`表达式的`generate`方法来获取一个生成器类型(这是一个遵循Generator协议的类型)的值。接下来循环开始,调用`collection`表达式的`next`方法。如果其返回值不是`None`,它将会被赋给`item`,然后执行`statements`,执行完毕后回到循环开始处;否则,将不会赋值给`item`也不会执行`statements`,`for-in`至此执行完毕。这和`javascript`中的`for in`语句是一样的,可以理解为循环`collection`,如果有key的取出,去执行接下来的`statements` + +`For-In`循环语法 +> *for-in语句* → **for** [*模式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) **in** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) [*代码块*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) + + +##While Statement## +##While语句## + +A while statement allows a block of code to be executed repeatedly, as long as a condition remains true. + +A while statement has the following form: + + while condition { + statements + } + +`while`语句允许重复执行代码块 +`while`语句的形式如下: + + while `condition` { + `statements` + } + +A while statement is executed as follows: + + 1. The condition is evaluated. +If true, execution continues to step 2. If false, the program is finished executing the while statement. + 2. The program executes the statements, and execution returns to step 1. + +Because the value of the condition is evaluated before the statements are executed, the statements in a while statement can be executed zero or more times. + +The value of the condition must have a type that conforms to the LogicValue protocol. The condition can also be an optional binding declaration, as discussed in [Optional Bindind](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_432) + +> GRAMMAR OF A WHILE STATEMENT +> while-statement -> whild [while-condition](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition) [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) +> while-condition -> [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) | [declaration](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration) + + +`while`语句的执行流程为判断`condition`,如果为真(`true`),这会执行`statements`,否则`while`语句执行到此结束 +由于`condition`的值在`statements`执行前就已计算出,因此`while`语句中的`statements`可能会被执行若干次,也可能不会被执行。 +`condition`表达式的值的类型必须遵循LogicValue协议。同时,`condition`表达式也可以使用可选绑定,请参考可选绑定待添加链接。 + +> While 循环语法 +> *while语句* → **while** [*while-condition*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition) [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) +> *while条件* → [*expression*](..\chapter3\04_Expressions.html#expression) | [*declaration*](..\chapter3\05_Declarations.html#declaration) + + +##Do-While Statement## +##Do-While 语句## + +A do-while statement allows a block of code to be executed one or more times, as long as a condition remains true. + +A do-while statement has the following form: + + do { + statements + } while condition + +`do-while`语句允许代码块被执行一次或多次。 +`do-while`语句的形式如下: + + do { + `statements` + } while `condition` + +A do-while statement is executed as follows: + + 1. The program executes the statements, and execution continues to step 2 + 2. The condition is evaluated.If true, execution returns to step 1. If false, the program is finished executing the do-while statement. + +`do-while`语句的执行流程如下: + + 1. 执行`statements`,完后钻到2 + 2. 计算condition表达式,如果返回(`true`)继续回到1新一轮执行,否则`do-while`至此执行完毕 + +Because the value of the condition is evaluated after the statements are executed, the statements in a do-while statement are executed at least once. + +The value of the condition must have a type that conforms to the LogicValue protocol. The condition can also be an optional binding declaration, as discussed in [Optional Binding](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_432) + + 由于`condition`是在`statements`执行之后才会计算,因此可见,相比较`while`而言,`do-while`至少会执行一次 +`condition`表达式的值的类型必须遵循`LogicValue`协议。同时,`condition`表达式也可以使用可选绑定,请参考可选绑定。 +> do-while-statement→ do [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) while [while-condition](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition) + + + +##Branch Statements## +##分支语句## + +Branch statements allow the program to execute certain parts of code depending on the value of one or more conditions. The values of the conditions specified in a branch statement control how the program branches and, therefore, what block of code is executed. Swift has two branch statements: an if statement and a switch statement. + +Control flow in a switch statement can be changed by a break statement and is discussed in Break Statement below + +> GRAMMAR OF A BRANCH STAREMENT +> branch-statement -> [if-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement) +> brach-statement -> [switch-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement) + +通过一个或者多个条件的值,来决定语句允许程序执行指定部分的代码,swift和其他语言类似提供了`if`语句和`switch`语句 +`switch`语句中的控制流可以用`break`语句修改,请参考[Break](http://chinaz.com/swift/chapter3/10_Statements.html#break_statement) 语句。 +>branch-statement → [if-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement) +>branch-statement → [switch-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement) + + +##If Statement## +##if语句## + +An if statement is used for executing code based on the evaluation of one or more conditions. + +There are two basic forms of an if statement. In each form, the opening and closing braces are required. + +The first form allows code to be executed only when a condition is true and has the following form: + + if condition { + statements + } + +通过一个或多个条件的值来决定执行哪一块的代码,if语句主要有三种使用方式,但无乱哪种方式都需要添加`{`和`}` +第一种形式是当且仅当条件为真时执行代码,像下面这样: + + if `condition` { + `statements` + } + +The second form of an if statement provides an additional else clause (introduced by the else keyword) and is used for executing one part of code when the condition is true and another part code when the same condition is false. When a single else clause is present, an if statement has the following form: + + + if condition { + statements to execute if condition is true + } else { + statements to execute if condition is false + } + +The else clause of an if statement can contain another if statement to test more than one condition. An if statement chained together in this way has the following form: + + if condition 1 { + statements to execute if condition 1 is true + } else if condition 2 { + statements to execute if condition 2 is true + } else { + statements to execute if both conditions are false + } + + +第二种形式是在第一种形式的基础上添加else语句,当只有一个else语句时,像下面这样: + + if `condition` { + `statements to execute if condition is true` + } else { + `statements to execute if condition is false` + } + +第三种则是else中又需要进行判断,即有多种判断情况: + + if `condition 1` { + `statements to execute if condition 1 is true` + } else if `condition 2` { + `statements to execute if condition 2 is true` + } + else { + `statements to execute if both conditions are false` + } + +The value of any condition in an if statement must have a type that conforms to the LogicValue protocol. The condition can also be an optional binding declaration, as discussed in[Option Binding](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_432) + +> GRAMMAR OF AN IF STATEMENT +> if-statement -> if [if-condition](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-condition) [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) [else-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/else-clause) opt +> if-condition -> [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) | [declaration](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration) +> else-clause -> else [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) | else [if-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement) + + +`if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,请参考可选绑定`待添加链接` +>if-statement → if [if-condition](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-condition) [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-condition) [else-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/else-clause) opt + +>if-condition → [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) | [declaration](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration) + +>else-clause → else [code-block](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) | else [if-statement opt](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement) + + +##Switch Statement## +##Switch 语句## + +A switch statement allows certain blocks of code to be executed depending on the value of a control expression. +A switch statement has the following form: + + switch control expression { + case pattern 1: + statements + case pattern 2 where condition: + statements + case pattern 3 where condition, + pattern 4 where condition: + statements + default: + statements + } + +取决于`switch~语句的控制表达式(control expression),`switch`语句将决定执行哪一块代码。 + +switch语句的形式如下: + + switch `control expression` { + case `pattern 1`: + `statements` + case `pattern 2` where `condition`: + `statements` + case `pattern 3` where `condition`, + `pattern 4` where `condition`: + `statements` + default: + `statements` + } +The control expression of the switch statement is evaluated and then compared with the patterns specified in each case. If a match is found, the program executes the statements listed within the scope of that case. The scope of each case can’t be empty. As a result, you must include at least one statement following the colon (:) of each case label. Use a single break statement if you don’t intend to execute any code in the body of a matched case. + +`switch`语句的表达式`control expression`会首选被计算,然后与下面的每个`case`的模式进行匹配,如果匹配成功,程序将会执行对应`case`中的`statements`,需要注意的是每个case不能为空,也就是说在case中至少有一条语句,如果在相应case中不想执行代码,只需要在程序中写个`break`即可 + + +The values of expressions your code can branch on is very flexible. For instance, in addition to the values of scalar types, such as integers and characters, your code can branch on the values of any type, including floating-point numbers, strings, tuples, instances of custom classes, and optionals. The value of the control expression can even be matched to the value of a case in an enumeration and checked for inclusion in a specified range of values. For examples of how to use these various types of values in switch statements, see Switch in the Control Flow chapter. + +A switch case can optionally contain a guard expression after each pattern. A guard expression is introduced by the keyword where followed by an expression, and is used to provide an additional condition before a pattern in a case is considered matched to the control expression. If a guard expression is present, the statements within the relevant case are executed only if the value of the control expression matches one of the patterns of the case and the guard expression evaluates to true. For instance, a control expression matches the case in the example below only if it is a tuple that contains two elements of the same value, such as (1, 1). + +case let (x, y) where x == y: + +可以用作控制表达式的值是什么灵活的,除了标量类型(scalartypes,如`Int`、`Character`)之外,你还可以使用其他任何类型的值,包括浮点数,字符串,远组,自定义类的实例以及可选(`optional`)类型,甚至是枚举类型中的成员和指定的范围(`range`)等。关于在`switch`语句中使用这些类型,请参考控制流章节的Switch. + +你可以在模式后端添加一个保护作用的表达式(guard expression),构成是这样的:关键字`where`后面跟着一个作为额外测试条件的表达式,因此当且仅当表达式匹配的一个case的某个模式在保护作用的表达式为真时,对应case中的`statements`才会被执行,在下面例子中,控制表达式只会匹配含有两个相等元素的元组 + + case let (x, y) where x == y: + } + + +As the above example shows, patterns in a case can also bind constants using the keyword let (they can also bind variables using the keyword var). These constants (or variables) can then be referenced in a corresponding guard expression and throughout the rest of the code within the scope of the case. That said, if the case contains multiple patterns that match the control expression, none of those patterns can contain constant or variable bindings. + + +正如上面的例子,也可以在模式中使用let(或var)语句来绑定常量(或变量)。这些常量(或变量)可以在其对应的起保护作用的表达式和其对应的case块里的代码中引用。但是,如果case中有多个模式匹配控制表达式,那么这些模式都不能绑定常量(或变量)。 + +A switch statement can also include a default case, introduced by the keyword default. The code within a default case is executed only if no other cases match the control expression. A switch statement can include only one default case, which must appear at the end of the switch statement. + +Although the actual execution order of pattern-matching operations, and in particular the evaluation order of patterns in cases, is unspecified, pattern matching in a switch statement behaves as if the evaluation is performed in source order—that is, the order in which they appear in source code. As a result, if multiple cases contain patterns that evaluate to the same value, and thus can match the value of the control expression, the program executes only the code within the first matching case in source order. + +switch语句也可以包含默认(default)块,只有其它case块都无法匹配控制表达式时,默认块中的代码才会被执行。一个switch语句只能有一个默认块,而且必须在switch语句的最后面。 + +尽管模式匹配操作的实际执行顺序,并且计算顺序是不可知的,但是Swift规定能够了swift中的语句中的模式匹配顺序和书写源码的顺序保持一致。因此,当多个模式含有相通的值并且能够匹配控制表达式时,程序只会执行源码中第一个匹配`case`中的代码 + +##Switch Statements Must Be Exhaustive## +##Switch 语句必须是完备的## + +In Swift, every possible value of the control expression’s type must match the value of at least one pattern of a case. When this simply isn’t feasible (for instance, when the control expression’s type is Int), you can include a default case to satisfy the requirement + +`switch`语句中包含多个`case`,每个`case`都是`switch`的一个分支,由`switch`来决定执行哪一个分支代码,`switch`语句必须是完整的,也就是值必须与`case`中的某一个对应,可以通过`default`来指定默认就不符合`case`条件时执行的代码块,并且这个`default`分支必须在最后。 + +##Execution Does Not Fall Through Cases Implicitly## +##不存在隐式的贯穿(fall through)## + +After the code within a matched case has finished executing, the program exits from the switch statement. Program execution does not continue or “fall through” to the next case or default case. That said, if you want execution to continue from one case to the next, explicitly include a fallthrough statement, which simply consists of the keyword fallthrough, in the case from which you want execution to continue. For more information about the fallthrough statement, see Fallthrough Statement below + +> switch-statement -> switch [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) {[switch-cases](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) opt} +> switch-cases -> [switch-case](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-case) [switch-cases](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) opt +> switch-case -> [case-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label) [statements](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) | [default-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label) [statements](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) +> switch-case [switch-case](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label): | [default-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label): +> case-label -> case [case-item-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list): +> case-item-list -> [pattern](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [guard-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause) opt | [pattern](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [guard-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause)opt , [case-item-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list) +> default-label -> default +> guard-clause -> where [guard-expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-expression) +> guard-expression -> [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) + +当匹配的`case`代码在执行完毕后,程序会终止`switch`语句,而不会继续执行下一个`case`语句,这就意味着,如果你想执行下一个case块,需要显式地在你需要的case块里使用fallthrough语句。关于fallthrough语句的更多信息,请参考Fallthrough 语句。 + +> switch-statement -> switch [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) {[switch-cases](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) opt} +> switch-cases -> [switch-case](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-case) [switch-cases](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) opt +> switch-case -> [case-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label) [statements](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) | [default-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label) [statements](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) +> switch-case [switch-case](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label): | [default-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label): +> case-label -> case [case-item-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list): +> case-item-list -> [pattern](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [guard-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause) opt | [pattern](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [guard-clause](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause)opt , [case-item-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list) +> default-label -> default +> guard-clause -> where [guard-expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-expression) +> guard-expression -> [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) + + +##Labeled Statement## +##带标签的语句## + +You can prefix a loop statement or a switch statement with a statement label, which consists of the name of the label followed immediately by a colon (:). Use statement labels with break and continue statements to be explicit about how you want to change control flow in a loop statement or a switch statement, as discussed in Break Statement and Continue Statement below. + +你可以在循环语句和`switch`语句前面加上标签,它由标签名和`:`组成,在`break`和`continue`后面跟上标签名可以显式的在循环语句和switch语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,请参考[Break](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-XID_976) 语句和[Continue](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-XID_976) 语句 + +The scope of a labeled statement is the entire statement following the statement label. You can nest labeled statements, but the name of each statement label must be unique. + +For more information and to see examples of how to use statement labels, see Labeled Statements in the Control Flow chapter + +> labeld-statement -> [statement-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [loop-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement) | [statement-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [switch-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement) +> statement-label -> [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name): +> label-name -> [identifier](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier) + + + +标签的作用域是该标签所标记的语句之后的所有语句。你可以不使用带标签的语句,但只要使用它,标签名就必唯一。 + +关于使用带标签的语句的例子,请参考控制流一章的带标签的语句 + +> labeld-statement -> [statement-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [loop-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement) | [statement-label](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [switch-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement) +> statement-label -> [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name): +> label-name -> [identifier](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier) + + +##Control Transfer Statements## +##控制传递语句## + +Control transfer statements can change the order in which code in your program is executed by unconditionally transferring program control from one piece of code to another. Swift has four control transfer statements: a break statement, a continue statement, a fallthrough statement, and a return statement. + +> control-transfer-statement -> [break-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/break-statement) +> control-transfer-statement -> [continue-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/continue-statement) +> continue-statement -> [fallthrougth-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/fallthrough-statement) +> continue-statement -> [return-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/return-statement) + +能够无条件的把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码的执行顺序,Swift 提供四种类型的控制传递语句: `break`语句,`continue`语句,`fallthrough`语句和`return`语句 + +> control-transfer-statement -> [break-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/break-statement) +> control-transfer-statement -> [continue-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/continue-statement) +> continue-statement -> [fallthrougth-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/fallthrough-statement) +> continue-statement -> [return-statement](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/return-statement) + +##Break Statement## +##break语句## +A break statement ends program execution of a loop or a switch statement. A break statement can consist of only the keyword break, or it can consist of the keyword break followed by the name of a statement label, as shown below. + + break + break label name + +`break`用于终止循环或是`switch`语句,用break语句时,可以只写break这个关键词,也可以在break后面跟上标签名(label name),像下面这样: + + break + break label name + +When a break statement is followed by the name of a statement label, it ends program execution of the loop or switch statement named by that label. + +When a break statement is not followed by the name of a statement label, it ends program execution of the switch statement or the innermost enclosing loop statement in which it occurs. + +当`break`后面带有标签名时,可用于终止这个标签标记的循环或`switch`语句的执行 + +而当只有`break`关键词时,则会终止`switch`语句或上下文中包含`break` +的最内层的循环的执行 + +In both cases, program control is then transferred to the first line of code following the enclosing loop or switch statement, if any. + +For examples of how to use a break statement, see Break and Labeled Statements in the Control Flow chapter. + +> break-statement -> break [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name) opt + +在这两种情况下,控制权都会被传递给循环或switch语句外面的第一行语句 + +关于使用break语句的例子,请参考控制流一章的Break和带标签的语句 + +> break-statement -> break [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name) opt + +##Continue Statement## +##Continue 语句## +A continue statement ends program execution of the current iteration of a loop statement but does not stop execution of the loop statement. A continue statement can consist of only the keyword continue, or it can consist of the keyword continue followed by the name of a statement label, as shown below. + + continue + continue label name + +continue语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用continue语句时,和break一样,可以只写continue这个关键词,也可以在continue后面跟上标签名(label name),像下面这样: + + continue + continue label name + +When a continue statement is followed by the name of a statement label, it ends program execution of the current iteration of the loop statement named by that label. + +When a continue statement is not followed by the name of a statement label, it ends program execution of the current iteration of the innermost enclosing loop statement in which it occurs. + +当continue语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行 + +而当只写break时,可用于终止上下文中包含continue语句的最内层循环中当前迭代的执行 + +In both cases, program control is then transferred to the condition of the enclosing loop statement. + +In a for statement, the increment expression is still evaluated after the continue statement is executed, because the increment expression is evaluated after the execution of the loop’s body. + +For examples of how to use a continue statement, see Continue and Labeled Statements in the Control Flow chapter + +> continue-statement -> continue [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name)opt + +在这两种情况下,控制权都会被传递给循环外面的第一行语句。 + +在for语句中,continue语句执行后,increment表达式还是会被计算,这是因为每次循环体执行完毕后increment表达式都会被计算。 + +关于使用continue语句的例子,请参考控制流一章的Continue和带标签的语句 + +> continue-statement -> continue [label-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name)opt + + +##Fallthrough Statement## +##Fallthrough 语句## + +A fallthrough statement consists of the fallthrough keyword and occurs only in a case block of a switch statement. A fallthrough statement causes program execution to continue from one case in a switch statement to the next case. Program execution continues to the next case even if the patterns of the case label do not match the value of the switch statement’s control expression. + +A fallthrough statement can appear anywhere inside a switch statement, not just as the last statement of a case block, but it can’t be used in the final case block. It also cannot transfer control into a case block whose pattern contains value binding patterns. + +`fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个`case`无条件的传递给下一个case,即使下一个`case`的值与`switch`语句的控制表达式的值不匹配 + +For an example of how to use a fallthrough statement in a switch statement, see Control Transfer Statements in the Control Flow chapter. + +> fallthrough-statement -> fallthrough + +关于在switch语句中使用fallthrough语句的例子,请参考控制流一章的控制传递语句 + +> fallthrough-statement -> fallthrough + +##Return Statement## +##Return 语句## + +A return statement occurs only in the body of a function or method definition and causes program execution to return to the calling function or method. Program execution continues at the point immediately following the function or method call. + +A return statement can consist of only the keyword return, or it can consist of the keyword return followed by an expression, as shown below. + + return + return expression + +`return`用于在函数或是方法中,将控制权传递给调用者,接着程序将会从调用者的位置继续的往下执行 +使用return语句时,可以只写return这个关键词,也可以在return后面跟上表达式,像下面这样 + + return + return `expression` + +When a return statement is followed by an expression, the value of the expression is returned to the calling function or method. If the value of the expression does not match the value of the return type declared in the function or method declaration, the expression’s value is converted to the return type before it is returned to the calling function or method. + +当`return`后面跟有表达式时,会把表达式的值返回给调用者,如果表达式值的类型与调用者期望的类型不匹配,swift会在返回表达式的值之前把表达式值的类型转为调用者期望的类型 + +When a return statement is not followed by an expression, it can be used only to return from a function or method that does not return a value (that is, when the return type of the function or method is Void or ()). + +> return-statement -> return [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt + +而当只写return时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为Void或()) + +> return-statement -> return [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)opt diff --git a/src/chapter3/06_Attributes.md b/src/chapter3/06_Attributes.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/chapter3/06_Declarations.md b/src/chapter3/06_Declarations.md new file mode 100644 index 0000000..85e263f --- /dev/null +++ b/src/chapter3/06_Declarations.md @@ -0,0 +1,1208 @@ +# 声明 +A declaration introduces a new name or construct into your program. For example, you use declarations to introduce functions and methods, variables and constants, and to define new, named enumeration, structure, class, and protocol types. You can also use a declaration to extend the the behavior of an existing named type and to import symbols into your program that are declared elsewhere. + +声明将新的名字或者结构引入到程序中。例如,使用声明可以引入函数、方法、变量和常量;可以定义新的,命名的枚举类型,结构,类和协议类型。也可以使用声明来扩展已存在命名类型的行为,在程序中引入在其它地方声明的符号。 + +In Swift, most declarations are also definitions in the sense that they are implemented or initialized at the same time they are declared. That said, because protocols don’t implement their members, most protocol members are declarations only. For convenience and because the distinction isn’t that important in Swift, the term declaration covers both declarations and definitions. + +在Swift中,大多数声明在某种意义上也是定义,可以在声明的同时实现和初始化他们。也就是说,因为协议并不会实现他们的成员,所以大多数协议成员仅仅是声明。为了方便,由于这个不同在Swift里不是那么重要,因此术语declaration同时包含了声明和定义。 + +>GRAMMAR OF A DECLARATION + +>declaration → import-declaration­ + +>declaration → constant-declaration­ + +>declaration → variable-declaration­ + +>declaration → typealias-declaration­ + +>declaration → function-declaration­ + +>declaration → enum-declaration­ + +>declaration → struct-declaration­ + +>declaration → class-declaration­ + +>declaration → protocol-declaration­ + +> declaration → initializer-declaration­ + +>declaration → deinitializer-declaration­ + +> declaration → extension-declaration­ + +> declaration → subscript-declaration­ + +>declaration → operator-declaration­ + +>declarations → declaration­declarations­opt­ + +>declaration-specifiers → declaration-specifier­declaration-specifiers­opt­ + +>declaration-specifier → class­ | mutating ­| nonmutating­ | override­ | static­ | unowned + +## 模块范围 + +The module scope defines the code that’s visible to other code in Swift source files that are part of the same module. The top-level code in a Swift source file consists of zero or more statements, declarations, and expressions. Variables, constants, and other named declarations that are declared at the top-level of a source file are visible to code in every source file that is part of the same module。 + +模块范围定义了对模块中其它Swift源文件可见的代码。在Swift源文件中顶层的代码由0或多个语句、声明和表达式组成。在源文件的顶层声明的变量、常量和其它命名的声明对同一模块的其它源文件代码都是可见的。 + +>GRAMMAR OF A TOP-LEVEL DECLARATION + +>top-level-declaration → statements­opt + +Code Blocks + +代码块 + +A code block is used by a variety of declarations and control structures to group statements together. It has the following form: + +通过代码块把不同的声明和控制结构语句分组,有如下的形式: + + { + `statements` + } + +The statements inside a code block include declarations, expressions, and other kinds of statements and are executed in order of their appearance in source code. + +在代码块里的语句包括声明,表达式,和其它类型的语句,按照在源代码里出现的顺序执行。 + +>GRAMMAR OF A CODE BLOCK +>code-block → {­statements­opt­} + +##Import Declaration + +## Import 声明 + +An import declaration lets you access symbols that are declared outside the current file. The basic form imports the entire module; it consists of the import keyword followed by a module name: + + +import声明可以让你访问当前文件之外声明的符号。基本的形式是引入整个模块。由import关键字后面跟着一个模块名进行声明。 + + + import module + +Providing more detail limits which symbols are imported—you can specify a specific submodule or a specific declaration within a module or submodule. When this detailed form is used, only the imported symbol (and not the module that declares it) is made available in the current scope. + +如果提供更多的细节限制,还可以明确限制引入一个具体的子模块或者模块子模块里的一个具体的声明。如果采用这种形式的声明,只有被引入的符号(而不是声明它的模块)可以在当前范围里被访问到。 + + import import kind module.symbol name + import module.submodule + +>GRAMMAR OF AN IMPORT DECLARATION + +>import-declaration → attributes ­opt ­import­ import-kind­ opt import-path­ +>import-kind → typealias­ | struct­ | class­ | enum­ | protocol­ | var­ | func­ +>import-path → import-path-identifier­ import-path-identifier­.­import-path­ +>import-path-identifier → identifier­ operator + +##Constant Declaration + +##常量声明 + +A constant declaration introduces a constant named value into your program. Constant declarations are declared using the keyword let and have the following form: + +常量声明引入一个不变的命名值到程序中。常量声明使用关键字let,采用如下的形式: + + let constant name: type = expression + +A constant declaration defines an immutable binding between the constant name and the value of the initializer expression; after the value of a constant is set, it cannot be changed. That said, if a constant is initialized with a class object, the object itself can change, but the binding between the constant name and the object it refers to can’t. + +常量声明在常量名和初始化表达式的值之间定义了一个不可变的绑定;在值被设置之后,它就不能改变了。也就是说,如果常量用一个class对象实例化,对象本身可以改变,但是在常量名和对象之间的绑定是不会改变的。 + +When a constant is declared at global scope, it must be initialized with a value. When a constant declaration occurs in the context of a class or structure declaration, it is considered a constant property. Constant declarations are not computed properties and therefore do not have getters or setters. + +如果一个常量在全局范围里声明,它就必须有一个初始值。当一个常量在类或者结构声明中声明,它会被认为是一个常量属性。常量声明不是计算型属性,因此没有getters和setters方法。 + +If the constant name of a constant declaration is a tuple pattern, the name of each item in the tuple is bound to the corresponding value in the initializer expression. + +如果一个常量声明的名字是一个元组,元组中的每一项在初始化表达式里都会绑定到相应的值。 + + let (firstNumber, secondNumber) = (10, 42) + +In this example, firstNumber is a named constant for the value 10, and secondNumber is a named constant for the value 42. Both constants can now be used independently: + +在这个例子,firstNumer是一个命名常量,值是10。secondNumber是一个命名常量,值是4。这两个常量现在都可以独立的使用。 + + println("The first number is \(firstNumber).") + // prints "The first number is 10." + println("The second number is \(secondNumber).") + // prints "The second number is 42." + +The type annotation (: type) is optional in a constant declaration when the type of the constant name can be inferred, as described in Type Inference. + +如Type Inference里描述的一样,如果常量名的类型可以被推断出来,类型标识(:type)是可选的。 + +To declare a static constant property, mark the declaration with the static keyword. Static properties are discussed in Type Properties. + +为了声明一个静态的常量属性,使用关键字static来标记声明。静态属性会在Type Proerties里讨论。 + +For more information about constants and for guidance about when to use them, see Constants and Variables and Stored Properties + +想获得更多关于常量的信息或者想在使用中获得帮助,请查看常量和变量和存储属性(stored properties)等节。 + +>GRAMMAR OF A CONSTANT DECLARATION + +>constant-declaration → attributes­ opt ­declaration-specifiers­ opt ­let­pattern-initializer-list­ +>pattern-initializer-list → pattern-initializer­ | pattern-initializer­ , pattern-initializer-list­ +>pattern-initializer → pattern ­initializer ­opt­ +>initializer → =­expression + + +##Variable Declaration + +##变量声明 + +A variable declaration introduces a variable named value into your program and is declared using the keyword var. + +变量声明会引入一个可变的命名值到程序中,使用关键字var进行声明。 + +Variable declarations have several forms that declare different kinds of named, mutable values, including stored and computed variables and properties, stored variable and property observers, and static variable properties. The appropriate form to use depends on the scope at which the variable is declared and the kind of variable you intend to declare. + +变量声明有好几种不同的形式来声明不同类型的命名的,可变的值,包括存储型和计算型的变量和属性,存储型变量和属性观察者,还有静态的变量属性。要使用哪种适合的形式取决于变量声明的范围和你打算声明的变量种类。 + +>注意: + + +You can override a property in a subclass by prefixing the subclass’s property declaration with the override keyword, as described in Overriding. + +通过在子类中的属性声明的前面使用override,可以在子类中重写一个属性。 + + +##Stored Variables and Stored Variable Properties + +##存储型变量和存储型变量属性 + +The following form declares a stored variable or stored variable property: + +下面的形式声明了一个存储型变量或存储型的变量属性 + + var variable name: type = expression + +You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class or structure declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, it is referred to as a stored variable. When it is declared in the context of a class or structure declaration, it is referred to as a stored variable property. + +你可以在全局范围、函数的局部范围或者类或结构声明的上下文环境里定义变量声明。如果这种形式的变量声明在全局范围或者函数局部范围里声明,它指的就是存储型变量。如果是在类或者结构声明的环境里声明的,它就是指的存储型变量属性。 + +The initializer expression can’t be present in a protocol declaration, but in all other contexts, the initializer expression is optional. That said, if no initializer expression is present, the variable declaration must include an explicit type annotation (: type). + +初始化表达式不能在协议声明里出现,但是可以出现在其它所有的上下文环境里。初始化表达式是可选的。也就是说,如果没有初始化表达式存在,变量声明必须包括一个显式的类型表示(:type)。 + +As with constant declarations, if the variable name is a tuple pattern, the name of each item in the tuple is bound to the corresponding value in the initializer expression. + +和常量声明一样,如果变量名字是一个元组,元组中每一项的名字都会被绑定到初始化表达式的对应的值。 + + +As their names suggest, the value of a stored variable or a stored variable property is stored in memory. + +顾名思义,存储型变量或者存储型属性的值是存储在内存中的。 + +##Computed Variables and Computed Properties + +##计算型变量和计算型属性 + +The following form declares a computed variable or computed property: + +下面的形式声明了一个计算型变量或者计算型属性 + + var variable name: type { + get { + statements + } + set(setter name) { + statements + } + } +You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class, structure, enumeration, or extension declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, it is referred to as a computed variable. When it is declared in the context of a class, structure, or extension declaration, it is referred to as a computed property. + +可以在全局范围,局部范围,或者类、结构、枚举或者扩展声明里定义这种形式的声明。如果这类型是的变量声明在全局范围或者函数的局部范围声明的,它指的是计算型变量。如果它在类,结构或者扩展声明里声明的,它指的就是计算型属性。 + + +The getter is used to read the value, and the setter is used to write the value. The setter clause is optional, and when only a getter is needed, you can omit both clauses and simply return the requested value directly, as described in Read-Only Computed Properties. But if you provide a setter clause, you must also provide a getter clause. + +getter用于读取值,setter用于写入值。setter子句是可选的,仅仅getter方法是必须的,你可以把两者都忽略,只是简单的直接返回请求值,如在Read-Only Comuted Properties描述的一样。但是如果你提供一个setter子句,你也必须要提供一个getter子句。 + +The setter name and enclosing parentheses is optional. If you provide a setter name, it is used as the name of the parameter to the setter. If you do not provide a setter name, the default parameter name to the setter is newValue, as described in Shorthand Setter Declaration. + +setter名字和封闭的括号是可选的。如果你提供了一个setter名字。如果你提供一个setter名字,它会被作为setter的参数的名称。如果你美欧提供setter名称,setter缺省的参数名是newvalue,如在Shorthand Setter Declaration描述的那样. + +Unlike stored named values and stored variable properties, the value of a computed named value or a computed property is not stored in memory. + +不像存储型命名值和存储型变量属性,计算型命名值或者计算型属性的值不会存储到内存中。 + +For more information and to see examples of computed properties, see Computed Properties. + +获得更多信息和如何使用属性监视器的例子,请查看属性监视器(prpperty observers)一节。 + +##Stored Variable Observers and Property Observers + +##存储型变量监视器和属性监视器 + +You can also declare a stored variable or property with willSet and didSet observers. A stored variable or property declared with observers has the following form: + +也可以使用willset和didset监视器声明一个存储型的变量或者属性。用监视器声明的存储型变量或者属性有以下的形式: + + var variable name: type = expression { + willSet(setter name) { + statements + } + didSet(setter name) { + statements + } + } + +You define this form of a variable declaration at global scope, the local scope of a function, or in the context of a class or structure declaration. When a variable declaration of this form is declared at global scope or the local scope of a function, the observers are referred to as stored variable observers. When it is declared in the context of a class or structure declaration, the observers are referred to as property observers. + +可以在全局范围,函数的局部返回,或类,结构的上下文环境里声明这种形式的变量。当这种类型是在在全局范围或者函数的局部范围里声明的时候,监视器会被作为存储型变量监视器。当在类或者结构声明中声明的时候,监视器会被作为属性监视器。 + +You can add property observers to any stored property. You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass, as described in Overriding Property Observers. + +可以为任何存储型属性增加监视器。也可以为继承的属性通过使用子类覆盖增加属性监视器(无论是存储型的或是计算型的),就像Overriding PropertyOververs里描述的一样。 + +The initializer expression is optional in the context of a class or structure declaration, but required elsewhere. The type annotation is required in all variable declarations that include observers, regardless of the context in which they are declared. + +在类或者结构声明里,初始化表达式是可选的。但是在其它所有地方是必须的。类型标识在所有变量声明里是必须的,包括监视器,无论他们声明的环境。 + +The willSet and didSet observers provide a way to observe (and to respond appropriately) when the value of a variable or property is being set. The observers are not called when the variable or property is first initialized. Instead, they are called only when the value is set outside of an initialization context. + +willset 和 didset 监视器提供了一种方式来观察(适当的响应)什么时候变量或者属性的值被设置。监视器在变量或属性在第一次初始化的时候不会被调用,仅仅在初始化环境的外边设置的时候会被调用。 + +A willSet observer is called just before the value of the variable or property is set. The new value is passed to the willSet observer as a constant, and therefore it can’t be changed in the implementation of the willSet clause. The didSet observer is called immediately after the new value is set. In contrast to the willSet observer, the old value of the variable or property is passed to the didSet observer in case you still need access to it. That said, if you assign a value to a variable or property within its own didSet observer clause, that new value that you assign will replace the one that was just set and passed to the willSet observer. + +willset监视器只是在变量或是属性值被设置的时候调用。新值会被作为常量传递给willset语句,因此在willset子句实现的时候不能改变。didSet监视器在新值被设置的时候会立即被调用。与willset监视器相比,变量或属性的旧值会传给didSet监视器,以应对万一需要访问它 + +The setter name and enclosing parentheses in the willSet and didSet clauses are optional. If you provide setter names, they are used as the parameter names to the willSet and didSet observers. If you do not provide setter names, the default parameter name to the willSet observer is newValue and the default parameter name to the didSet observer is oldValue. + +在willset和didset语句里 setter名字和括号是可选的。如果你提供你不提供setter名字,willset 缺省的参数名是newValue好缺省的参数名是oldValue. + +The didSet clause is optional when you provide a willSet clause. Likewise, the willSet clause is optional when you provide a didSet clause. + +如果你提供一个willset语句,didset语句是可选的。同样的,如果你提供一个didset 语句,willset语句是可选的 + +For more information and to see an example of how to use property observers, see Property Observers. + +获得更多信息,查看如何使用属性监视器的例子,请查看属性监视器(prpperty observers)一节。 + +##Class and Static Variable Properties + +##类和静态变量属性 + +To declare a class computed property, mark the declaration with the class keyword. To declare a static variable property, mark the declaration with the static keyword. Class and static properties are discussed in Type Properties. + +要声明一个类的计算型属性,要用关键字class标记声明。声明静态的变量属性,用关键字static标记。类和静态属性在Type属性里有讨论。 + +>GRAMMAR OF A VARIABLE DECLARATION + +>variable-declaration → variable-declaration-head­pattern-initializer-list­ + +>variable-declaration → variable-declaration-head ­variable-name ­type-annotation ­code-block­ + +>variable-declaration → variable-declaration-head ­variable-name ­type-annotation ­getter-setter-block­ + +>variable-declaration → variable-declaration-head ­variable-name­ type-annotation ­getter-setter-keyword-block­ + + > variable-declaration → variable-declaration-head­ variable-name ­type-annotation­initializer­ opt ­willSet-didSet-block­ + +>variable-declaration-head → attributes ­opt­ declaration-specifiers ­opt ­var +­ +>variable-name → identifier­ + +>getter-setter-block → {­getter-clause ­setter-clause­ opt­}­ + +>getter-setter-block → {­setter-clause ­getter-clause­}­ + +>getter-clause → attributes ­opt­get­code-block­ + +>setter-clause → attributes ­opt ­set­ setter-name­ opt­ code-block­ + +>setter-name → (­identifier­)­ + +>getter-setter-keyword-block → {­getter-keyword-clause ­setter-keyword-clause­ opt­} +­ +>getter-setter-keyword-block → {­setter-keyword-clause ­getter-keyword-clause­} + +>getter-keyword-clause → attributes­ opt­ get­ + +>setter-keyword-clause → attributes ­opt­ set­ + +>willSet-didSet-block → {­willSet-clause ­didSet-clause ­opt­}­ + +>willSet-didSet-block → {­didSet-clause ­willSet-clause­}­ + +>willSet-clause → attributes ­opt ­willSet ­setter-name­ opt ­code-block­ + +>didSet-clause → attributes ­opt ­didSet ­setter-name ­opt­ code-bloc + + +##Type Alias Declaration + +##类型的别名声明 + +A type alias declaration introduces a named alias of an existing type into your program. Type alias declarations begin with the keyword typealias and have the following form: + +类型别名声明引入了存在类型的名字到程序中,以关键字typelias开头,采用如下的形式: + + typealias name = existing type + +After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases do not create new types; they simply allow a name to refer to an existing type. + +在声明一个类型别名后,那个别名可以使用在你的程序的的任何有那个类型的地方使用了。存在的类型可能是命名类型或是复合类型。类型 别名不是创造新类型,它们只是简单的把名字指向一个存在的类型。 + +See also Protocol Associated Type Declaration. + +也可以参看Protocol Associated Type Declaration. + +>GRAMMAR OF A TYPE ALIAS DECLARATION + + +> typealias-declaration → typealias-head­ typealias-assignment +> typealias-head → typealias­ typealias-name +> typealias-name → identifier +> typealias-assignment → =type + +##Function Declaration + +##函数声明 + +A :newTerm`function declaration` introduces a function or method into your program. A function declared in the context of class, structure, enumeration, or protocol is referred to as a method. Function declarations are declared using the keyword func and have the following form: + +新术语”函数声明“引入了一个函数或者方法到程序中。在类,结构,枚举或者协议里声明的函数会被当做方法。函数声明使用关键字func,采用以下的形式。 + + func function name(parameters) -> return type { + statements + } + +If the function has a return type of Void, the return type can be omitted as follows: + +如果函数的返回类型是void,返回类型可像下面这样忽略: + +func function name(parameters) { + statements +} + +The type of each parameter must be included—it can’t be inferred. By default, the parameters to a function are constants. Write var in front of a parameter’s name to make it a variable, scoping any changes made to the variable just to the function body, or write inout to make those changes also apply to the argument that was passed in the caller’s scope. For a discussion of in-out parameters, see In-Out Parameters. + +必须包含每个参数的类型-参数类型不能通过推断。默认情况下,函数的参数是常量。在参数名的前面写一个var会让它成为一个变量,对变量的做的任何变化只会在函数内有效,或者用inout使的这些改变可以在调用域内生效。 + +Functions can return multiple values using a tuple type as the return type of the function. + +函数可以使用元组类型作为函数的返回类型来返回多个值。 + +A function definition can appear inside another function declaration. This kind of function is known as a nested function. For a discussion of nested functions, see Nested Functions. + +函数定义可以出现在另一个函数声明之内。这种函数叫做嵌入函数。对于嵌入函数的讨论,请参见Nested Function。 + +Parameter Names + +Function parameters are a comma separated list where each parameter has one of several forms. The order of arguments in a function call must match the order of parameters in the function’s declaration. The simplest entry in a parameter list has the following form: + +函数参数是一个逗号分隔的列表,每个参数可以有好几种形式。函数调用的时候参数的顺序必须匹配函数声明的参数顺序。在参数列表里最简单的输入形式: + + parameter name: parameter type + + +For function parameters, the parameter name is used within the function body, but is not used when calling the function. For method parameters, the parameter name is used as within the function body, and is also used as a label for the argument when calling the method. The name of a method’s first parameter is used only within the function body, like the parameter of a function. For example: + +对于函数参数,参数名字在函数体内部使用,但是在调用函数的时候不会使用。对于方法参数,参数名可以在函数体使用,在调用方法时也可以作为参数的标签使用。函数的一个参数名字仅仅在函数内使用,就像函数的参数。例如 + + + func f(x: Int, y: String) -> String { + return y + String(x) + } + f(7, "hello") // x and y have no name + + class C { + func f(x: Int, y: String) -> String { + return y + String(x) + } + } + let c = C() + c.f(7, y: "hello") // x没有名称,y有名称 + + +You can override the default behavior for how parameter names are used with one of the following forms: + +可以重写参数名字使用的默认行为,采用如下的形式: + + + external parameter name local parameter name: parameter type + #parameter name: parameter type + _ local parameter name: parameter type + + +A second name before the local parameter name gives the parameter an external name, which can be different than the local parameter name. The external parameter name must be used when the function is called. The corresponding argument must have the external name in function or method calls. + +本地参数名前的第二个名字给参数了一个外部的名字,这个不同于本地的参数名。外部的参数名必须在函数调用的时候调用。相应的参数必须在函数或者方法调用时有外部名字。 + +A hash symbol (#) before a parameter name indicates that the name should be used as both an external and a local parameter name. It has the same meaning as writing the local parameter name twice. The corresponding argument must have this name in function or method calls. + +参数名前的#显示了名字应该同时被作为外部和本地参数使用。它和写入本地参数名两次有同样的意义。相应的参数在函数或者参数调用时必须有这个名字。 + +An underscore (_) before a local parameter name gives that parameter no name to be used in function calls. The corresponding argument must have no name in function or method calls. + +本地参数名前面的下划线在函数调用的时候让参数可以没有名字。相应的参数在函数或者方法调用的时候没有名字。 + +##Special Kinds of Parameters + +##特殊类型的参数 + +Parameters can be ignored, take a variable number of values, and provide default values using the following forms: + +参数可以忽略,需要不同数量的值,会提供默认值,使用如下的形式: + + + _ : <#parameter type#. + parameter name: parameter type... + parameter name: parameter type = default argument value + + +A parameter named with an underscore (_) is explicitly ignored an can’t be accessed within the body of the function. + +带有下划线的参数会显式的被忽略,在函数内部不能被访问到。 + +A parameter with a base type name followed immediately by three dots (...) is understood as a variadic parameter. A function can have at most one variadic parameter, which must be its last parameter. A variadic parameter is treated as an array that contains elements of the base type name. For instance, the variadic parameter Int... is treated as Int[]. For an example that uses a variadic parameter, see Variadic Parameters. + +基本类型名的参数,如果紧跟着三个点(...),被理解为是可变参数。一个函数最多可以有一个可变参数,且必须是最后一个参数。可变参数被作包含基本类型的元素数组。举例来讲,可变参数int...被看做是int[]。 查看可变参数的使用例子,详见可变参数(variadic parameters)一节。 + +A parameter with an equals sign (=) and an expression after its type is understood to have a default value of the given expression. If the parameter is omitted when calling the function, the default value is used instead. If the parameter is not omitted, it must have its name in the function call. For example, f() and f(x: 7) are both valid calls to a function with a single default parameter named x, but f(7) is invalid because it provides a value without a name. + +带有等号(=)的参数和它的类型后面的表达式会被当作给定表达式的默认类型。如果参数在调用函数的时候被忽略,默认值会被使用。例如,对于带有单个默认的的参数名x的函数,f()和f(x:7)都是有效的调用,但是f(7)是有效的,因为它提供了没有名字的值。 + +Special Kinds of Methods on an enumeration or a structure that modify self must be marked with the mutating keyword at the start of the function declaration. + +会修改self的枚举类型或者结构上的方法,必须在函数声明的开始标记为mutating关键字 + +Methods that override a superclass method must be marked with the override keyword at the start of the function declaration. It is an error to override a method without the override keyword or to use the overridekeyword on a method that doesn’t override a superclass method. + +重写超类方法的方法必须在函数声明的开头override关键词标记。没有override就重写方法会产生错误或者使用override在没有重载超类的方法 + +Methods associated with a type rather than an instance of a type must be marked with the static attribute for enumerations and structures or the class attribute for classes. + +与type相关而不是类型实例相关的方法必须使用使用static属性来标记枚举、结构或者类的class属性。 + +##Curried Functions and Methods + +##科里化函数和方法 + +Curried functions and methods have the following form: + +科里化函数和方法有以下的形式: + + + func function name(parameters)(parameters) -> return type { + statements + } + +以这种形式定义的函数的返回值是另一个函数。举例来说,下面的两个声明时等价的: + + func addTwoNumbers(a: Int)(b: Int) -> Int { + return a + b + } + func addTwoNumbers(a: Int) -> (Int -> Int) { + func addTheSecondNumber(b: Int) -> Int { + return a + b + } + return addTheSecondNumber + } + + addTwoNumbers(4)(5) // Returns 9 + +多级柯里化应用如下 + +>GRAMMAR OF A FUNCTION DECLARATION + +>function-declaration → function-head­ function-name­ generic-parameter-clause ­opt­function-signature­ function-body­ +> function-head → attributes ­opt ­declaration-specifiers ­opt ­func­ +> function-name → identifier­ operator­ +>function-signature → parameter-clauses ­function-result ­opt­ +> function-result → ->­attributes ­opt ­type­ +> function-body → code-block­ +> parameter-clauses → parameter-clause ­parameter-clauses ­opt­ +> parameter-clause → (­)­ (­parameter-list­...­opt­)­ +> parameter-list → parameter­ parameter­,­parameter-list­ +> parameter → inout ­opt ­let ­opt­#­opt­parameter-name local-parameter-name ­opt­ type-annotation ­default-argument-clause ­opt­ +> parameter → inout­opt­var­#­opt­parameter-name­local-parameter-name ­opt­ type-annotation­default-argument-clause ­opt­ +> parameter → attributes ­opt ­type­ +> parameter-name → identifier­ _­ +> local-parameter-name → identifier­ _­ +> default-argument-clause → =­expression­: + +A function declared this way is understood as a function whose return type is another function. For example, the following two declarations are equivalent: + +用这种方式声明的函数会被当做一个函数,它的返回类型是另外一个函数。例如,下面2个声明是一样的: + + +##Enumeration Declaration + +##枚举声明 + +An enumeration declaration introduces a named enumeration type into your program. + +枚举声明引入了名叫枚举的类型程序中。 + + +Enumeration declarations have two basic forms and are declared using the keyword enum. The body of an enumeration declared using either form contains zero or more values—called enumeration cases—and any number of declarations, including computed properties, instance methods, static methods, initializers, type aliases, and even other enumeration, structure, and class declarations. Enumeration declarations can’t contain destructor or protocol declarations. + +枚举声明有2种基本的形式,使用关键字enum声明。一个枚举类型的的主体会使用这2种形式之一,包含0或多个值-被称作枚举类型。还有任何数量的声明,包括计算型的属性,实例方法,静态方法,初始化,类型别名,和其它的枚举、结构和类声明。枚举声明不能包含析构或者协议声明。 + +Unlike classes and structures, enumeration types do not have an implicitly provided default initializer; all initializers must be declared explicitly. Initializers can delegate to other initializers in the enumeration, but the initialization process is complete only after an initializer assigns one of the enumeration cases to self. + +不像类或者结构,枚举类型没有一个默认的初始化,但是初始化过程仅仅在初始化赋值给枚举类型的一个给self的时候。 + +Like structures but unlike classes, enumerations are value types; instances of an enumeration are copied when assigned to variables or constants, or when passed as arguments to a function call. For information about value types, see Structures and Enumerations Are Value Types. + +类似结构但是不像类,枚举是值类型;一个枚举实例在赋值给变量或者常量的时候会被复制,或者在传递参数给函数调用的的时候。 + +You can extend the behavior of an enumeration type with an extension declaration, as discussed in Extension Declaration. + + +可以使用extension声明扩展枚举类型的行为,如果Extension Declaration Enumerations with Cases of Any Type描述的那样。 + +The following form declares an enumeration type that contains enumeration cases of any type: + +下面的形式声明了一个枚举类型,包含了所有类型的枚举。 + + + enum enumeration name { + case enumeration case 1 + case enumeration case 2(associated value types) + } + + + +Enumerations declared in this form are sometimes called discriminated unions in other programming languages. + +用这种形式声明的枚举在其它语言里有时候被叫做discriminated unions + +In this form, each case block consists of the keyword case followed by one or more enumeration cases, separated by commas. The name of each case must be unique. Each case can also specify that it stores values of a given type. These types are specified in the associated value types tuple, immediately following the name of the case. For more information and to see examples of cases with associated value types, seeAssociated Values. + +用这种方式,每个cast块由关键字case组成,case后跟着一个或者多个逗号分隔的枚举case。每个case的名字必须是唯一的。每个case也能够确定它存储一个给定类型的值。这些类型用关联值类型元组来设置,后面紧跟着case的名字。获取更多信息,或关联值类型的的例子,请看Assiciated Values Enumerations with Raw Cases Values + +The following form declares an enumeration type that contains enumeration cases of the same basic type: + +下面的形式声明了一种枚举类型,包含同样基本类型的枚举case + + + enum enumeration name: raw value type { + case enumeration case 1 = raw value 1 + case enumeration case 2 = raw value 2 + } + + + +In this form, each case block consists of the keyword case, followed by one or more enumeration cases, separated by commas. Unlike the cases in the first form, each case has an underlying value, called a raw value, of the same basic type. The type of these values is specified in the raw value type and must represent a literal integer, floating-point number, character, or string. + +通过这种形式,每种caes块是由关键字case后面跟着一个或者多个由多个逗号分隔的枚举case。不像第一种形式的case,每个case有一个underlying值,叫做原生值。这些值的类型在rsw值的类型设置,必须表示一个字面量整数,浮点数,字符和字符串。 + +Each case must have a unique name and be assigned a unique raw value. If the raw value type is specified as Int and you don’t assign a value to the cases explicitly, they are implicitly assigned the values 0, 1, 2, and so on. Each unassigned case of type Int is implicitly assigned a raw value that is automatically incremented from the raw value of the previous case. + +每个case必须有一个唯一的名字,然后被赋值为一个单独的原生值。如果原生值用Int设置,你不需要要显式的赋值,他们可以默认的赋值1,1,2等等。每个没有赋值的Int类型会被赋值为原生类型,可以自动的从之前的case的原生值递增。 + + + enum ExampleEnum: Int { + case A, B, C = 5, D + } + + +In the above example, the value of ExampleEnum.A is 0 and the value of ExampleEnum.B is 1. And because the value of ExampleEnum.C is explicitly set to 5, the value of ExampleEnum.D is automatically incremented from 5 and is therefore 6. + +在上面的例子中,ExampleEnum。A的值是o.ExampleEnum.B值是1。因为ExampleEnum.C是显式的设置为5,ExampleEnumd.D的值会从5自动的增加,所以是6. + +The raw value of an enumeration case can be accessed by calling its toRaw method, as inExampleEnum.B.toRaw(). You can also use a raw value to find a corresponding case, if there is one, by calling the fromRaw method, which returns an optional case. For more information and to see examples of cases with raw value types, see Raw Values. + +枚举类型的原生值可以通过toRaw方法访问,比如ExampleEnum.B.toRaw()。你也可以使用一个原生值来找到对应的case通过调用fromRaw方法。 + +##Accessing Enumeration Cases + +##访问枚举case + +To reference the case of an enumeration type, use dot (.) syntax, as in EnumerationType.EnumerationCase. When the enumeration type can be inferred from context, you can omit it (the dot is still required), as described in Enumeration Syntax and Implicit Member Expression. + +和EnumerationType.EnumerationCase一样,可以使用语法.来引用一个enumeration类型的case,就像inEnumeration Syntax and Implicit Member Expression所描述的。 + +To check the values of enumeration cases, use a switch statement, as shown in Matching Enumeration Values with a Switch Statement. The enumeration type is pattern-matched against the enumeration case patterns in the case blocks of the switch statement, as described in Enumeration Case Pattern. + +使用switch语句来检验枚举事件的值,如使用switch语句匹配枚举值(Matching Enumeration Values with a Switch Statement)一节描述的那样。 + +>GRAMMAR OF AN ENUMERATION DECLARATION + +> enum-declaration → attributes­opt­union-style-enum­ attributes­opt­raw-value-style-enum­ +> union-style-enum → enum-name­generic-parameter-clause­opt­{­union-style-enum-members­opt­}­ + union-style-enum-members → union-style-enum-member­union-style-enum-members­opt­ + union-style-enum-member → declaration­ union-style-enum-case-clause­ + union-style-enum-case-clause → attributes­opt­case­union-style-enum-case-list­ + union-style-enum-case-list → union-style-enum-case­ union-style-enum-case­,­union-style-enum-case-list­ + union-style-enum-case → enum-case-name­tuple-type­opt­ + enum-name → identifier­ + enum-case-name → identifier­ + raw-value-style-enum → enum-name­generic-parameter-clause­opt­:­type-identifier­{­raw-value-style-enum-members­opt­}­ + raw-value-style-enum-members → raw-value-style-enum-member­raw-value-style-enum-members­opt­ + raw-value-style-enum-member → declaration­ raw-value-style-enum-case-clause­ + raw-value-style-enum-case-clause → attributes­opt­case­raw-value-style-enum-case-list­ + raw-value-style-enum-case-list → raw-value-style-enum-case­ raw-value-style-enum-case­,­raw-value-style-enum-case-list­ + raw-value-style-enum-case → enum-case-name­raw-value-assignment­opt­ + raw-value-assignment → =­literal­ + +##Structure Declaration + +##结构声明 + +A structure declaration introduces a named structure type into your program. Structure declarations are declared using the keyword struct and have the following form: + +结构声明引入了命名的类型到程序中。结构声明使用关键字struct,采用如下的形式: + + struct structure name: adopted protocols { + declarations + } + + +The body of a structure contains zero or more declarations. These declarations can include both stored and computed properties, static properties, instance methods, static methods, initializers, type aliases, and even other structure, class, and enumeration declarations. Structure declarations can’t contain destructor or protocol declarations. For a discussion and several examples of structures that include various kinds of declarations, see Classes and Structures. + +结构体包含0或者更多的声明。这个声明能包括存储型和计算型的属性,静态属性,示例方法,静态方法,构造器,类型别名,甚至其它结构,类和枚举声明。结构声明不能包含析构或者协议声明。在Classes and Structures里,包括好几种了不同类型的声明的结构例子。 + +Structure types can adopt any number of protocols, but can’t inherit from classes, enumerations, or other structures. + +结构类型可以采用任何数量的协议,但是不能从classes,枚举或者它结构继承。 + +There are three ways create an instance of a previously declared structure: + +有3种方式创建过去声明过的示例结构的示例。 + + +- Call one of the initializers declared within the structure, as described in Initializers. + 就像Initializers描述的,调用结构里的一个初始器之一。 + +- If no initializers are declared, call the structure’s memberwise initializer, as described in Memberwise Initializers for Structure Types. + 如果没有初始器被声明,就可以调用结构区的成员 + +- If no initializers are declared, and all properties of the structure declaration were given initial values, call the structure’s default initializer, as described in Default Initializers. + 如果没有初始化器被声明,结构所有其他声明的属性就会被给予初始化的值,调用结构的某人初始化器,正如Default Initializers .描述的那样。 + + + +The process of initializing a structure’s declared properties is described in Initialization. + +初始化一个结构的声明的属性的过程在Initialization描述。 + +Properties of a structure instance can be accessed using dot (.) syntax, as described in Accessing Properties. + +一个结构实例的属性使用通过.语来访问。 + +Structures are value types; instances of a structure are copied when assigned to variables or constants, or when passed as arguments to a function call. For information about value types, see Structures and Enumerations Are Value Types. + +结构是值类型;一个结构的实例在赋值给变量或者常量或者在传递参数给函数调用的时候采用复制的形式。关于值类型的信息,请参看Structures and Enumerations Are Value Types. + +You can extend the behavior of a structure type with an extension declaration, as discussed in Extension Declaration. + +你可以用一个扩展声明来扩展结构类型的行为,正如Extension Declaration讨论的。 + +>GRAMMAR OF A STRUCTURE DECLARATION + +> struct-declaration → attributes­opt­struct­struct-name­generic-parameter-clause­opt­type-inheritance-clause­opt­struct-body­ +> struct-name → identifier­ +> struct-body → {­declarations­opt­} + +##Class Declaration + +##类声明 + + +A class declaration introduces a named class type into your program. Class declarations are declared using the keyword class and have the following form: + +类型声明引入了命名的类型到程序中。类声明使用关键字class声明,采用下面的形式: + + + class class name: superclass, adopted protocols { + declarations + } + + +The body of a class contains zero or more declarations. These declarations can include both stored and computed properties, instance methods, class methods, initializers, a single destructor method, type aliases, and even other class, structure, and enumeration declarations. Class declarations can’t contain protocol declarations. For a discussion and several examples of classes that include various kinds of declarations, see Classes and Structures. + +一个class的主题会包含0或多个声明。这些声明可以包括存储型和计算型的属性,实例方法,类方法,构造器,单个析构函数,类型 别名,和甚至其它的class,结构,枚举声明。类声明不能包括协议声明。详细讨论和例子请看 Classes and Structures + +A class type can inherit from only one parent class, its superclass, but can adopt any number of protocols. The superclass appears first in the type-inheritance-clause, followed by any adopted protocols. +一个class类只能从一个父类中继承,但是可以有多个协议。超类首先出现在类型继承语句中,后面跟着采用的协议。 + +As discussed in Initializer Declaration, classes can have designated and convenience initializers. When you declare either kind of initializer, you can require any subclass to override it by marking the initializer with therequired attribute. The designated initializer of a class must initialize all of the class’s declared properties and it must do so before calling any of its superclass’s designated initializers. + +正如在初始化声明讨论的一样,类可以有指定和便利的构造器。当你声明某种构造器时,可以使用required属性要求所有子类重写。这个特定的构造器必须初始化类的所有声明的属性。在调用任何超类的特定的构造器时都必须这么做。 + +A class can override properties, methods, and initializers of its superclass. Overridden methods and properties must be marked with the override keyword. + +类可以重写它超类的属性,方法和构造器。重写的方法和属性必须都使用override来标记。 + +Although properties and methods declared in the superclass are inherited by the current class, designated initializers declared in the superclass are not. That said, if the current class overrides all of the superclass’s designated initializers, it inherits the superclass’s convenience initializers. Swift classes do not inherit from a universal base class. + +尽管在子类中声明的属性和方法可以被当前的类继承,但是在超类中指定的构造器却不可以。也就是说,如果当前类重写了超类的所有指定的构造器,它就会继续超类的便利构造器。Swift类不会从通用的基类中继承。 + +There are two ways create an instance of a previously declared class: + +有两种方式创建过去声明的类的实例: + +- Call one of the initializers declared within the class, as described in Initializers. + 在类的内部调用声明的初始化器。 +- If no initializers are declared, and all properties of the class declaration were given initial values, call the class’s default initializer, as described in Default Initializers. + 如果没有声明的初始化器,类声明的素有属性会有一个初始化值,是通过调用类的默认的初始化器来完成的。正如Default Initializers描述的。 + + + +Access properties of a class instance with dot (.) syntax, as described in Accessing Properties. + +使用.语法访问类的属性,如Accessing Properties描述的一样。 + + +Classes are reference types; instances of a class are referred to, rather than copied, when assigned to variables or constants, or when passed as arguments to a function call. For information about reference types, see Structures and Enumerations Are Value Types. + +类是引用类型;在被赋值给变量或常量的时候,或者当被作为参数传递给函数调用的时候,类的实例会被引用而不是被复制。更多信息,参考Structures and Enumerations Are Value Types.。 + + +You can extend the behavior of a class type with an extension declaration, as discussed in Extension Declaration. + +可以使用extension声明来扩展类的行为,如Extension Desclaration描述的那样. + + + > GRAMMAR OF A CLASS DECLARATION + > class-declaration → attributes­opt­class­class-name­generic-parameter-clause­opt­type-inheritance-clause­opt­class-body­ + > class-name → identifier­ + > class-body → {­declarations­opt­} + +##Protocol Declaration + +##协议声明 + +A protocol declaration introduces a named protocol type into your program. Protocol declarations are declared using the keyword protocol and have the following form: + +协议声明引入命名的协议类型到程序中,用关键字protocol来声明,有如下的形式: + + + protocol protocol name: inherited protocols { + protocol member declarations + } + +The body of a protocol contains zero or more protocol member declarations, which describe the conformance requirements that any type adopting the protocol must fulfill. In particular, a protocol can declare that conforming types must implement certain properties, methods, initializers, and subscripts. Protocols can also declare special kinds of type aliases, called associated types, that can specify relationships among the various declarations of the protocol. The protocol member declarations are discussed in detail below. + +协议的主体包含0或多个协议成员声明,它描述了任何实现它的协议都必须满足的一致性要求。特别是,一致性的协议必须实现某个属性,方法,构造器,和附属脚本。协议也能声明特殊种类的类型别名,被、称作关联类型,设置协议里的不同声明的关系。下面会对协议成员声明会做详细的讨论 + +Protocol types can inherit from any number of other protocols. When a protocol type inherits from other protocols, the set of requirements from those other protocols are aggregated, and any type that inherits from the current protocol must conform to all those requirements. For an example of how to use protocol inheritance, see Protocol Inheritance. + +协议类型可以继承任意数量的其它协议。如果一个协议类型从其它协议继承时任何从当前协议继承的类型都必须和从那些协议中的要求集合一致。如何使用协议继承,请查看Protocol Inheritance. + + +NOTE +注意 + +You can also aggregate the conformance requirements of multiple protocols using protocol composition types, as described in Protocol Composition Type and Protocol Composition. + +你也可以使用协议符合类型聚合多个协议的一致性要求。 + +You can add protocol conformance to a previously declared type by adopting the protocol in an extension declaration of that type. In the extension, you must implement all of the adopted protocol’s requirements. If the type already implements all of the requirements, you can leave the body of the extension declaration empty. + +你可以增加协议conformance到任何一个过去声明的类型通过在那个类型的extendsion中声明。在这个扩展里,你必须实现所有使用的协议的需求。如果那个类型已经实现了所有的要求,扩展声明的主题可以是空的。 + +By default, types that conform to a protocol must implement all properties, methods, and subscripts declared in the protocol. That said, you can mark these protocol member declarations with the optional attribute to specify that their implementation by a conforming type is optional. The optional attribute can be applied only to protocols that are marked with the objc attribute. As a result, only class types can adopt and conform to a protocol that contains optional member requirements. For more information about how to use the optionalattribute and for guidance about how to access optional protocol members—for example, when you’re not sure whether a conforming type implements them—see Optional Protocol Requirements. + +默认情况下,与协议一致的类型必须实现所有的属性,方法,和声明的附属脚本。也就是说,你可以用opaional属性来标记协议成员,表示他们的实现是可选的。option属性仅仅能被应用到objc标记的属性。结构,仅仅class类型能采纳和玉包含可选的成员变量的协议一致。对于如何访问可选的协议成员-例如,如果你不确定一个一致性类型是否要实现-查看一下 Optional Protocol Requirements. + +To restrict the adoption of a protocol to class types only, mark the entire protocol declaration with theclass_protocol attribute. Any protocol that inherits from a protocol marked with the class_protocol attribute can likewise be adopted only by a class type. + +为了限制协议的的只应用到某个class type,用 class_protocol属性标记整个协议声明。任何从标记class_protocol 协议继承的协议可以只被class类型采纳。 + +NOTE +注意 + +If a protocol is already marked with the objc attribute, the class_protocol attribute is implicitly applied to that protocol; there’s no need to mark the protocol with the class_protocol attribute explicitly. + +如果协议已经用objc标记过了,class_protocol会隐式的应用到那个协议;没有必要显式的用class_protocol标记协议。 + +Protocols are named types, and thus they can appear in all the same places in your code as other named types, as discussed in Protocols as Types. However, you can’t construct an instance of a protocol, because protocols do not actually provide the implementations for the requirements they specify. + +协议是命名类型,因此在你的代码里它们可以像其它命名类型一样出现在左右同样的地方,如在协议里的类型一样。不管怎么样,你不能构造协议的实例,因为协议实际上不能提供它们 + +You can use protocols to declare which methods a delegate of a class or structure should implement, as described in Delegation. +你可以使用协议来声明类或者结构应该实现那个方法,如Delegation.描述。 + + +>协议声明的语法 +protocol-declaration → attributes­opt­protocol­protocol-name­type-inheritance-clause­opt­protocol-body­ +protocol-name → identifier­ +protocol-body → {­protocol-member-declarations­opt­}­ +protocol-member-declaration → protocol-property-declaration­ +protocol-member-declaration → protocol-method-declaration­ +protocol-member-declaration → protocol-initializer-declaration­ +protocol-member-declaration → protocol-subscript-declaration­ +protocol-member-declaration → protocol-associated-type-declaration­ +protocol-member-declarations → protocol-member-declaration­protocol-member-declarations­opt­ + +##Protocol Property Declaration + +##协议属性声明 + +Protocols declare that conforming types must implement a property by including a protocol property declaration in the body of the protocol declaration. Protocol property declarations have a special form of a variable declaration: + +协议声明一致性类型必须通过在协议声明体里包括一个协议属性声明来实现属性。协议属性声明有一个特殊形式的变量声明: + + var property name: type { get set } + +As with other protocol member declarations, these property declarations declare only the getter and setter requirements for types that conform to the protocol. As a result, you don’t implement the getter or setter directly in the protocol in which it is declared. + +与其它协议成员声明一样,这些属性声明只声明了与协议一致的类型的getter和setter要求。结果,你不能在它声明的协议里实现getter货setter + +The getter and setter requirements can be satisfied by a conforming type in a variety of ways. If a property declaration includes both the get and set keywords, a conforming type can implement it with a stored variable property or a computed property that is both readable and writeable (that is, one that implements both a getter and a setter). However, that property declaration can’t be implemented as a constant property or a read-only computed property. If a property declaration includes only the get keyword, it can be implemented as any kind of property. For examples of conforming types that implement the property requirements of a protocol, see Property Requirements. + +getter和setter要求可以通过一致性类型以各种方式满足。如果属性声明包含get和set关键词,一致性类型就可以用可读写(实现了getter和setter)的存储型变量属性或计算型属性,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含get关键词的话,它可以作为任意类型的属性被实现。比如说实现了协议的属性要求的一致性类型,参见属性要求。 + +See also Variable Declaration. + +更多参见变量声明 + +GRAMMAR OF A PROTOCOL PROPERTY DECLARATION + +protocol-property-declaration → variable-declaration-head­variable-name­type-annotation­getter-setter-keyword-block­ + + +##Protocol Method Declaration + +##协议方法声明 + +Protocols declare that conforming types must implement a method by including a protocol method declaration in the body of the protocol declaration. Protocol method declarations have the same form as function declarations, with two exceptions: They don’t include a function body, and you can’t provide any default parameter values as part of the function declaration. For examples of conforming types that implement the method requirements of a protocol, see Method Requirements. + +协议声明一致性类型必须在协议声明体里通过包括一个协议方法声明来实现方法。协议方法声明与函数声明有同样的形式,只有2点例外:他们不需要包括函数体,你不能提供任何某人参数值作为函数声明的一部分。 + + +To declare a class or static method requirement in a protocol declaration, mark the method declaration with the class keyword. Classes that implement this method also declare the method with the class keyword. Structures that implement it must declare the method with the static keyword instead. If you’re implementing the method in an extension, use the class keyword if you’re extending a class and the static keyword if you’re extending a structure. + +在协议声明里,为了声明一个类或者静态方法要求,使用class关键字来标记方法声明。实现这个方法的类也用class来声明方法。实现它的结构必须用static进行声明。如果你在extension里实现,如果你扩展class使用class,如果是结构使用static + + +>GRAMMAR OF A PROTOCOL METHOD DECLARATION + +>protocol-method-declaration → function-head­function-name­generic-parameter-clause­opt­function-signature­ + +##Protocol Initializer Declaration + +##协议构造器声明 + +Protocols declare that conforming types must implement an initializer by including a protocol initializer declaration in the body of the protocol declaration. Protocol initializer declarations have the same form as initializer declarations, except they don’t include the initializer’s body. + +协议声明了一致性类型必须在协议声明的主体里通过引入一个协议构造器声明来实现一个构造器。协议构造器声明除了不包含构造器体外,和构造器声明有着相同的形式. + +See also Initializer Declaration. + +更多请参阅构造器声明。 + +>GRAMMAR OF A PROTOCOL INITIALIZER DECLARATION + +>protocol-initializer-declaration → initializer-head­generic-parameter-clause­opt­parameter-clause­ + + +##Protocol Subscript Declaration + +Protocols declare that conforming types must implement a subscript by including a protocol subscript declaration in the body of the protocol declaration. Protocol property declarations have a special form of a subscript declaration: + +协议声明了一致性类型必须在协议声明的主体里通过引入一个协议附属脚本声明来实现一个附属脚本。协议属性声明对附属脚本声明有一个特殊附属脚本声明形式: + +>subscript (parameters) -> return type { get set } + + +Subscript declarations only declare the minimum getter and setter implementation requirements for types that conform to the protocol. If the subscript declaration includes both the get and set keywords, a conforming type must implement both a getter and a setter clause. If the subscript declaration includes only the get keyword, a conforming type must implement at least a getter clause and optionally can implement a setter clause. +See also Subscript Declaration. + +附属脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果附属脚本声明包含get和set关键字, 一致的类型也必须有一个getter和setter语句。如果附属脚本声明值包含get关键字,一致的类型必须至少包含一个 getter语句,可以选择是否包含setter语句。 + +>GRAMMAR OF A PROTOCOL SUBSCRIPT DECLARATION + +>protocol-subscript-declaration → subscript-head­subscript-result­getter-setter-keyword-block­ + +##Protocol Associated Type Declaration + +##协议关联类型声明 + +Protocols declare associated types using the keyword typealias. An associated type provides an alias for a type that is used as part of a protocol’s declaration. Accosiated types are similiar to type paramters in generic parameter clauses, but they’re associated with Self in the protocol in which they’re declared. In that context, Self refers to the eventual type that conforms to the protocol. For more information and examples, see Associated Types. + +协议用关键字typealias声明关联类型。关联类型为类型提供了一个别名,这个被用作协议声明的一部分。关联类型与在通用参数子句的类型参数相似,但是他们与声明他们的协议中的self相似。在那个环境下,self指的是时间类型。 + +See also Type Alias Declaration. + +也可以参看 Type Alias Declaration. + +>GRAMMAR OF A PROTOCOL ASSOCIATED TYPE DECLARATION + +>protocol-associated-type-declaration → typealias-head­type-inheritance-clause­opt­typealias-assignment­op + + +##Initializer Declaration + +##构造器声明 + +An initializer declaration introduces an initializer for a class, structure, or enumeration into your program. Initializer declarations are declared using the keyword init and have two basic forms. + +构造器声明引入了类的构造器器到程序中。初始化声明使用关键字init声明,有两种基本形式: + +Structure, enumeration, and class types can have any number of initializers, but the rules and associated behavior for class initializers are different. Unlike structures and enumerations, classes have two kinds of initializers: designated initializers and convenience initializers, as described in Initialization. + +结构,枚举和类类型可以有很多构造器,但是类构造器的规则和关联行为是不同的。不像结构和枚举类型。类有两种构造器:指定的和便利的构造器器。 + +The following form declares initializers for structures, enumerations, and designated initializers of classes: + +下面的形式声明了结构,枚举和类指定的构造器 + + init(parameters) { + statements + } + + +A designated initializer of a class initializes all of the class’s properties directly. It can’t call any other initializers of the same class, and if the class has a superclass, it must call one of the superclass’s designated initializers. If the class inherits any properties from its superclass, one of the superclass’s designated initializers must be called before any of these properties can be set or modified in the current class. + +类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其它构造器,它只能调用超类的一个 指定构造器。如果该类从它的超类处继承了任何属性,这些属性在当前类内被赋值或修饰时,必须带调用一个超类的 指定构造器。 + +Designated initializers can be declared in the context of a class declaration only and therefore can’t be added to a class using an extension declaration. + +指定构造器可以在类声明的上下文中声明,因此它不能用扩展声明的方法被加入到一个类中。 + +Initializers in structures and enumerations can call other declared initializers to delegate part or all of the initialization process. + +结构体和枚举的构造器可以调用其它的已声明的构造器,来委托部分或全部初始化过程。 + +To declare convenience initializers for a class, prefix the initializer declaration with the context-sensitive keyword convenience. + +以关键字convenience来声明一个类的便利构造器: + + convenience init(parameters) { + statements + } + + +Convenience initializers can delegate the initialization process to another convenience initializer or to one of the class’s designated initializers. That said, the initialization processes must end with a call to a designated initializer that ultimately initializes the class’s properties. Convenience initializers can’t call a superclass’s initializers. + +便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须 以一个将所有类属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。 + +You can mark designated and convenience initializers with the required attribute to require that every subclass implement the initializer. Because designated initializers are not inherited by subclasses, they must be implemented directly. Required convenience initializers can be either implemented explicitly or inherited when the subclass directly implements all of the superclass’s designated initializers (or overrides the designated initializers with convenience initializers). Unlike methods, properties, and subscripts, you don’t need to mark overridden initializers with the override keyword. + +你可以使用requierd关键字,将便利构造器和指定构造器标记为每个子类的构造器都必须拥有的。因为指定构造器 不被子类继承,他们必须被立即执行。当子类直接执行所有超类的指定构造器(或使用便利构造器重写指定构造器)时, 必需的便利构造器可以被隐式的执行,亦可以被继承。不像方法,附属脚本那样,你不需要为这些重写的构造器标注 overrride关键字。 + +To see examples of initializers in various type declarations, see Initialization. + +查看更多关于不同声明方法的构造器的例子,参阅构造过程一节。 + +>GRAMMAR OF AN INITIALIZER DECLARATION + +>initializer-declaration → initializer-head­generic-parameter-clause­opt­parameter-clause­initializer-body­ +>initializer-head → attributes­opt­convenience­opt­init­ +>initializer-body → code-block­ + +A deinitializer declaration declares a deinitializer for a class type. Deinitializers take no parameters and have the following form: + +析构声明在类中声明了一个析构器。析构器不需要参数,遵循如下的格式: + + deinit { + statements + } + + +A deinitializer is called automatically when there are no longer any references to a class object, just before the class object is deallocated. A deinitializer can be declared only in the body of a class declaration—but not in an extension of a class—and each class can have at most one. + +当类中没有对任何其它对象的引用时,在类对象释放之前,析构器会自动的被调用。析构器只能在类的声明体内、而不能在 类的扩展内声明,每个类最多只能有一个析构器声明。 + +A subclass inherits its superclass’s deinitializer, which is implicitly called just before the subclass object is deallocated. The subclass object is not deallocated until all deinitializers in its inheritance chain have finished executing. + +子类继承了它的超类的析构器,在子类帝乡将要被释放时会被隐式的调用。子类在所有析构器被执行完毕前不会被释放。 + +Deinitializers are not called directly. + +析构器不会被直接调用。 + +For an example of how to use a deinitializer in a class declaration, see Deinitialization. +如何使用类声明中的析构器,请查看Deinitialization + +>GRAMMAR OF A DEINITIALIZER DECLARATION + +>deinitializer-declaration → attributes­opt­deinit­code-block + +##Extension Declaration +##扩展声明 + +An extension declaration allows you to extend the behavior of existing class, structure, and enumeration types. Extension declarations begin with the keyword extension and have the following form: + +扩展声明用于扩展一个已存在的类,结构体,枚举的行为。扩展声明以关键字extension开始,有如下的形式: + + + extension type: adopted protocols { + declarations + } + + + +The body of an extension declaration contains zero or more declarations. These declarations can include computed properties, computed static properties, instance methods, static and class methods, initializers, subscript declarations, and even class, structure, and enumeration declarations. Extension declarations can’t contain destructor or protocol declarations, store properties, property observers, or other extension declarations. For a discussion and several examples of extensions that include various kinds of declarations, see Extensions. + +扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器,附属脚本声明,甚至其它类,结构和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其它的扩展属性。详细讨论和查看包含多种扩展声明的实例,参见Extensions一节。 + +Extension declarations can add protocol conformance to an existing class, structure, and enumeration type in the adopted protocols. Extension declarations can’t add class inheritance to an existing class, and therefore the type-inheritance-clause in an extension declaration contains only a list of protocols. + +扩展声明可以向存在的类,结构体,枚举添加一致的协议。扩展声明不能向一个类中添加类的继承,因此type-inheritance-clause只包含协议的列表。 + + +Properties, methods, and initializers of an existing type can’t be overridden in an extension of that type. +现存类型的属性,方法,构造器不能被它们的扩展重写。 + +Extension declarations can contain initializer declarations. That said, if the type you’re extending is defined in another module, an initializer declaration must delegate to an initializer already defined in that module to ensure members of that type are properly initialized. +扩展声明可以包含构造器声明,这意味着,如果你扩展的类型在其他模块中定义,构造器声明必须委托另一个在 那个模块里声明的构造器来恰当的初始化。 + +>GRAMMAR OF AN EXTENSION DECLARATION + +>extension-declaration → extension­type-identifier­type-inheritance-clause­opt­extension-body­ +>extension-body → {­declarations­opt­}­ + + +##Subscript Declaration + +##附属脚本声明 + +A subscript declaration allows you to add subscripting support for objects of a particular type and are typically used to provide a convenient syntax for accessing the elements in a collection, list, or sequence. Subscript declarations are declared using the keyword subscript and have the following form: + +附属脚本声明用于向特定类型的对象添加附属脚本支持,通常为访问集合,列表和序列的元素提供便利的语法。使用关键字subscript,声明形式如下: + + +> subscript (`parameter`) -> (return type){ + get{ + `statements` + } + set(`setter name`){ + `statements` + } +} + + +Subscript declarations can appear only in the context of a class, structure, enumeration, extension, or protocol declaration. + +附属脚本声明只能出现在类,结构,枚举类型,扩展或协议声明的上下文中。 + +The parameters specify one or more indexes used to access elements of the corresponding type in a subscript expression (for example, the i in the expression object[i]). Although the indexes used to access the elements can be of any type, each parameter must include a type annotation to specify the type of each index. The return type specifies the type of the element being accessed. + +参数(parameters)指定了一个或多个用于在相应类型的附属脚本表达式中访问元素的索引(例如,表达式object[i]中的i)。尽管用于访问元素的索引可以是任意类型,但是每个参数必须包含一个类型标注来指定每种索引的类型。返回类型(return type)指定被访问的元素的类型。 + + +As with computed properties, subscript declarations support reading and writing the value of the accessed elements. The getter is used to read the value, and the setter is used to write the value. The setter clause is optional, and when only a getter is needed, you can omit both clauses and simply return the requested value directly. That said, if you provide a setter clause, you must also provide a getter clause. + +和计算型属性一样,附属脚本声明支持对访问元素的读写。getter用于读取,setter用于写入。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略,直接返回请求的值即可。也就是说,如果提供了setter子句,getter子句也必须要有。 + +The setter name and enclosing parentheses are optional. If you provide a setter name, it is used as the name of the parameter to the setter. If you do not provide a setter name, the default parameter name to the setter is value. That type of the setter name must be the same as the return type. + +setter的名字和封闭的括号是可选的。如果提供了setter名称,它会被setter的参数名。如果没有提供setter名称,那么传给setter的参数的名称默认是value。setter名称的类型必须与返回类型(return type)的类型相同。 + +You can overload a subscript declaration in the type in which it is declared, as long as the parameters or the return type differ from the one you’re overloading. You can also override a subscript declaration inherited from a superclass. When you do so, you must mark the overridden subscript declaration with the override keyword. + +在附属脚本声明的类型中,可以重载附属脚本,只要参数(parameters)或返回类型(return type) 与先前的不同即可。如果这样做的话,必须使用override关键字声明那个被覆盖的附属脚本。 + +You can also declare subscripts in the context of a protocol declaration, as described in Protocol Subscript Declaration. +在协议声明的上下文中,也声明附属脚本,正如Protocol Subscript Declaration描述的一样。 + +For more information about subscripting and to see examples of subscript declarations, see Subscripts。. + +获取更多关于附属脚本的信息和例子,请参看Subscripts。 + +>GRAMMAR OF A SUBSCRIPT DECLARATION + +>subscript-declaration → subscript-head­subscript-result­code-block­ +>subscript-declaration → subscript-head­subscript-result­getter-setter-block­ +>subscript-declaration → subscript-head­subscript-result­getter-setter-keyword-block­ +>subscript-head → attributes­opt­subscript­parameter-clause­ +>subscript-result → ->­attributes­opt­type­ + +##Operator Declaration + +##运算符声明 + +An operator declaration introduces a new infix, prefix, or postfix operator into your program and is declared using the contextual keyword operator. + +运算符声明引入了新的中缀,前缀或后缀运算到程序中,使用上下文关键字operator声明。 + +You can declare operators of three different fixities: infix, prefix, and postfix. The fixity of an operator specifies the relative position of an operator to its operands. + +可以声明3种不同的缀:中缀、前缀和后缀。一个运算符的缀规定了一个它相对于它的操作数的相对位置。 + +There are three basic forms of an operator declaration, one for each fixity. The fixity of the operator is specified by including the contextual keyword infix, prefix, or postfix between operator and the name of the operator. In each form, the name of the operator can contain only the operator characters defined in Operators. + +运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在operator和运算符之间添加上下文关键字infix,prefix或postfix来指定。对于每种形式,运算符的名字只能包含Operators中定义的运算符字符。 + +The following form declares a new infix operator: + +下面这种形式声明了一个新的中缀运算符: + +> operator infix `operator name`{ + precedence `precedence level` + associativity `associativity` + } + + +An infix operator is a binary operator that is written between its two operands, such as the familiar addition operator (+) in the expression 1 + 2. + +中缀运算符是二元运算符,置于两个操作数之间,比如我们熟悉的表达式1 + 2 中的加法运算符(+)。 + +Infix operators can optionally specify a precedence, associativity, or both. + +中缀运算符可以指定优先级,结合性,或两者同时指定。 + +The precedence of an operator specifies how tightly an operator binds to its operands in the absence of grouping parentheses. You specify the precedence of an operator by writing the contextual keyword precedence followed by the precedence level. The precedence level can be any whole number (decimal integer) from 0 to 255; unlike decimal integer literals, it can’t contain any underscore characters. Although the precedence level is a specific number, it is significant only relative to another operator. That is, when two operators compete with each other for their operands, such as in the expression 2 + 3 * 5, the operator with the higher precedence level binds more tightly to its operands. + +运算符的优先级指定了在没有分组括号的情况下,运算符与它的操作数绑定的紧密程度。可以使用上下文关键字precedence和优先等级一起来指定一个运算符的优先级。优先级可以是0到255之间的任何一个数字(十进制整数);与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符比较(大小)。也就是说,一个操作数可以同时被两个运算符使用时,例如2 + 3 * 5,优先级更高的运算符将优先与操作数绑定。 + +The associativity of an operator specifies how a sequence of operators with the same precedence level are grouped together in the absence of grouping parentheses. You specify the associativity of an operator by writing the contextual keyword associativity followed by the associativity, which is one of the contextual keywords left, right, or none. Operators that are left-associative group left-to-right. For example, the subtraction operator (-) is left-associative, and therefore the expression 4 - 5 - 6 is grouped as (4 - 5) - 6 and evaluates to -7. Operators that are right-associative group right-to-left, and operators that are specified with an associativity of none don’t associate at all. Nonassociative operators of the same precedence level can’t appear adjacent to each to other. For example, 1 < 2 < 3 is not a valid expression. + +运算符的结合性明确了,没有分组的括号包围的情况下,优先级相同的运算符以何种顺序被分组的。使用上下文关键字associativity和结合性(associativity)一起来指定一个运算符的结合性,其中结合性的值是上下文关键字left,right或none之一。左结合运算符以从左到右的形式分组。例如,减法运算符(-)具有左结合性,因此表达式4 - 5 - 6以(4 - 5) - 6的形式分组,其结果为-7。 右结合运算符以从右到左的形式分组,对于设置为none的非结合运算符,它们不以任何形式分组。具有相同优先级的非结合运算符,不可以互相邻接。例如,1 < 2 < 3 就是一个无效的表达式 + +Infix operators that are declared without specifying a precedence or associativity are initialized with a precedence level of 100 and an associativity of none. + +如果再声明时不指定任何优先级或结合性,中缀运算符的优先级会被初始化为100,结合性被初始化为none。 + +The following form declares a new prefix operator: + +下面的形式声明了一个新的前缀运算符: + +> operator prefix `operator name`{} + + +A prefix operator is a unary operator that is written immediately before its operand, such as the prefix increment operator (++) is in the expression ++i. + +前缀运算符一元运算符,紧跟在操作数之前,比如表达式 ++i 中的前缀递增运算符(++)。 + +Prefix operators declarations don’t specify a precedence level. Prefix operators are nonassociative. + +前缀缀运算符的声明中不指定优先级。前缀运算符是非结合的。 + +The following form declares a new postfix operator: + +下面的形式声明了一个新的后缀运算符: + + +> operator postfix `operator name`{} + + + +A postfix operator is a unary operator that is written immediately after its operand, such as the postfix increment operator (++) is in the expression i++. + +后缀运算符一元运算符,紧跟在操作数之前,比如表达式 i++ 中的前后缀递增运算符(++)。 + +As with prefix operators, postfix operator declarations don’t specify a precedence level. Postfix operators are nonassociative. + +与前缀运算符一样,后缀运算符声明不会指定优先级。后缀运算符也是非结合性的。 + +After declaring a new operator, you implement it by declaring a function that has the same name as the operator. To see an example of how to create and implement a new operator, see Custom Operators. + +在声明了一个新的运算符之后,要声明一个和运算符同名的函数来实现它。如何创建和实现新的操作符,请看Custom Operators。 + + + +>GRAMMAR OF AN OPERATOR DECLARATION +> +>operator-declaration → prefix-operator-declaration­ postfix-operator-declaration­ >infix-operator-declaration­ +>prefix-operator-declaration → operator ­prefix­ operator­{­}­ +>postfix-operator-declaration → operator ­postfix­ operator­{­}­ +>infix-operator-declaration → operator­infix­operator­{­infix-operator-attributes­opt­}­ +>infix-operator-attributes → precedence-clause­opt­associativity-clause­opt­ +>precedence-clause → precedence­precedence-level­ +>precedence-level → Digit 0 through 255 +>associativity-clause → associativity­associativity­ +>associativity → left­ right­ none + diff --git a/src/chapter3/07_Attributes.md b/src/chapter3/07_Attributes.md new file mode 100644 index 0000000..b285b60 --- /dev/null +++ b/src/chapter3/07_Attributes.md @@ -0,0 +1,208 @@ +# Attributes + +# 特性 + +*Attributes* provide more information about a declaration or type. There are two kinds of attributes in Swift, those that apply to declarations and those that apply to types. For instance, the `required` attribute—when applied to a designated or convenience initializer declaration of a class—indicates that every subclass must implement that initializer. And the `noreturn` attribute—when applied to a function or method type—indicates that the function or method doesn’t return to its caller. + +*特性* 为声明和类型提供了更多的信息。在Swift中有两种类型的特性,一种用于修饰声明,另一种用于修饰类型。比如,`required`特性——当被用来修饰一个类的预设构造函数声明或者便利构造函数声明时——表明其所有的子类都必须实现这个构造函数。`noreturn`特性——当修饰函数或者方法类型时——表明这个函数或者方法不会返回任何值给其调用者。 + +You specify an attribute by writing the `@` symbol followed by the attribute’s name and any arguments that the attribute accepts: + +``` + @attribute name + @attribute name(attribute arguments) +``` + +你可以通过`@`符号以及紧随其后的特性名称和该特性可以接受的参数来使用特性。 + +``` + @attribute name + @attribute name(attribute arguments) +``` + +Some declaration attributes accept arguments that specify more information about the attribute and how it applies to a particular declaration. These *attribute arguments* are enclosed in parentheses, and their format is defined by the attribute they belong to. + +有些声明特性可以通过给定参数来补充该特性相关的更多信息,并说明该特性将如何应用于这个声明中。这些`特性参数`写在小括号里面,他们的具体格式需要根据他们所属的特性来确定。 + +## Declaration Attributes + +## 声明特性 + +You can apply a declaration attribute to declarations only. However, you can also apply the `noreturn` attribute to a function or method *type*. + +声明特性只能描述声明。不过你还是可以使用`noreturn`特性来描述函数或者方法*类型*。 + +`assignment` +> Apply this attribute to functions that overload a compound assignment operator. Functions that overload a compound assignment operator must mark their initial input parameter as `inout`. For an example of how to use the assignment attribute, see [Compound Assignment Operators](#). + +`assignment` +> 这个特性用于描述重载了复合赋值运算符的函数。一个重载了复合赋值运算符的函数必须将它的初始化输入参数标记为`inout`.关于如何使用assigment特性请查看[符合赋值运算符](#)中的一个例子。 + +`class_protocol` +> Apply this attribute to a protocol to indicate that the protocol can be adopted by class types only. + +> If you apply the `objc` attribute to a protocol, the `class_protocol` attribute is implicitly applied to that protocol; there’s no need to mark the protocol with the `class_protocol` attribute explicitly. + +`class_protocol` +> 这个特性用于描述协议,它表明被修饰的协议只能被类使用[//todo adopted]。 + +> 如果一个协议已经添加了`objc`特性,那么`class_protocol`特性就已经被隐形地添加在这个协议上了,不需要再显性地添加一次。 + +`exported` +> Apply this attribute to an import declaration to export the imported module, submodule, or declaration from the current module. If another module imports the current module, that other module can access the items exported by the current module. + +`exported` +> 这个特性被用于修饰导入声明。当一个导入声明具有该特性时,所有通过该声明导入的的模块,子模块或者声明都可以被导出。如果另一个模块导入了这个模块,那么那个模块可以访问所有被这个模块导出的项。 + +`final` +> Apply this attribute to a class or to a property, method, or subscript member of a class. It’s applied to a class to indicate that the class can’t be subclassed. It’s applied to a property, method, or subscript of a class to indicate that that class member can’t be overridden in any subclass. + +`final` +> 这个特性用于描述类或者类的属性、方法、或者下标上。如若一个类具有该特性,则表明这个类不能被继承。如果一个类的属性、方法或者下标具有该特性,则表明这个类的成员不能被任何子类覆盖。 + +`lazy` +> Apply this attribute to a stored variable property of a class or structure to indicate that the property’s initial value is calculated and stored at most once, when the property is first accessed. For an example of how to use the `lazy` attribute, see [Lazy Stored Properties](#). + +`lazy` +> 如果一个类或者结构体的存储变量属性具有该特性,则这个属性的初始值最多只被计算和存储一次,且这一次的计算和存储发生在第一次被访问时。查看[懒惰存储属性](#)中给出的关于如何使用`lazy`的例子。 + +`noreturn` +> Apply this attribute to a function or method declaration to indicate that the corresponding type of that function or method, `T`, is `@noreturn T`. You can mark a function or method type with this attribute to indicate that the function or method doesn’t return to its caller. + +> You can override a function or method that is not marked with the `noreturn` attribute with a function or method that is. That said, you can’t override a function or method that is marked with the `noreturn` attribute with a function or method that is not. Similar rules apply when you implement a protocol method in a conforming type. + +`noreturn` +> 可以通过将这个特性应用在函数或者方法声明中来指定这个函数或者方法,`T`,是`@noreturn T`类型。你可以通过使用这个特性来标记函数或者方法,借此来表明这个函数或者方法不返回任何值给它的调用者。 + +> 你可以使用一个标记了这个特性的函数或者方法来覆盖一个没有标记过这个特性的函数或者方法,但是反过来不行。同样的,当你要实现一个协议方法时,也需要遵循这个规则。 + +`NSCopying` +> Apply this attribute to a stored variable property of a class. This attribute causes the property’s setter to be synthesized with a *copy* of the property’s value—returned by the `copyWithZone` method—instead of the value of the property itself. The type of the property must conform to the `NSCopying` protocol. + +> The `NSCopying` attribute behaves in a way similar to the Objective-C `copy` property attribute. + +`NSCopying` +> 这个特性用于修饰一个类的储存变量属性。该特性将使得这个类的属性的setter方法与这个属性的返回值的*副本*合成——通过`copyWithZone`方法进行返回——而不是这个属性值本身。这个属性值的类型必须遵循`NSCopying`协议。 + +> `NSCopying`特性与Objective-C中的`copy`属性特性类似。 + +`NSManaged` +> Apply this attribute to a stored variable property of a class that inherits from `NSManagedObject` to indicate that the storage and implementation of the property are provided dynamically by Core Data at runtime based on the associated entity description. + +`NSManaged` +> 这个特性用于修饰继承了`NSManagedObject`的类的成员,通过该特性来表明这个成员的存储和实现由Core Data在运行时根据相关实体描述来动态地提供。 + +`objc` +> Apply this attribute to any declaration that can be represented in Objective-C—for example, non-nested classes, protocols, properties and methods (including getters and setters) of classes and protocols, initializers, deinitializers, and subscripts. The `objc` attribute tells the compiler that a declaration is available to use in Objective-C code. + +> If you apply the `objc` attribute to a class or protocol, it’s implicitly applied to the members of that class or protocol. The compiler also implicitly adds the `objc` attribute to a class that inherits from another class marked with the `objc` attribute. Protocols marked with the `objc` attribute can’t inherit from protocols that aren’t. + +> The `objc` attribute optionally accepts a single attribute argument, which consists of an identifier. Use this attribute when you want to expose a different name to Objective-C for the entity the `objc` attribute applies to. You can use this argument to name classes, protocols, methods, getters, setters, and initializers. The example below exposes the getter for the enabled property of the `ExampleClass` to Objective-C code as `isEnabled` rather than just as the name of the property itself. + +``` + @objc + class ExampleClass { + var enabled: Bool { + @objc(isEnabled) get { + // Return the appropriate value + } + } + } +``` + +`objc` +> 这个特性用于描述任何可以在Objective-C中表示的声明,如非嵌套类,协议,类和协议的属性和方法(包括getter和setter),构造函数,析构函数和下标。`objc`特性告诉编译器该声明可以在Objective-C的代码中被使用。 + +> 如果一个类或者协议具有`objc`特性,那么这个类或者协议的所有成员都将隐形地具有`objc`特性。编译器甚至会隐形地添加`objc`特性给具有`objc`特性的类的子类。被标记`objc`特性的协议不能继承没有被标记`objc`特性的协议。 + +> `objc`特性可选地接收一个标识符作为参数。你就可以使用这个特性参数让被标记的对象对Objective-C暴露一个不一样的名字。你可以使用这个参数来重新命名类,协议,方法,getter,setter和构造函数。下面的例子通过给定特性参数使得类`ExampleClass`的enabled这个属性以`isEnabled`这个名字暴露给Objective-C. + +``` + @objc + class ExampleClass { + var enabled: Bool { + @objc(isEnabled) get { + // 返回合适的值 + } + } + } +``` + +`optional` +> Apply this attribute to a protocol’s property, method, or subscript members to indicate that a conforming type isn’t required to implement those members. + +> You can apply the `optional` attribute only to protocols that are marked with the `objc` attribute. As a result, only class types can adopt and conform to a protocol that contains optional member requirements. For more information about how to use the `optional` attribute and for guidance about how to access optional protocol members—for example, when you’re not sure whether a conforming type implements them—see [Optional Protocol Requirements](#). + +`optional` + +> 这个特性用于描述协议的属性,方法和下标,以此来表明遵循了该协议的类型,只需要可选地实现这些被标记的成员。 + +> 这个特性只能被用于描述具有`objc`特性的协议。也就是说,只有类才能接受和遵循一个具有optional特性成员的协议。更多关于如何使用`optional`特性,以及对于一个遵循了具有`optional`特性的协议的类型,在无法明确它实现了哪些可选成员的情况下,如何正确地读取其可选成员的问题,请参考[可选协议要求](#)。 + +`required` +> Apply this attribute to a designated or convenience initializer of a class to indicate that every subclass must implement that initializer. + +> Required designated initializers must be implemented explicitly. Required convenience initializers can be either implemented explicitly or inherited when the subclass directly implements all of the superclass’s designated initializers (or when the subclass overrides the designated initializers with convenience initializers). + +`required` +> 通过为一个类的预设构造函数或者便利构造函数添加该特性来表明这个类的每一个子类都必须实现这个构造函数。 + +> 具有该特性的预设构造函数必须被显性地实现。具有该特性的便利构造函数可以被显性地实现,或者当子类直接实现了所有父类的预设构造函数时(或者子类使用便利构造函数重写了预设构造函数),可以直接继承这个便利构造函数。 + +### Declaration Attributes Used by Interface Builder + +## 在Interface Builder中使用声明特性 + +Interface Builder attributes are declaration attributes used by Interface Builder to synchronize with Xcode. Swift provides the following Interface Builder attributes: `IBAction`, `IBDesignable`, `IBInspectable`, and `IBOutlet`. These attributes are conceptually the same as their Objective-C counterparts. + +Interface Builder特性是Interface Builder用来和Xcode同步的声明特性。Swift提供了以下几种Interface Builder特性: `IBAction`,`IBDesignable`,`IBInspectable`和`IBOutlet`。这些特性在概念上和Objective-C中对应的特性是一样的。 + +You apply the `IBOutlet` and `IBInspectable` attributes to property declarations of a class. You apply the `IBAction` attribute to method declarations of a class and the `IBDesignable` attribute to class declarations. + +`IBOutlet`和`IBInspectable`特性用于描述类的属性声明,`IBAction`特性用于描述类的方法声明而`IBDesignable`用于描述类的声明。 + +## Type Attributes + +## 类型特性 + +You can apply type attributes to types only. However, you can also apply the `noreturn` attribute to a function or method *declaration*. + +类型特性只能描述类型。尽管如此,你还是可以使用`noreturn`特性去修饰函数和方法的声明。 + +`auto_closure` +> This attribute is used to delay the evaluation of an expression by automatically wrapping that expression in a closure with no arguments. Apply this attribute to a function or method type that takes no arguments and that returns the type of the expression. For an example of how to use the `auto_closure` attribute, see [Function Type](#). + +`auto_closure` +> 这个特性通过自动地将表达式包裹在一个没有参数的闭包中来延迟表达式的求值。将该特性用于描述不带任何参数的函数和方法,而这些函数和方法返回表达式的类型。关于如何使用`auto_closure`特性,参考[函数类型](#)中的例子。 + +`noreturn` +> Apply this attribute to the type of a function or method to indicate that the function or method doesn’t return to its caller. You can also mark a function or method declaration with this attribute to indicate that the corresponding type of that function or method, `T`, is `@noreturn T`. + +`noreturn` +> 使用这个特性来描述函数或者方法,以此表明该函数或者方法不返回任何值给它的调用者。你也可以使用这个特性来标记函数或者方法声明,借此来表明这个函数或者方法,`T`,是`@noreturn T`类型。 + +## GRAMMAR OF AN ATTRIBUTE + +## 特性语法总结 + +> attribute → @attribute-nameattribute-argument-clauseopt + +> attribute-name → identifier + +> attribute-argument-clause → (balanced-tokensopt) + +> attributes → attributeattributesopt + +> balanced-tokens → balanced-tokenbalanced-tokensopt + +> balanced-token → (balanced-tokensopt) + +> balanced-token → [balanced-tokensopt] + +> balanced-token → {balanced-tokensopt} + +> balanced-token → Any identifier, keyword, literal, or operator + +> balanced-token → Any punctuation except (, ), [, ], {, or } + + diff --git a/src/chapter3/07_Patterns.md b/src/chapter3/07_Patterns.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/chapter3/08_Generic_Parameters_and_Arguments.md b/src/chapter3/08_Generic_Parameters_and_Arguments.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/chapter3/08_Patterns.md b/src/chapter3/08_Patterns.md new file mode 100644 index 0000000..935c705 --- /dev/null +++ b/src/chapter3/08_Patterns.md @@ -0,0 +1,369 @@ +# Patterns +# 模式 + +A pattern represents the structure of a single value or a composite value. For example, the structure of a tuple ```(1, 2)``` is a comma-separated list of two elements. Because patterns represent the structure of a value rather than any one particular value, you can match them with a variety of values. For instance, the pattern (x, y) matches the tuple (1, 2) and any other two-element tuple. In addition matching a pattern with a value, you can extract part or all of a composite value and bind each part to a constant or variable name. + +模式代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是用逗号分隔的两个元素的列表。由于模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值进行匹配。比如,模式`(x, y)`可以匹配元组`(1, 2)`,以及任何含有两个元素的元组。除了将模式与值匹配外,你可以从合成值中提取部分或全部,并把各部分分别同常量或变量进行绑定。 + +In Swift, patterns occur in variable and constant declarations (on their left-hand side), in ```for-in``` statements, and in ```switch``` statements (in their case labels). Although any pattern can occur in the case labels of a ```switch``` statement, in the other contexts, only wildcard patterns, identifier patterns, and patterns containing those two patterns can occur. + +在Swift中,模式出现在变量和常量的声明(放在它们的左侧)、`for-in`语句及`switch`语句(放在其case标签内)中。尽管任何模式都可以出现在`switch`语句的case标签中,但在其他情况下,只有通配符模式、标识符模式和包含这两种模式的模式才能出现。 + +You can specify a type annotation for a wildcard pattern, an identifier pattern, and a tuple pattern to constraint the pattern to match only values of a certain type. + +你可以为通配符模式、标识符模式和元组模式指定类型注释,用来限制这种模式只匹配某种类型的值。 + +> GRAMMAR OF A PATTERN +> +> pattern → wildcard-pattern­ type-annotation­ opt +> +> pattern → identifier-pattern­ type-annotation­ opt +> +> pattern → value-binding-pattern­ +> +> pattern → tuple-pattern­ type-annotation ­opt­ +> +> pattern → enum-case-pattern­ +> +> pattern → type-casting-pattern­ +> +> pattern → expression-pattern­ + +  + +> 模式语法 +> +> 模式 → 通配符模式 类型注解 可选 +> +> 模式 → 标识符模式 类型注解 可选 +> +> 模式 → 值绑定模式 +> +> 模式 → 元组模式 类型注解 可选 +> +> 模式 → 枚举用例模式 +> +> 模式 → 类型转换模式 +> +> 模式 → 表达式模式 + + +## Wildcard Pattern +## 通配符模式 + +A wildcard pattern matches and ignores any value and consists of an underscore (_). Use a wildcard pattern when you don’t care about the values being matched against. For example, the following code iterates through the closed range `1...3`, ignoring the current value of the range on each iteration of the loop: + +通配符模式由一个下划线(_)组成,它会匹配并忽略任何值。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码对闭区间`1...3`进行了循环,并忽略了每次循环的区间当前值: + +``` +for _ in 1...3 { + // Do something three times. +} +``` + +``` +for _ in 1...3 { + // 执行某操作3次. +} +``` + +> GRAMMAR OF A WILDCARD PATTERN +> +> wildcard-pattern → _­ + +  + +> 通配符模式语法 +> +> 通配符模式 → _ + +## Identifier Pattern +## 标识符模式 + +An identifier pattern matches any value and binds the matched value to a variable or constant name. For example, in the following constant declaration, `someValue` is an identifier pattern that matches the value `42` of type ```Int```: + +标识符模式匹配任何值,并把匹配的值绑定到一个变量或常量。例如,在下面的常量声明中,`someValue`是一个标识符模式,它匹配了`Int`类型的值`42`。 + +``` +let someValue = 42 +``` + +``` +let someValue = 42 +``` + +When the match succeeds, the value ```42``` is bound (assigned) to the constant name ```someValue```. + +当匹配成功时,值`42`被绑定(赋值)给了常量`someValue` + +When the pattern on the left-hand side of a variable or constant declaration is an identifier pattern, the identifier pattern is implicitly a subpattern of a value-binding pattern. + +当一个变量或常量声明的左边是标识符模式时,标识符模式是值绑定模式的隐式子模式。 + +> GRAMMAR OF AN IDENTIFIER PATTERN +> +> identifier-pattern → identifier­ + +  + +> 标识符模式语法 +> +> 标识符模式 → 标识符 + +## Value-Binding Pattern +## 值绑定模式 + +A value-binding pattern binds matched values to variable or constant names. Value-binding patterns that bind a matched value to the name of a constant begin with the keyword ```let```; those that bind to the name of variable begin with the keyword ```var```. + +值绑定模式把匹配的值绑定到一个变量或常量上。把匹配值绑定给常量时,以关键字`let`开头;绑定给变量时,则使用关键字`var`。 + +Identifiers patterns within a value-binding pattern bind new named variables or constants to their matching values. For example, you can decompose the elements of a tuple and bind the value of each element to a corresponding identifier pattern. + +包含在值绑定模式中的标识符模式,会把新命名的变量或常量绑定到它们匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。 + +``` +let point = (3, 2) +switch point { + // Bind x and y to the elements of point. +case let (x, y): + println("The point is at (\(x), \(y)).") +} +// prints "The point is at (3, 2)." +``` + +``` +let point = (3, 2) +switch point { + // 将x,y分别绑定到常量point的元素3,2。 +case let (x, y): + println("The point is at (\(x), \(y)).") +} +// prints "The point is at (3, 2)." +``` + +In the example above, ```let``` distributes to each identifier pattern in the tuple pattern ```(x, y)```. Because of this behavior, the ```switch``` cases ```case let (x, y):``` and ```case (let x, let y):``` match the same values. + +在上面的例子中,`let`把元素分配给元组模式`(x,y)`的相应标识符模式。正是由于这种行为,`switch`语句中的`case let (x, y):`和`case (let x, let y):`匹配的值是一样的。 + +> GRAMMAR OF A VALUE-BINDING PATTERN +> +> value-binding-pattern → var­ pattern­ let ­pattern­ + +  + +> 值绑定模式语法 +> +> 值绑定模式 → var 模式 | let 模式 + +## Tuple Pattern +## 元组模式 + +A tuple pattern is a comma-separated list of zero or more patterns, enclosed in parentheses. Tuple patterns match values of corresponding tuple types. + +元组模式是被一对圆括号包围,含有零或多个模式,并用逗号分隔的列表。元组模式会匹配相应元组类型的值。 + +You can constrain a tuple pattern to match certain kinds of tuple types by using type annotations. For example, the tuple pattern ```(x, y): (Int, Int)``` in the constant declaration ``` let (x, y): (Int, Int) = (1, 2)``` matches only tuple types in which both elements are of type ``` Int``` . To constrain only some elements of a tuple pattern, provide type annotations directly to those individual elements. For example, the tuple pattern in ``` let (x: String, y)``` matches any two-element tuple type, as long as the first element is of type ``` String``` . + +你可以使用类型注释来限制一个元组模式仅匹配某种类型的元组。例如,在常量声明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`类型的元组。如果仅需要限制元组模式中的某几个元素,直接对这几个元素提供类型注释即可。例如,在`let (x: String, y)`中的元组模式,将匹配任意包含两个元素且第一个元素类型是`String`的元组。 + +When a tuple pattern is used as the pattern in a ```for-in``` statement or in a variable or constant declaration, it can contain only wildcard patterns, identifier patterns, or other tuple patterns that contain those. For example, the following code isn’t valid because the element ```0``` in the tuple pattern ```(x, 0)``` is an expression pattern: + +当元组模式用于`for-in`语句、变量或常量声明中时,它只可以包含通配符模式、标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为元组模式`(x, 0)`中的元素`0`是表达式模式: + +``` +let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)] +// This code isn't valid. +for (x, 0) in points { + /* ... */ +} +``` + +``` +let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)] +// 下面的代码是不正确的. +for (x, 0) in points { + /* ... */ +} +``` + +The parentheses around a tuple pattern that contains a single element have no effect. The pattern matches values of that single element’s type. For example, the following are equivalent: + +对于只包含一个元素的元组,包围元组的圆括号是不起作用的。模式会匹配该单个元素的类型。例如,下面的不同写法是等价的: + +``` +let a = 2 // a: Int = 2 +let (a) = 2 // a: Int = 2 +let (a): Int = 2 // a: Int = 2 +``` + +``` +let a = 2 // a: Int = 2 +let (a) = 2 // a: Int = 2 +let (a): Int = 2 // a: Int = 2 +``` + +> GRAMMAR OF A TUPLE PATTERN +> +> tuple-pattern → (­ tuple-pattern-element-list­ opt )­ +> +> tuple-pattern-element-list → tuple-pattern-element­ | tuple-pattern-element­ ,­ tuple-pattern-element-list­ +> +> tuple-pattern-element → pattern­ + +  + +> 元组模式语法 +> +> 元组模式 → ( 元组模式元素列表 可选 ) +> +> 元组模式元素列表 → 元组模式元素 | 元组模式元素 , 元组模式元素列表 +> +> 元组模式元素 → 模式 + +## Enumeration Case Pattern +## 枚举用例模式 + +An enumeration case pattern matches a case of an existing enumeration type. Enumeration case patterns appear only in ```switch``` statement case labels. + +枚举用例模式匹配现有枚举类型的某种用例。枚举用例模式仅在`switch`语句的`case`标签中出现。 + +If the enumeration case you’re trying to match has any associated values, the corresponding enumeration case pattern must specify a tuple pattern that contains one element for each associated value. For an example that uses a ```switch``` statement to match enumeration cases containing associated values, see [Associated Values](). + +如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。使用`switch`语句来匹配包含关联值的枚举用例的例子,请参阅[关联值](). + +> GRAMMAR OF AN ENUMERATION CASE PATTERN +> +> enum-case-pattern → type-identifier ­opt­ .­ enum-case-name­ tuple-pattern­ opt­ + +  + +> 枚举用例模式语法 +> +> 枚举用例模式 → 类型标识 可选 . 枚举的case名 元组模式 可选 + +## Type-Casting Patterns +## 类型转换模式 + +There are two type-casting patterns, the ```is``` pattern and the ```as``` pattern. Both type-casting patterns appear only in ```switch``` statement case labels. The ```is``` and ```as``` patterns have the following form: + +有两种类型转换模式,分别是`is`模式和`as`模式。这两种模式都只出现在`switch`语句的`case`标签中。`is`模式和`as`模式有以下形式: + +> is type +> +> pattern as type + +  + +> `is` type +> +> pattern `as` type + +The ```is``` pattern matches a value if the type of that value at runtime is the same as the type specified in the right-hand side of the ```is``` pattern—or a subclass of that type. The ```is``` pattern behaves like the ```is``` operator in that they both perform a type cast but discard the returned type. + +如果一个值的类型在运行时(runtime)和`is`模式右边所指定的类型(或者那个类型的子类型)一致,`is`模式将会匹配这个值。`is`模式类似`is`操作符,它们都进行类型转换,但是抛弃了返回的类型。 + +The ```as``` pattern matches a value if the type of that value at runtime is the same as the type specified in the right-hand side of the ```as``` pattern—or a subclass of that type. If the match succeeds, the type of the matched value is cast to the pattern specified in the left-hand side of the ```as``` pattern. + +如果一个值的类型在运行时(runtime)和`as`模式右边所指定的类型(或者那个类型的子类型)一致,`as`模式将会匹配这个值。一旦匹配成功,匹配值的类型被转换成`as`模式左边所指定的模式。 + +For an example that uses a ```switch``` statement to match values with ```is``` and ```as``` patterns, see [Type Casting for Any and AnyObject](). + +关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅[Type Casting for Any and AnyObject]()。 + +> GRAMMAR OF A TYPE CASTING PATTERN +> +> type-casting-pattern → is-pattern­ as-pattern­ +> is-pattern → is­ type­ +> as-pattern → pattern­ as ­type­ + +  + +> 类型转换模式语法 +> +> 类型转换模式 → is模式 | as模式 +> is模式 → is 类型 +> as模式 → 模式 as 类型 + +## Expression Pattern +## 表达式模式 + +An expression pattern represents the value of an expression. Expression patterns appear only in ```switch``` statement case labels. + +表达式模式代表了一个表达式的值。该模式只出现在`switch`语句的`case`标签中。 + +The expression represented by the expression pattern is compared with the value of an input expression using the Swift standard library ```~=``` operator. The matches succeeds if the ```~=``` operator returns ```true```. By default, the ```~=``` operator compares two values of the same type using the ```==``` operator. It can also match an integer value with a range of integers in an ```Range``` object, as the following example shows: + +由表达式模式所代表的表达式会使用Swift标准库中的`~=`操作符与输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示: + +``` +let point = (1, 2) +switch point { +case (0, 0): + println("(0, 0) is at the origin.") +case (-2...2, -2...2): + println("(\(point.0), \(point.1)) is near the origin.") +default: + println("The point is at (\(point.0), \(point.1)).") +} +// prints "(1, 2) is near the origin." +``` + +``` +let point = (1, 2) +switch point { +case (0, 0): + println("(0, 0) is at the origin.") +case (-2...2, -2...2): + println("(\(point.0), \(point.1)) is near the origin.") +default: + println("The point is at (\(point.0), \(point.1)).") +} +// prints "(1, 2) is near the origin." +``` + +You can overload the ```~=``` operator to provide custom expression matching behavior. For example, you can rewrite the above example to compare the point expression with a string representations of points. + +你可以重载`~=`操作符来提供自定义的表达式匹配行为。例如,你可以重写上面的例子来使用point的字符串形式来比较point表达式。 + +``` +// Overload the ~= operator to match a string with an integer +func ~=(pattern: String, value: Int) -> Bool { + return pattern == "\(value)" +} +switch point { +case ("0", "0"): + println("(0, 0) is at the origin.") +case ("-2...2", "-2...2"): + println("(\(point.0), \(point.1)) is near the origin.") +default: + println("The point is at (\(point.0), \(point.1)).") +} +// prints "(1, 2) is near the origin." +``` + +``` +// 重载 ~= 操作符让整数能匹配字符串 +func ~=(pattern: String, value: Int) -> Bool { + return pattern == "\(value)" +} +switch point { +case ("0", "0"): + println("(0, 0) is at the origin.") +case ("-2...2", "-2...2"): + println("(\(point.0), \(point.1)) is near the origin.") +default: + println("The point is at (\(point.0), \(point.1)).") +} +// prints "(1, 2) is near the origin." +``` + +> GRAMMAR OF AN EXPRESSION PATTERN +> +> expression-pattern → expression­ + +  + +> 表达式模式语法 +> +> 表达式模式 → 表达式 + diff --git a/src/chapter3/09_Generic_Parameters_and_Arguments.md b/src/chapter3/09_Generic_Parameters_and_Arguments.md new file mode 100644 index 0000000..76e375a --- /dev/null +++ b/src/chapter3/09_Generic_Parameters_and_Arguments.md @@ -0,0 +1,150 @@ +# Generic Parameters and Arguments +# 泛型参数 + + +This chapter describes parameters and arguments for generic types, functions, and initializers. When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. + +本章节介绍泛型类型、函数、初始构造器的参数。当你在声明泛型类型、函数或者初始化构造器时,你指定了它们能够使用的类型参数。这些类型参数起到了占位符的作用,在泛型类型的实例创建或者泛型方法、初始化构造器调用时在创建泛型类型的实例或者调用泛型方法、初始化构造器时,它们会被替换为实际的参数。 + +For an overview of generics in Swift, see [Generics](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_234). + +关于Swift泛型的概述,可以详见[泛型](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_234)。 + +## Generic Parameter Clause +## 泛型形参句式 + +A generic parameter clause specifies the type parameters of a generic type or function, along with any associated constraints and requirements on those parameters. A generic parameter clause is enclosed in angle brackets (<>) and has one of the following forms: + +泛型参数句式指定了泛型类型或函数的参数类型,以及与这些参数相关的约束、依赖条件。泛型参数句式的内容用一对尖括号(<>)包围,并且是以下形式中的一种: + +``` + + +``` +The generic parameter list is a comma-separated list of generic parameters, each of which has the following form: +泛型参数列表是用逗号分隔的一组参数,其中的每一个都类似以下的形式: + +``` +type parameter: constraint +``` + +A generic parameter consists of a type parameter followed by an optional constraint. A type parameter is simply the name of a placeholder type (for instance, T, U, V, KeyType, ValueType, and so on). You have access to the type parameters (and any of their associated types) in the rest of the type, function, or initializer declaration, including in the signature of the function or initializer. + +泛型参数由类型形参组成,紧跟其后的是是一个可选的约束条件。类型形参只是一个占位符类型的名称(比如:T、U、V、KeyType、ValueType等)。You have access to the type parameters (and any of their associated types) in the rest of the type, function, or initializer declaration, including in the signature of the function or initializer. + +reviewer注: 泛型参数由类型形参组成,紧跟其后的是是一个可选的约束条件。类型形参只是一个占位符类型的名称(比如:T、U、V、KeyType、ValueTyp等等)。在类型,函数或者初始化构造器的其余部分以及函数或初始化构造器的签名里,都可以访问类型参数(还有它的任何相关类型)。 + + +The constraint specifies that a type parameter inherits from a specific class or conforms to a protocol or protocol composition. For instance, in the generic function below, the generic parameter T: Comparable indicates that any type argument substituted for the type parameter T must conform to the Comparable protocol. + +约束条件指定了类型形参是继承自特定的类、符合某个协议或者协议的组件部分。比如,在下面的泛型函数中,泛型形参`T: Comparable`表明替换泛型类型形参的任何类型实参都必须符合`Comparable`协议。 + +``` +func simpleMin(x: T, y: T) -> T { + if x < y { + return y + } + return x +} +``` + +Because Int and Double, for example, both conform to the Comparable protocol, this function accepts arguments of either type. In contrast with generic types, you don’t specify a generic argument clause when you use a generic function or initializer. The type arguments are instead inferred from the type of the arguments passed to the function or initializer. + +比如,`Int`与`Double`都符合`Comparable`协议,所以这个函数接受任意一种实参类型。与泛型类型形参相反的是相比,当使用泛型方式或者初始构造器时,无需指定泛型实参的句式,实参由传入函数或初始化构造器的实际类型推断得出。 + + +``` +simpleMin(17, 42) // T被推断是Int +simpleMin(3.14159, 2.71828) // T被推断是Double +``` + +## Where Clauses +## Where 句式 + +You can specify additional requirements on type parameters and their associated types by including a where clause after the generic parameter list. A where clause consists of the keyword where, followed by a comma-separated list of one or more requirements. + +通过在泛型形参列表之后引入where句式,可以在类型形参及相关的类型上定义额外的依赖条件。`where`句 式由`where`关键字和紧随其后的由逗号分隔的一个或多个依赖条件组成。 + +The requirements in a where clause specify that a type parameter inherits from a class or conforms to a protocol or protocol composition. Although the where clause provides syntactic sugar for expressing simple constraints on type parameters (for instance, T: Comparable is equivalent to T where T: Comparable and so on), you can use it to provide more complex constraints on type parameters and their associated types. For instance, you can express the constraints that a generic type T inherits from a class C and conforms to a protocol P as . + +`where`句式的依赖条件指定了类型形参是继承自特定的类、符合某个协议或者协议的组成部分。尽管`where`句式提供了语法糖来简化表达类型形参的约束条件(比如,`T: Comparable`等同于`T where T: Comparable`等),但仍可以在类型形参和相关类型上提供复杂的约束条件。比如:泛型类型T继承自类C且符合协议P可以表达为``````。 + +As mentioned above, you can constrain the associated types of type parameters to conform to protocols. For example, the generic parameter clause specifies that T conforms to the Generator protocol and the associated type of T, T.Element, conforms to the Equatable protocol (T has the associated type Element because Generator declares Element and T conforms to Generator). + +如上所述,可以约束类型形参的相关类形符合特定的协议。比如:泛型参数句式``````指定了T符合`Generator`协议,且T的Element类型符合Equatable协议(T有关联系的Element类型是因为Generator协议声明了Element,而T符合Generator协议)。 + +You can also specify the requirement that two types be identical, using the == operator. For example, the generic parameter clause expresses the constraints that T and U conform to the Generator protocol and that their associated types must be identical. +你也可以用```==```运符符指定两种类型完全相等的依赖条件。比如,泛型形参句式``````表明T和U符合Generator协议,并且他们关联的类型必须完全相等的约束条件。 + +Any type argument substituted for a type parameter must meet all the constraints and requirements placed on the type parameter. +所有实参在替换类型形参时必须实现所有的约束和依赖条件。 + +You can overload a generic function or initializer by providing different constraints, requirements, or both on the type parameters in the generic parameter clause. When you call an overloaded generic function or initializer, the compiler uses these constraints to resolve which overloaded function or initializer to invoke. +通过在泛型参数句式中为类型形参定义不同的约束条件、依赖条件可以实现泛型函数、初始化构造器的重载。当调用重载的泛型函数或初始化构造器时,编译器会使用这些约束条件来实现它们的调用。 + +You can subclass a generic class, but the subclass must also be a generic class. +泛型类可以子类化,但子类也必须是一个泛型类。 + +> **GRAMMAR OF A GENERIC PARAMETER CLAUSE** + +> generic-parameter-clause → <­generic-parameter-list­requirement-clause­opt­>­ +> generic-parameter-list → generic-parameter­ generic-parameter­,­generic-parameter-list­ +> generic-parameter → type-name­ +> generic-parameter → type-name­:­type-identifier­ +> generic-parameter → type-name­:­protocol-composition-type­ +> requirement-clause → where­requirement-list­ +> requirement-list → requirement­ requirement­,­requirement-list­ +> requirement → conformance-requirement­ same-type-requirement­ +> conformance-requirement → type-identifier­:­type-identifier­ +> conformance-requirement → type-identifier­:­protocol-composition-type­ +> same-type-requirement → type-identifier­==­type-identifier­ + + +## Generic Argument Clause +## 泛型实参句式 + +A generic argument clause specifies the type arguments of a generic type. A generic argument clause is enclosed in angle brackets (<>) and has the following form: + +泛型实参句式指定了泛型类型的类型实参。它由一对尖括号包围,形式如下: + +``` + +``` + +The generic argument list is a comma-separated list of type arguments. A type argument is the name of an actual concrete type that replaces a corresponding type parameter in the generic parameter clause of a generic type. The result is a specialized version of that generic type. As an example, the Swift standard library defines a generic dictionary type as: + +泛型实参列表是由一组逗号分隔组成的类型实参组成。类型实参是泛型参数句式中替换对应类型形参的具体实参的名称一个实际的具体类型的名称,它会替换在泛型类型形参对应的类型参数。由此得到此泛型类形的一个特定配置。比如,Swift标准库定义泛型字典类型是这样实现的: + +``` +struct Dictionary: Collection, DictionaryLiteralConvertible { + /* ... */ +} +``` + +The specialized version of the generic Dictionary type, Dictionary is formed by replacing the generic parameters KeyType: Hashable and ValueType with the concrete type arguments String and Int. Each type argument must satisfy all the constraints of the generic parameter it replaces, including any additional requirements specified in a where clause. In the example above, the KeyType type parameter is constrained to conform to the Hashable protocol and therefore String must also conform to the Hashable protocol. + +Dictionary类型的特定配置```Dictionary```是由具体替换泛型参数```KeyType: Hashable```和```ValueType```的实参```String```与```Int```组成。每一个实参都必须满足它所替换的泛型型参所要求的约束条件,包括在```where```句式中指定的额外依赖条件。在以上的例子中,`KeyType`参数的约束条件是符合```Hashable```协议,```String```也必须符合```Hashable```协议。 + +You can also replace a type parameter with a type argument that is itself a specialized version of a generic type (provided it satisfies the appropriate constraints and requirements). For example, you can replace the type parameter T in Array with a specialized version of an array, Array, to form an array whose elements are themselves arrays of integers. + + +你也可以用一个满足约束条件与依赖条件的类型实参的特定配置来替换类型形参。比如,你可以用特定的数组`Array`替换`Array`中的类型形参`T`,得到 +一个数组,且它的元素是跟它本身一样的由整形元素组成的数组。 + +``` +let arrayOfArrays: Array> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +``` + +As mentioned in Generic Parameter Clause, you don’t use a generic argument clause to specify the type arguments of a generic function or initializer. + +正如在泛型参式句式里面提及的,不需要泛型实参句式指定泛形函数、初始化器的实参。 + +> **GRAMMAR OF A GENERIC ARGUMENT CLAUSE** + +> generic-argument-clause → <­generic-argument-list­>­ +> generic-argument-list → generic-argument­ generic-argument­,­generic-argument-list­ +> generic-argument → type­ + + + + diff --git a/src/chapter3/09_Summary_of_the_Grammar.md b/src/chapter3/09_Summary_of_the_Grammar.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/chapter3/10_Summary_of_the_Grammar.md b/src/chapter3/10_Summary_of_the_Grammar.md new file mode 100644 index 0000000..a287a89 --- /dev/null +++ b/src/chapter3/10_Summary_of_the_Grammar.md @@ -0,0 +1,1773 @@ +#Summary of the Grammar +#语法总结 + +##Statements +##语句 + +###GRAMMAR OF A STATEMENT + +‌ statement → expression;opt + +‌ statement → declaration;opt + +‌ statement → loop-statement;opt + +‌ statement → branch-statement;opt + +‌ statement → labeled-statement + +‌ statement → control-transfer-statement;opt + +‌ statements → statementstatements opt + +###语句语法 +语句 → 表达式 ;可选 + +语句 → 声明 ;可选 + +语句 → 循环语句 ;可选 + +语句 → 分支语句 ;可选 + +语句 → 标记语句 + +语句 → 控制转移语句 ;可选 + +多条语句 → 语句 多条语句 可选 + +###GRAMMAR OF A LOOP STATEMENT + +‌ loop-statement → for-statement + +‌ loop-statement → for-in-statement + +‌ loop-statement → while-statement + +‌ loop-statement → do-while-statement + +###循环语句语法 +循环语句 → for 语句 + +循环语句 → for in 语句 + +循环语句 → while 语句 + +循环语句 → do-while 语句 + +###GRAMMAR OF A FOR STATEMENT + +‌ for-statement → for for-init opt ;expression opt ; expression opt code-block + +‌ for-statement → for (for-init opt ;expression opt ; expression opt) code-block + +‌ for-init → variable-declaration expression-list + +###for 语句语法 +for语句 → **for** for-初始条件 可选; 表达式 可选 ;表达式 可选 代码块 + +for语句 → **for** ( for-初始条件 可选; 表达式 可选 ;表达式 可选 ) 代码块 + +for-初始条件 → 变量声明 | 表达式列表 + +###GRAMMAR OF A FOR-IN STATEMENT + +‌ for-in-statement → **for** pattern **in** expression code-block + +###for-in 语句语法 +for-in 语句 → for 模式 in 表达式代码块 + +###GRAMMAR OF A WHILE STATEMENT + +‌ while-statement → while while-condition code-block + +‌ while-condition → expression declaration + +###while 语句语法 +while语句 → **while** while-条件 代码块 + +while-条件 → 表达式 | 声明 + +###GRAMMAR OF A DO-WHILE STATEMENT + +‌ do-while-statement → do code-block while while-condition + +###do-while 语句语法 +do-while语句 → **do** 代码块 **while** while-条件 + + +###GRAMMAR OF A BRANCH STATEMENT + +‌ branch-statement → if-statement + +‌ branch-statement → switch-statement + +###分支语句语法 + +分支语句 → if语句 + +分支语句 → switch语句 + +###GRAMMAR OF AN IF STATEMENT + +‌ if-statement → **if** if-condition code-block else-clause opt + +‌ if-condition → expression | declaration + +‌ else-clause → **else** code-block | **else** if-statement + +###if 语句语法 +if语句 → if if-条件 代码块 else 子句 可选 + +if条件 → 表达式 | 申明 + +else子句 → else 代码块 | else if-语句 + +###GRAMMAR OF A SWITCH STATEMENT + +‌ switch-statement → switch expression{switch-casesopt} + +‌ switch-cases → switch-case switch-cases opt + +‌ switch-case → case-label statements | default-label statements + +‌ switch-case → case-label; | default-label; + +‌ case-label → case case-item-list: + +‌ case-item-list → patternguard-clause opt | pattern guard-clause opt,case-item-list + +‌ default-label → default: + +‌ guard-clause → whereguard-expression + +‌ guard-expression → expression + +###switch语句语法 +switch语句 → **switch** 表达式 { switch-case列表 可选 } + +switch-case列表 → switch-case switch-case列表 可选 + +switch-case → case标签 多条语句 | default标签 多条语句 + +switch-case → case标签 ; | default标签 ; + +case标签 → **case** case项列表 : + +case项列表 → 模式 监护子句 可选 | 模式 监护子句 可选 , case项列表 + +default标签 → **default** : + +监护字句 → **where** 监护表达式 + +监护表达式 → 表达式 + +###GRAMMAR OF A LABELED STATEMENT + +‌ labeled-statement → statement-label loop-statement | statement-label switch-statement + +‌ statement-label → label-name: + +‌ label-name → identifier + +###标记语句语法 +标记语句 → 语句标签 循环语句 | 语句标签 switch语句 + +语句标签 → 标签名称 : + +标签名称 → 标识符 + +###GRAMMAR OF A CONTROL TRANSFER STATEMENT + +‌ control-transfer-statement → break-statement + +‌ control-transfer-statement → continue-statement + +‌ control-transfer-statement → fallthrough-statement + +‌ control-transfer-statement → return-statement + +###控制传递语句语法 +控制传递语句 → break-语句 + +控制传递语句 → continue-语句 + +控制传递语句 → fallthrough-语句 + +控制传递语句 → return-语句 + +###GRAMMAR OF A BREAK STATEMENT + +‌ break-statement → breaklabel-name opt + +###break 语句语法 +break语句 → **break** 标签名称 可选 + + +###GRAMMAR OF A CONTINUE STATEMENT + +‌ continue-statement → continue label-name opt + +###continue 语句语法 +continue语句 → **continue** 标签名称 可选 + +###GRAMMAR OF A FALLTHROUGH STATEMENT + +‌ fallthrough-statement → fallthrough + +###fallthrough 语句语法 +fallthrough-语句 → **fallthrough** + +###GRAMMAR OF A RETURN STATEMENT + +‌ return-statement → return expression opt +####return 语句语法 +return语句 → **return** 表达式 可选 + +##Generic Parameters and Arguments + + +##泛型和参数 + +###GRAMMAR OF A GENERIC PARAMETER CLAUSE + +‌ generic-parameter-clause → + +‌ generic-parameter-list → generic-parameter | generic-parameter,generic-parameter-list + +‌ generic-parameter → type-name + +‌ generic-parameter → type-name:type-identifier + +‌ generic-parameter → type-name:protocol-composition-type + +‌ requirement-clause → where requirement-list + +‌ requirement-list → requirement | requirement,requirement-list + +‌ requirement → conformance-requirement | same-type-requirement + +‌ conformance-requirement → type-identifier:type-identifier + +‌ conformance-requirement → type-identifier:protocol-composition-type + +‌ same-type-requirement → type-identifier==type-identifier + +###泛型形参子句语法 + +泛型参数子句 → < 泛型参数列表 约束子句 可选 > + +泛型参数列表 → 泛形参数 | 泛形参数 , 泛型参数列表 + +泛形参数 → 类型名称 + +泛形参数 → 类型名称 : 类型标识 + +泛形参数 → 类型名称 : 协议合成类型 + +约束子句 → **where** 约束列表 + +约束列表 → 约束 | 约束 , 约束列表 + +约束 → 一致性约束 | 同类型约束 + +一致性约束 → 类型标识 : 类型标识 + +一致性约束 → 类型标识 : 协议合成类型 + +同类型约束 → 类型标识 == 类型标识 + +###GRAMMAR OF A GENERIC ARGUMENT CLAUSE + +‌ generic-argument-clause → + +‌ generic-argument-list → generic-argument | generic-argument,generic-argument-list + +‌ generic-argument → type + +###泛型参数子句语法 +泛型参数子句 → < 泛型参数列表 > + +泛型参数列表 → 泛型参数 | 泛型参数 , 泛型参数列表 + +泛型参数 → 类型 + +##Declarations +##声明 +###GRAMMAR OF A DECLARATION + +‌ declaration → import-declaration + +‌ declaration → constant-declaration + +‌ declaration → variable-declaration + +‌ declaration → typealias-declaration + +‌ declaration → function-declaration + +‌ declaration → enum-declaration + +‌ declaration → struct-declaration + +‌ declaration → class-declaration + +‌ declaration → protocol-declaration + +‌ declaration → initializer-declaration + +‌ declaration → deinitializer-declaration + +‌ declaration → extension-declaration + +‌ declaration → subscript-declaration + +‌ declaration → operator-declaration + +‌ declarations → declarationdeclarations opt + +‌ declaration-specifiers → declaration-specifier declaration-specifiersopt + +‌ declaration-specifier → class | mutating | nonmutating | override | static | unowned | unowned(safe) | unowned(unsafe) | weak + + +###声明语法 + +声明 → 导入声明 + +声明 → 常量声明 + +声明 → 变量声明 + +声明 → 类型别名声明 + +声明 → 函数声明 + +声明 → 枚举声明 + +声明 → 结构体声明 + +声明 → 类声明 + +声明 → 协议声明 + +声明 → 构造器声明 + +声明 → 析构器声明 + +声明 → 扩展声明 + +声明 → 下标脚本声明 + +声明 → 运算符声明 + +声明列表 → 声明 声明列表 可选 + +声明描述符列表 → 声明描述符 声明描述符列表 可选 + +声明描述符 → **class** | **mutating** | **nonmutating** | **override** | **static** | **unowned** | **unowned(safe) **| **unowned(unsafe)** | **weak** + +###GRAMMAR OF A TOP-LEVEL DECLARATION + +‌ top-level-declaration → statements opt + +###顶级声明语法 +顶级声明 → 多条语句 可选 + +###GRAMMAR OF A CODE BLOCK + +‌ code-block → {statements opt} + +###代码块语法 +代码块 → { 多条语句 可选 } + +###GRAMMAR OF AN IMPORT DECLARATION + +‌ import-declaration → attributes opt import import-kind opt import-path + +‌ import-kind → typealias | struct | class | enum | protocol | var | func + +‌ import-path → import-path-identifier | import-path-identifier.import-path + +‌ import-path-identifier → identifier | operator + +###导入声明语法 +导入声明 → 属性列表 可选 **import** 导入类型 可选 导入路径 + +导入类型 → **typealias** | **struct** | **class** | **enum** | **protocol** | **var** | **func** + +导入路径 → 导入路径标识符 | 导入路径标识符 . 导入路径 + +导入路径标识符 → 标识符 | 运算符 + +###GRAMMAR OF A CONSTANT DECLARATION + +‌ constant-declaration → attributes opt declaration-specifiers opt let pattern-initializer-list + +‌ pattern-initializer-list → pattern-initializer | pattern-initializer,pattern-initializer-list + +‌ pattern-initializer → pattern initializer opt + +‌ initializer → =expression + +###常数声明语法 +常量声明 → 属性列表 可选 声明描述符列表 可选 **let** 模式构造器列表 + +模式构造器列表 → 模式构造器 | 模式构造器 , 模式构造器列表 + +模式构造器 → 模式 构造器 可选 + +构造器 → = 表达式 + +###GRAMMAR OF A VARIABLE DECLARATION + +‌ variable-declaration → variable-declaration-head pattern-initializer-list + +‌ variable-declaration → variable-declaration-head variable-name type-annotationcode-block + +‌ variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block + +‌ variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block + +‌ variable-declaration → variable-declaration-head variable-name type-annotation initializer opt willSet-didSet-block + +‌ variable-declaration-head → attributes opt declaration-specifiers opt var + +‌ variable-name → identifier + +‌ getter-setter-block → {getter-clausesetter-clauseopt} + +‌ getter-setter-block → {setter-clausegetter-clause} + +‌ getter-clause → attributes opt getcode-block + +‌ setter-clause → attributes opt setsetter-nameoptcode-block + +‌ setter-name → (identifier) + +‌ getter-setter-keyword-block → {getter-keyword-clausesetter-keyword-clauseopt} + +‌ getter-setter-keyword-block → {setter-keyword-clausegetter-keyword-clause} + +‌ getter-keyword-clause → attributes opt get + +‌ setter-keyword-clause → attributes opt set + +‌ willSet-didSet-block → {willSet-clausedidSet-clauseopt} + +‌ willSet-didSet-block → {didSet-clausewillSet-clause} + +‌ willSet-clause → attributes opt willSetsetter-name opt code-block + +‌ didSet-clause → attributes opt didSetsetter-name opt code-block + +###变量声明语法 +变量声明 → 变量声明头 模式构造器列表 + +变量声明 → 变量声明头 变量名 类型注解 代码块 + +变量声明 → 变量声明头 变量名 类型注解 getter-setter块 + +变量声明 → 变量声明头 变量名 类型注解 getter-setter关键字块 + +变量声明 → 变量声明头 变量名 类型注解 构造器 可选 willSet-didSet代码块 + +变量声明头 → 属性列表 可选 声明描述符列表 可选 **var** + +变量名称 → 标识符 + +getter-setter块 → { getter子句 setter子句 可选 } + +getter-setter块 → { setter子句 getter子句 } + +getter子句 → 属性列表 可选 **get** 代码块 + +setter子句 → 属性列表 可选 **set** setter名称 可选 代码块 + +setter名称 → **(** 标识符 **)** + +getter-setter关键字块 → **{** getter关键字子句 setter关键字子句 可选 **}** + +getter-setter关键字块 → **{ **setter关键字子句 getter关键字子句 **}** + +getter关键字子句 → 属性列表 可选 **get** + +setter关键字子句 → 属性列表 可选 **set** + +willSet-didSet代码块 → **{** willSet子句 didSet子句 可选**}** + +willSet-didSet代码块 → **{** didSet子句 willSet子句 **}** + +willSet子句 → 属性列表 可选 **willSet** setter名称 可选 代码块 + +didSet子句 → 属性列表 可选 **didSet** setter名称 可选 代码块 + +###GRAMMAR OF A TYPE ALIAS DECLARATION + +‌ typealias-declaration → typealias-head typealias-assignment + +‌ typealias-head → typealias typealias-name + +‌ typealias-name → identifier + +‌ typealias-assignment → =type + +###类型别名声明语法 + +类型别名声明 → 类型别名头 类型别名赋值 + +类型别名头 → **typealias** 类型别名名称 + +类型别名名称 → 标识符 + +类型别名赋值 → = 类型 + + +###GRAMMAR OF A FUNCTION DECLARATION + +‌ function-declaration → function-head function-name generic-parameter-clause opt function-signature function-body + +‌ function-head → attributes opt declaration-specifiers opt func + +‌ function-name → identifier | operator + +‌ function-signature → parameter-clauses function-result opt + +‌ function-result → ->attributes opt type + +‌ function-body → code-block + +‌ parameter-clauses → parameter-clauseparameter-clauses opt + +‌ parameter-clause → () | (parameter-list...opt) + +‌ parameter-list → parameter | parameter,parameter-list + +‌ parameter → inout opt let opt # opt parameter-name local-parameter-name opt type-annotation default-argument-clause opt + +‌ parameter → inout opt var# opt parameter-name local-parameter-name opt type-annotation default-argument-clause opt + +‌ parameter → attributes opt type + +‌ parameter-name → identifier | _ + +‌ local-parameter-name → identifier | _ + +‌ default-argument-clause → =expression + +###函数声明语法 +函数声明 → 函数头 函数名 泛型参数子句 可选 函数签名 函数体 + +函数头 → 属性列表 可选 声明描述符列表 可选 **func** + +函数名 → 标识符 | 运算符 + +函数签名 → 参数子句列表 函数结果 可选 + +函数结果 → → 属性列表 可选 类型 + +函数体 → 代码块 + +参数子句列表 → 参数子句 参数子句列表 可选 + +参数子句 → **( ) **| ( 参数列表 ... 可选 ) + +参数列表 → 参数 | 参数 , 参数列表 + +参数 → **inout** 可选 **let** 可选 **#** 可选 参数名 局部参数名 可选 类型注解 默认参数子句 可选 + +参数 → **inout** 可选 **var** **#** 可选 参数名 局部参数名 可选 类型注解 默认参数子句 可选 + +参数 → 属性列表 可选 类型 + +参数名 → 标识符 | _ + +局部参数名 → 标识符 | _ + +默认参数子句 → = 表达式 + +###GRAMMAR OF AN ENUMERATION DECLARATION + +‌ enum-declaration → attributes opt union-style-enum | attributes opt raw-value-style-enum + +‌ union-style-enum → enum-name generic-parameter-clause opt { union-style-enum-members opt } + +‌ union-style-enum-members → union-style-enum-member union-style-enum-members opt + +‌ union-style-enum-member → declaration | union-style-enum-case-clause + +‌ union-style-enum-case-clause → attributes opt case union-style-enum-case-list + +‌ union-style-enum-case-list → union-style-enum-case | union-style-enum-case,union-style-enum-case-list + +‌ union-style-enum-case → enum-case-nametuple-type opt + +‌ enum-name → identifier + +‌ enum-case-name → identifier + +‌ raw-value-style-enum → enum-name generic-parameter-clause opt:type-identifier{raw-value-style-enum-members opt} + +‌ raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-members opt + +‌ raw-value-style-enum-member → declaration raw-value-style-enum-case-clause + +‌ raw-value-style-enum-case-clause → attributes opt caseraw-value-style-enum-case-list + +‌ raw-value-style-enum-case-list → raw-value-style-enum-case raw-value-style-enum-case,raw-value-style-enum-case-list + +‌ raw-value-style-enum-case → enum-case-name raw-value-assignment opt + +‌ raw-value-assignment → =literal + + +###枚举声明语法 +枚举声明 → 属性列表 可选 联合式枚举 | 属性列表 可选 原始值式枚举 + +联合式枚举 → 枚举名 泛型参数子句 可选 { union-style-enum-members 可选 } + +union-style-enum-members → union-style-enum-member union-style-enum-members 可选 + +union-style-enum-member → 声明 | 联合式的枚举case子句 + +联合式的枚举case子句 → 属性列表 可选 **case** 联合式的枚举case列表 + +联合式的枚举case列表 → 联合式的case | 联合式的case , 联合式的枚举case列表 + +联合式的case → 枚举的case名 元组类型 可选 + +枚举名 → 标识符 + +枚举的case名 → 标识符 + +原始值式枚举 → 枚举名 泛型参数子句 可选 : 类型标识 { 原始值式枚举成员列表 可选 } + +原始值式枚举成员列表 → 原始值式枚举成员 原始值式枚举成员列表 可选 + +原始值式枚举成员 → 声明 | 原始值式枚举case子句 + +原始值式枚举case子句 → 属性列表 可选 **case** 原始值式枚举case列表 + +原始值式枚举case列表 → 原始值式枚举case | 原始值式枚举case , 原始值式枚举case列表 + +原始值式枚举case → 枚举的case名 原始值赋值 可选 + +原始值赋值 → = 字面量 + +###GRAMMAR OF A STRUCTURE DECLARATION + +‌ struct-declaration → attributes opt struct struct-name generic-parameter-clause opt type-inheritance-clause opt struct-body + +‌ struct-name → identifier + +‌ struct-body → {declarations opt} +###结构体声明语法 +结构体声明 → 属性列表 可选 **struct** 结构体名称 泛型参数子句 可选 类型继承子句 可选 结构体主体 + +结构体名称 → 标识符 + +结构体主体 → { 声明列表 可选 } + +###GRAMMAR OF A CLASS DECLARATION + +‌ class-declaration → attributes opt classclass-name generic-parameter-clause opt type-inheritance-clause opt class-body + +‌ class-name → identifier + +‌ class-body → {declarations opt} + +###类声明语法 +类声明 → 属性列表 可选 class 类名 泛型参数子句 可选 类型继承子句 可选 类主体 + +类名 → 标识符 + +类主体 → { 声明列表 可选 } + + +###GRAMMAR OF A PROTOCOL DECLARATION + +‌ protocol-declaration → attributes opt protocol protocol-name type-inheritance-clause opt protocol-body + +‌ protocol-name → identifier + +‌ protocol-body → {protocol-member-declarations opt} + +‌ protocol-member-declaration → protocol-property-declaration + +‌ protocol-member-declaration → protocol-method-declaration + +‌ protocol-member-declaration → protocol-initializer-declaration + +‌ protocol-member-declaration → protocol-subscript-declaration + +‌ protocol-member-declaration → protocol-associated-type-declaration + +‌ protocol-member-declarations → protocol-member-declarationprotocol-member-declarations opt + + +###协议声明语法 +协议声明 → 属性列表 可选 **protocol** 协议名 类型继承子句 可选 协议主体 + +协议名 → 标识符 + +协议主体 → { 协议成员声明列表 可选 } + +协议成员声明 → 协议属性声明 + +协议成员声明 → 协议方法声明 + +协议成员声明 → 协议构造器声明 + +协议成员声明 → 协议下标脚本声明 + +协议成员声明 → 协议关联类型声明 + +协议成员声明列表 → 协议成员声明 协议成员声明列表 可选 + +###GRAMMAR OF A PROTOCOL PROPERTY DECLARATION + +‌ protocol-property-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block + +###协议属性声明语法 +协议属性声明 → 变量声明头 变量名 类型注解 getter-setter关键字块 + +###GRAMMAR OF A PROTOCOL METHOD DECLARATION + +‌ protocol-method-declaration → function-head function-namem generic-parameter-clause opt function-signature + +###协议方法声明语法 +协议方法声明 → 函数头 函数名 泛型参数子句 可选 函数签名 + + +###GRAMMAR OF A PROTOCOL INITIALIZER DECLARATION + +‌ protocol-initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause + +###协议构造器声明语法 +协议构造器声明 → 构造器头 泛型参数子句 可选 参数子句 + +###GRAMMAR OF A PROTOCOL SUBSCRIPT DECLARATION + +‌ protocol-subscript-declaration → subscript-head subscript-result getter-setter-keyword-block + +###协议下标脚本声明语法 +协议下标脚本声明 → 下标脚本头 下标脚本结果 getter-setter关键字块 + +###GRAMMAR OF A PROTOCOL ASSOCIATED TYPE DECLARATION + +‌ protocol-associated-type-declaration → typealias-head type-inheritance-clause opt typealias-assignment opt + +###协议关联类型声明语法 +协议关联类型声明 → 类型别名头 类型继承子句 可选 类型别名赋值 可选 + + +###GRAMMAR OF AN INITIALIZER DECLARATION + +‌ initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause initializer-body + +‌ initializer-head → attributes opt convenience opt init + +‌ initializer-body → code-block + +###构造器声明语法 + +构造器声明 → 构造器头 泛型参数子句 可选 参数子句 构造器主体 + +构造器头 → 属性列表 可选 **convenience** 可选 **init** + +构造器主体 → 代码块 + +###GRAMMAR OF A DEINITIALIZER DECLARATION + +‌ deinitializer-declaration → attributes opt deinitcode-block + +###析构器声明语法 + +析构器声明 → 属性列表 可选 **deinit** 代码块 + +###GRAMMAR OF AN EXTENSION DECLARATION + +‌ extension-declaration → extensiontype-identifiertype-inheritance-clause opt extension-body + +‌ extension-body → {declarations opt} + +###扩展声明语法 +扩展声明 → **extension** 类型标识 类型继承子句 可选 extension-主体 + +extension-主体 → { 声明列表 可选 } + +###GRAMMAR OF A SUBSCRIPT DECLARATION + +‌ subscript-declaration → subscript-head subscript-result code-block + +‌ subscript-declaration → subscript-head subscript-result getter-setter-block + +‌ subscript-declaration → subscript-head subscript-result getter-setter-keyword-block + +‌ subscript-head → attributes opt subscript parameter-clause + +‌ subscript-result → ->attributesopttype + +###下标脚本声明语法 +下标脚本声明 → 下标脚本头 下标脚本结果 代码块 + +下标脚本声明 → 下标脚本头 下标脚本结果 getter-setter块 + +下标脚本声明 → 下标脚本头 下标脚本结果 getter-setter关键字块 + +下标脚本头 → 属性列表 可选 下表脚本参数子句 + +下标脚本结果 → → 属性列表 可选 类型 + +###GRAMMAR OF AN OPERATOR DECLARATION + +‌ operator-declaration → prefix-operator-declaration | postfix-operator-declaration | infix-operator-declaration + +‌ prefix-operator-declaration → operator prefix operator{} + +‌ postfix-operator-declaration → operator postfix operator{} + +‌ infix-operator-declaration → operator infix operator{infix-operator-attributes opt} + +‌ infix-operator-attributes → precedence-clause opt associativity-clause opt + +‌ precedence-clause → precedence precedence-level + +‌ precedence-level → Digit 0 through 255 + +‌ associativity-clause → associativity associativity + +‌ associativity → left right none + +###运算符声明语法 +运算符声明 → 前置运算符声明 | 后置运算符声明 | 中置运算符声明 + +前置运算符声明 → **operator prefix** 运算符 { } + +后置运算符声明 → **operator postfix** 运算符 { } + +中置运算符声明 → **operator infix** 运算符 { 中置运算符属性 可选 } + +中置运算符属性 → 优先级子句 可选 结和性子句 可选 + +优先级子句 → **precedence** 优先级水平 + +优先级水平 → 数值 0 到 255 + +结和性子句 → **associativity** 结和性 + +结和性 → **left** | **right** | **none** + +##Patterns +##模式 + +###GRAMMAR OF A PATTERN + +‌ pattern → wildcard-patterntype-annotation opt + +‌ pattern → identifier-patterntype-annotation opt + +‌ pattern → value-binding-pattern + +‌ pattern → tuple-patterntype-annotation opt + +‌ pattern → enum-case-pattern + +‌ pattern → type-casting-pattern + +‌ pattern → expression-pattern + +###模式语法 + +模式 → 通配符模式 类型注解 可选 + +模式 → 标识符模式 类型注解 可选 + +模式 → 值绑定模式 + +模式 → 元组模式 类型注解 可选 + +模式 → 枚举用例模式 + +模式 → 类型转换模式 + +模式 → 表达式模式 + +###GRAMMAR OF A WILDCARD PATTERN + +‌ wildcard-pattern → _ + +###通配符模式语法 + +通配符模式 → **_** + +###GRAMMAR OF AN IDENTIFIER PATTERN + +‌ identifier-pattern → identifier + +###标识符模式语法 +标识符模式 → 标识符 + +###GRAMMAR OF A VALUE-BINDING PATTERN + +‌ value-binding-pattern → varpattern let pattern + +###值绑定模式语法 + +值绑定模式 → **var** 模式 | **let** 模式 + +###GRAMMAR OF A TUPLE PATTERN + +‌ tuple-pattern → (tuple-pattern-element-list opt) + +‌ tuple-pattern-element-list → tuple-pattern-element tuple-pattern-element,tuple-pattern-element-list + +‌ tuple-pattern-element → pattern + +###元组模式语法 +元组模式 → ( 元组模式元素列表 可选 ) + +元组模式元素列表 → 元组模式元素 | 元组模式元素 , 元组模式元素列表 + +元组模式元素 → 模式 + +###GRAMMAR OF AN ENUMERATION CASE PATTERN + +‌ enum-case-pattern → type-identifier opt.enum-case-nametuple-pattern opt + +###枚举用例模式语法 + +枚举用例模式 → 类型标识 可选 . 枚举的case名 元组模式 可选 + + +###GRAMMAR OF A TYPE CASTING PATTERN + +‌ type-casting-pattern → is-pattern as-pattern + +‌ is-pattern → istype + +‌ as-pattern → patternastype +###类型转换模式语法 +类型转换模式 → is模式 | as模式 + +is模式 → **is** 类型 + +as模式 → 模式 **as** 类型 + +###GRAMMAR OF AN EXPRESSION PATTERN + +‌ expression-pattern → expression + +###表达式模式语法 +表达式模式 → 表达式 + +##Attributes +##属性 + +###GRAMMAR OF AN ATTRIBUTE + +‌ attribute → @ attribute-nameattribute-argument-clause opt + +‌ attribute-name → identifier + +‌ attribute-argument-clause → (balanced-tokens opt) + +‌ attributes → attribute attributes opt + +‌ balanced-tokens → balanced-token balanced-tokens opt + +‌ balanced-token → (balanced-tokens opt) + +‌ balanced-token → [balanced-tokens opt] + +‌ balanced-token → {balanced-tokens opt} + +‌ balanced-token → Any identifier, keyword, literal, or operator + +‌ balanced-token → Any punctuation except (, ), [, ], {, or } +####属性语法 +属性 → @ 属性名 属性参数子句 可选 + +属性名 → 标识符 + +属性参数子句 → ( 平衡令牌列表 可选 ) + +属性列表 → 属性 属性列表 可选 + +平衡令牌列表 → 平衡令牌 平衡令牌列表 可选 + +平衡令牌 → **(** 平衡令牌列表 可选 **)** + +平衡令牌 → **[** 平衡令牌列表 可选 **]** + +平衡令牌 → **{** 平衡令牌列表 可选 **}** + +平衡令牌 → 任意标识符, 关键字, 字面量或运算符 + +平衡令牌 → 任意标点除了**(, ), [, ], {,** 或 **}** + +##Expressions +##表达式 +###GRAMMAR OF AN EXPRESSION + +‌ expression → prefix-expressionbinary-expressions opt + +‌ expression-list → expression | expression , expression-list + +###表达式语法 +表达式 → 前置表达式 二元表达式列表 可选 + +表达式列表 → 表达式 | 表达式 **,** 表达式列表 + +GRAMMAR OF A PREFIX EXPRESSION + +‌ prefix-expression → prefix-operator opt postfix-expression +‌ prefix-expression → in-out-expression +‌ in-out-expression → & identifier + +###前置表达式语法 +前置表达式 → 前置运算符 可选 后置表达式 + +前置表达式 → 写入写出表达式 + +写入写出表达式 → **&** 标识符 + +###GRAMMAR OF A BINARY EXPRESSION + +‌ binary-expression → binary-operator prefix-expression + +‌ binary-expression → assignment-operator prefix-expression + +‌ binary-expression → conditional-operator prefix-expression + +‌ binary-expression → type-casting-operator + +‌ binary-expressions → binary-expression binary-expressionsopt + +###二元表达式语法 +二元表达式 → 二元运算符 前置表达式 + +二元表达式 → 赋值运算符 前置表达式 + +二元表达式 → 条件运算符 前置表达式 + +二元表达式 → 类型转换运算符 + +二元表达式列表 → 二元表达式 二元表达式列表 可选 + +###GRAMMAR OF AN ASSIGNMENT OPERATOR + +‌ assignment-operator → = +###赋值运算符语法 +赋值运算符 → **=** + +###GRAMMAR OF A CONDITIONAL OPERATOR + +‌ conditional-operator → ? expression : + +###三元条件运算符语法 +三元条件运算符 → **?** 表达式 **:** + +###GRAMMAR OF A TYPE-CASTING OPERATOR + +‌ type-casting-operator → is type | as ? opttype + +###类型转换运算符语法 +类型转换运算符 → **is** 类型 | **as ?** 可选 类型 + +###GRAMMAR OF A PRIMARY EXPRESSION + +‌ primary-expression → identifiergeneric-argument-clause opt + +‌ primary-expression → literal-expression + +‌ primary-expression → self-expression + +‌ primary-expression → superclass-expression + +‌ primary-expression → closure-expression + +‌ primary-expression → parenthesized-expression + +‌ primary-expression → implicit-member-expression + +‌ primary-expression → wildcard-expression + + +###主表达式语法 +主表达式 → 标识符 泛型参数子句 可选 + +主表达式 → 字面量表达式 + +主表达式 → self表达式 + +主表达式 → 超类表达式 + +主表达式 → 闭包表达式 + +主表达式 → 圆括号表达式 + +主表达式 → 隐式成员表达式 + +主表达式 → 通配符表达式 + +###GRAMMAR OF A LITERAL EXPRESSION + +‌ literal-expression → literal + +‌ literal-expression → array-literal | dictionary-literal + +‌ literal-expression → __FILE__ | __LINE__ | __COLUMN__ | __FUNCTION__ + +‌ array-literal → [array-literal-items opt] + +‌ array-literal-items → array-literal-item,opt array-literal-item,array-literal-items + +‌ array-literal-item → expression + +‌ dictionary-literal → [dictionary-literal-items] [:] + +‌ dictionary-literal-items → dictionary-literal-item,opt dictionary-literal-item,dictionary-literal-items + +‌ dictionary-literal-item → expression:expression +###字面量表达式语法 +字面量表达式 → 字面量 + +字面量表达式 → 数组字面量 | 字典字面量 + +字面量表达式 → __FILE__ | __LINE__ | __COLUMN__ | __FUNCTION__ + +数组字面量 → [ 数组字面量项列表 可选 ] + +数组字面量项列表 → 数组字面量项 , 可选 | 数组字面量项 , 数组字面量项列表 + +数组字面量项 → 表达式 + +字典字面量 → [ 字典字面量项列表 ] | [ : ] + +字典字面量项列表 → 字典字面量项 , 可选 | 字典字面量项 , 字典字面量项列表 + +字典字面量项 → 表达式 : 表达式 + +###GRAMMAR OF A SELF EXPRESSION + +‌ self-expression → self + +‌ self-expression → self.identifier + +‌ self-expression → self[expression] + +‌ self-expression → self.init + +###Self 表达式语法 +self表达式 → **self** + +self表达式 → **self** **.** 标识符 + +self表达式 → **self** [ 表达式 ] + +self表达式 → **self . init** + +###GRAMMAR OF A SUPERCLASS EXPRESSION + +‌ superclass-expression → superclass-method-expression | superclass-subscript-expression |superclass-initializer-expression +‌ +superclass-method-expression → super.identifier +‌ +superclass-subscript-expression → super[expression] +‌ +superclass-initializer-expression → super.init +###超类表达式语法 +超类表达式 → 超类方法表达式 | 超类下标表达式 | 超类构造器表达式 + +超类方法表达式 → **super . **标识符 + +超类下标表达式 → **super [** 表达式 **]** + +超类构造器表达式 → **super . init** + +###GRAMMAR OF A CLOSURE EXPRESSION + +‌ closure-expression → {closure-signature opt statements} + +‌ closure-signature → parameter-clause function-result opt in + +‌ closure-signature → identifier-list function-result opt in + +‌ closure-signature → capture-listp arameter-clause function-result opt in + +‌ closure-signature → capture-list identifier-list function-result opt in + +‌ closure-signature → capture-list in + +‌ capture-list → [capture-specifierexpression] + +‌ capture-specifier → weak unowned unowned(safe) unowned(unsafe) + +###闭包表达式语法 + +闭包表达式 → **{** 闭包签名 可选 多条语句 **}** + +闭包签名 → 参数子句 函数结果 可选 **in** + +闭包签名 → 标识符列表 函数结果 可选 **in** + +闭包签名 → 捕获列表 参数子句 函数结果 可选 **in** + +闭包签名 → 捕获列表 标识符列表 函数结果 可选 **in** + +闭包签名 → 捕获列表 **in** + +捕获列表 → **[** 捕获说明符 表达式 **]** + +捕获说明符 → **weak** |**unowned** | **unowned(safe)** | **unowned(unsafe)** + +###GRAMMAR OF A IMPLICIT MEMBER EXPRESSION + +‌ implicit-member-expression → .identifier +###隐式成员表达式语法 +隐式成员表达式 → **.** 标识符 + +###GRAMMAR OF A PARENTHESIZED EXPRESSION + +‌ parenthesized-expression → (expression-element-list opt) + +‌ expression-element-list → expression-element | expression-element,expression-element-list + +‌ expression-element → expression | identifier:expression + +###圆括号表达式语法 + +圆括号表达式 → **(**表达式元素列表 可选 **)** + +表达式元素列表 → 表达式元素 | 表达式元素 , 表达式元素列表 + +表达式元素 → 表达式 | 标识符 **:** 表达式 + +###GRAMMAR OF A WILDCARD EXPRESSION + +‌ wildcard-expression → _ +###通配符表达式语法 +通配符表达式 → _ + +###GRAMMAR OF A POSTFIX EXPRESSION + +‌ postfix-expression → primary-expression + +‌ postfix-expression → postfix-expressionpostfix-operator + +‌ postfix-expression → function-call-expression + +‌ postfix-expression → initializer-expression + +‌ postfix-expression → explicit-member-expression + +‌ postfix-expression → postfix-self-expression + +‌ postfix-expression → dynamic-type-expression + +‌ postfix-expression → subscript-expression + +‌ postfix-expression → forced-value-expression + +‌ postfix-expression → optional-chaining-expression + +###后置表达式语法 + +后置表达式 → 主表达式 + +后置表达式 → 后置表达式 后置运算符 + +后置表达式 → 函数调用表达式 + +后置表达式 → 构造器表达式 + +后置表达式 → 显示成员表达式 + +后置表达式 → 后置self表达式 + +后置表达式 → 动态类型表达式 + +后置表达式 → 下标表达式 + +后置表达式 → 强制取值表达式 + +后置表达式 → 可选表达式 + +###GRAMMAR OF A FUNCTION CALL EXPRESSION + +‌ function-call-expression → postfix-expressionparenthesized-expression + +‌ function-call-expression → postfix-expressionparenthesized-expressionopttrailing-closure + +‌ trailing-closure → closure-expression +###函数调用表达式语法 +函数调用表达式 → 后置表达式 圆括号表达式 + +函数调用表达式 → 后置表达式 圆括号表达式 可选 后置闭包 + +后置闭包 → 闭包表达式 + +###GRAMMAR OF AN INITIALIZER EXPRESSION + +‌ initializer-expression → postfix-expression .init +###构造器表达式语法 +构造器表达式 → 后置表达式 **. init** + +###GRAMMAR OF AN EXPLICIT MEMBER EXPRESSION + +‌ explicit-member-expression → postfix-expression . decimal-digit + +‌ explicit-member-expression → postfix-expression . identifier generic-argument-clause opt + +###显式成员表达式语法 +显示成员表达式 → 后置表达式 . 十进制数字 + +显示成员表达式 → 后置表达式 . 标识符 泛型参数子句 可选 + +###GRAMMAR OF A SELF EXPRESSION + +‌ postfix-self-expression → postfix-expression .self + +###后置Self 表达式语法 +后置self表达式 → 后置表达式 **. self** + +###GRAMMAR OF A DYNAMIC TYPE EXPRESSION + +‌ dynamic-type-expression → postfix-expression . dynamicType + +###动态类型表达式语法 + +动态类型表达式 → 后置表达式 **. dynamicType** + +###GRAMMAR OF A SUBSCRIPT EXPRESSION + +‌ subscript-expression → postfix-expression [ expression-list ] + +###附属脚本表达式语法 +附属脚本表达式 → 后置表达式 **[** 表达式列表 **]** + +###GRAMMAR OF A FORCED-VALUE EXPRESSION + +‌ forced-value-expression → postfix-expression ! +###强制取值语法 + +强制取值表达式 → 后置表达式 **!** + +###GRAMMAR OF AN OPTIONAL-CHAINING EXPRESSION + +‌ optional-chaining-expression → postfix-expression ? +##可选表达式语法 +可选表达式 → 后置表达式 **?** + +##Lexical Structure +##词法结构 + +###GRAMMAR OF AN IDENTIFIER + +‌ identifier → identifier-head identifier-characters opt + +‌ identifier → ` identifier-head identifier-characters opt ` + +‌ identifier → implicit-parameter-name + +‌ identifier-list → identifier | identifier,identifier-list + +‌ identifier-head → Upper- or lowercase letter A through Z + +‌ identifier-head → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA + +‌ identifier-head → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF + +‌ identifier-head → U+0100–U+02FF, U+0370–U+167F,U+1681–U+180D, or U+180F–U+1DBF + +‌ identifier-head → U+1E00–U+1FFF + +‌ identifier-head → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F + +‌ identifier-head → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793 + +‌ identifier-head → U+2C00–U+2DFF or U+2E80–U+2FFF + +‌ identifier-head → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF + +‌ identifier-head → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44 + +‌ identifier-head → U+FE47–U+FFFD + +‌ identifier-head → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD + +‌ identifier-head → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD + +‌ identifier-head → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD + +‌ identifier-head → U+D0000–U+DFFFD or U+E0000–U+EFFFD + +‌ identifier-character → Digit 0 through 9 + +‌ identifier-character → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F + +‌ identifier-character → identifier-head + +‌ identifier-characters → identifier-characteridentifier-characters opt + +‌ implicit-parameter-name → $ decimal-digits + +###标识符语法 + +标识符 → 标识符头 标识符字符列表 可选 + +标识符 → ` 标识符头 标识符字符列表 可选 ` + +标识符 → 隐式参数名 + +标识符列表 → 标识符 | 标识符 , 标识符列表 + +标识符头 → 大写或者 小写的字母 A 到 Z + +标识符头 → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, 或者 U+00B7–U+00BA + +标识符头 → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, 或者 U+00F8–U+00FF + +标识符头 → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, 或者 U+180F–U+1DBF + +标识符头 → U+1E00–U+1FFF + +标识符头 → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, 或者 U+2060–U+206F + +标识符头 → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, 或者 U+2776–U+2793 + +标识符头 → U+2C00–U+2DFF 或者 U+2E80–U+2FFF + +标识符头 → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, 或者 U+3040–U+D7FF + +标识符头 → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, 或者 U+FE30–U+FE44 + +标识符头 → U+FE47–U+FFFD + +标识符头 → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, 或者 U+40000–U+4FFFD + +标识符头 → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, 或者 U+80000–U+8FFFD + +标识符头 → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, 或者 U+C0000–U+CFFFD + +标识符头 → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD + +标识符字符 → 数字 0 到 9 + +标识符字符 → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, 或者 U+FE20–U+FE2F + +标识符字符 → 标识符头 + +标识符字符列表 → 标识符字符 标识符字符列表 可选 + +隐式参数名 → **$** 十进制数字列表 + + +###GRAMMAR OF A LITERAL + +‌ literal → integer-literal | floating-point-literal | string-literal + +###字面量语法 +字面量 → 整型字面量 | 浮点数字面量 | 字符串字面量 + + +###GRAMMAR OF AN INTEGER LITERAL + +‌ integer-literal → binary-literal + +‌ integer-literal → octal-literal + +‌ integer-literal → decimal-literal + +‌ integer-literal → hexadecimal-literal + +‌ binary-literal → 0b binary-digitbinary-literal-characters opt + +‌ binary-digit → Digit 0 or 1 + +‌ binary-literal-character → binary-digit _ + +‌ binary-literal-characters → binary-literal-characterbinary-literal-characters opt + +‌ octal-literal → 0ooctal-digitoctal-literal-characters opt + +‌ octal-digit → Digit 0 through 7 + +‌ octal-literal-character → octal-digit _ + +‌ octal-literal-characters → octal-literal-characteroctal-literal-characters opt + +‌ decimal-literal → decimal-digitdecimal-literal-characters opt + +‌ decimal-digit → Digit 0 through 9 + +‌ decimal-digits → decimal-digitdecimal-digits opt + +‌ decimal-literal-character → decimal-digit _ + +‌ decimal-literal-characters → decimal-literal-characterdecimal-literal-characters opt + +‌ hexadecimal-literal → 0xhexadecimal-digithexadecimal-literal-characters opt + +‌ hexadecimal-digit → Digit 0 through 9, a through f, or A through F + +‌ hexadecimal-literal-character → hexadecimal-digit _ + +‌ hexadecimal-literal-characters → hexadecimal-literal-characterhexadecimal-literal-characters opt +###整型字面量语法 +整型字面量 → 二进制字面量 + +整型字面量 → 八进制字面量 + +整型字面量 → 十进制字面量 + +整型字面量 → 十六进制字面量 + +二进制字面量 → **0b** 二进制数字 二进制字面量字符列表 可选 + +二进制数字 → 数字 0 到 1 + +二进制字面量字符 → 二进制数字 | _ + +二进制字面量字符列表 → 二进制字面量字符 二进制字面量字符列表 可选 + +八进制字面量 →** 0o** 八进字数字 八进制字符列表 可选 + +八进字数字 → 数字 0 到 7 + +八进制字符 → 八进字数字 | _ + +八进制字符列表 → 八进制字符 八进制字符列表 可选 + +十进制字面量 → 十进制数字 十进制字符列表 可选 + +十进制数字 → 数字 0 到 9 + +十进制数字列表 → 十进制数字 十进制数字列表 可选 + +十进制字符 → 十进制数字 | _ + +十进制字符列表 → 十进制字符 十进制字符列表 可选 + +十六进制字面量 → **0x **十六进制数字 十六进制字面量字符列表 可选 + +十六进制数字 → 数字 0 到 9, a 到 f, or A 到 F + +十六进制字符 → 十六进制数字 | _ + +十六进制字面量字符列表 → 十六进制字符 十六进制字面量字符列表 可选 + +###GRAMMAR OF A FLOATING-POINT LITERAL + +‌ floating-point-literal → decimal-literal decimal-fraction opt decimal-exponentopt + +‌ floating-point-literal → hexadecimal-literal hexadecimal-fraction opt hexadecimal-exponent + +‌ decimal-fraction → .decimal-literal + +‌ decimal-exponent → floating-point-esign opt decimal-literal + +‌ hexadecimal-fraction → .hexadecimal-literal opt + +‌ hexadecimal-exponent → floating-point-psign opt hexadecimal-literal + +‌ floating-point-e → e | E + +‌ floating-point-p → p | P + +‌ sign → + | - + +###浮点型字面量语法 +浮点数字面量 → 十进制字面量 十进制分数 可选 十进制指数 可选 + +浮点数字面量 → 十六进制字面量 十六进制分数 可选 十六进制指数 + +十进制分数 → . 十进制字面量 + +十进制指数 → 浮点数e 正负号 可选 十进制字面量 + +十六进制分数 → . 十六进制字面量 可选 + +十六进制指数 → 浮点数p 正负号 可选 十六进制字面量 + +浮点数e → **e** | **E** + +浮点数p → **p** | **P** + +正负号 → **+** |** -** + +###GRAMMAR OF A STRING LITERAL + +‌ string-literal → "quoted-text" + +‌ quoted-text → quoted-text-item quoted-text opt + +‌ quoted-text-item → escaped-character + +‌ quoted-text-item → \(expression) + +‌ quoted-text-item → Any Unicode extended grapheme cluster except ", \, U+000A, or U+000D + +‌ escaped-character → \0 \\ \t \n \r \" \' + +‌ escaped-character → \x hexadecimal-digit hexadecimal-digit + +‌ escaped-character → \u hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit + +‌ escaped-character → \U hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit + +###字符型字面量语法 +字符串字面量 → **"** 引用文本** "** + +引用文本 → 引用文本条目 引用文本 可选 + +引用文本条目 → 转义字符 + +引用文本条目 → **\(** 表达式 **)** + +引用文本条目 → 除了"­, \­, U+000A, 或者 U+000D外的所有Unicode的字符 + +转义字符 → **\0**| **\\** | **\t** | **\n** | **\r** | **\"** | **\'** + +转义字符 → **\x** 十六进制数字 十六进制数字 + +转义字符 → **\u** 十六进制数字 十六进制数字 十六进制数字 十六进制数字 + +转义字符 → **\U** 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 + +###GRAMMAR OF OPERATORS + +‌ operator → operator-character operator opt + +‌ operator-character → / = - + ! * % < > & | ^ ~ . + + +‌ binary-operator → operator + +‌ prefix-operator → operator + +‌ postfix-operator → operator + +###运算符语法语法 + +运算符 → 运算符字符 运算符 可选 + +运算符字符 → **/** | **=** | **-** | **+** | **!** | ***** | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **.** + +二元运算符 → 运算符 + +前置运算符 → 运算符 + +后置运算符 → 运算符 + +##Types +##类型 + +###GRAMMAR OF A TYPE + +‌ type → array-type | function-type | type-identifier | tuple-type | optional-type | implicitly-unwrapped-optional-type | protocol-composition-type | metatype-type + +###类型语法 +类型 → 数组类型 | 函数类型 | 类型标识 | 元组类型 | 可选类型 | 隐式解析可选类型 | 协议合成类型 | 元型类型 + + +###GRAMMAR OF A TYPE ANNOTATION + +‌ type-annotation → :attributes opt type +###类型注解语法 + +类型注解 → : 属性列表 可选 类型 + +###GRAMMAR OF A TYPE IDENTIFIER + +‌ type-identifier → type-name generic-argument-clause opt type-name generic-argument-clauseopt.type-identifier + +‌ type-name → identifier +###类型标识语法 +类型标识 → 类型名称 泛型参数子句 可选 | 类型名称 泛型参数子句 可选 . 类型标识 + +类名 → 标识符 + +###GRAMMAR OF A TUPLE TYPE + +‌ tuple-type → (tuple-type-body opt) + +‌ tuple-type-body → tuple-type-element-list...opt + +‌ tuple-type-element-list → tuple-type-element | tuple-type-element,tuple-type-element-list + +‌ tuple-type-element → attributes opt inout opt type inout opt element-nametype-annotation + +‌ element-name → identifier + +###元组类型语法 +元组类型 → ( 元组类型主体 可选 ) + +元组类型主体 → 元组类型的元素列表 ... 可选 + +元组类型的元素列表 → 元组类型的元素 | 元组类型的元素 , 元组类型的元素列表 + +元组类型的元素 → 属性列表 可选 **inout** 可选 类型 | **inout** 可选 元素名 类型注解 + +元素名 → 标识符 + +###GRAMMAR OF A FUNCTION TYPE + +‌ function-type → type->type + +###函数类型语法 +函数类型 → 类型 → 类型 + +###GRAMMAR OF AN ARRAY TYPE + +‌ array-type → type | array-type +###数组类型语法 +数组类型 → 类型** [ ]** 数组类型 **[ ]** + +###GRAMMAR OF AN OPTIONAL TYPE + +‌ optional-type → type ? + +###可选类型语法 +可选类型 → 类型 **?** + +###GRAMMAR OF AN IMPLICITLY UNWRAPPED OPTIONAL TYPE + +‌ implicitly-unwrapped-optional-type → type ! + +###隐式解析可选类型语法 + +隐式解析可选类型 → 类型 ! + +###GRAMMAR OF A PROTOCOL COMPOSITION TYPE + +‌ protocol-composition-type → protocol + +‌ protocol-identifier-list → protocol-identifier protocol-identifier,protocol-identifier-list + +‌ protocol-identifier → type-identifier + +###协议合成类型语法 +协议合成类型 → **protocol** < 协议标识符列表 可选 > + +协议标识符列表 → 协议标识符 | 协议标识符 , 协议标识符列表 + +协议标识符 → 类型标识 + +###GRAMMAR OF A METATYPE TYPE + +‌ metatype-type → type.Type type.Protocol +###元类型语法 +元类型 → 类型 **.** **Type** | 类型 **.** **Protocol** + +###GRAMMAR OF A TYPE INHERITANCE CLAUSE + +‌ type-inheritance-clause → :type-inheritance-list + +‌ type-inheritance-list → type-identifier | type-identifier,type-inheritance-list + +###类型继承子句语法 +类型继承子句 → : 类型继承列表 + +类型继承列表 → 类型标识 | 类型标识 , 类型继承列表 + diff --git a/src/words.md b/src/words.md index cb4ca6e..17e4a8b 100644 --- a/src/words.md +++ b/src/words.md @@ -1 +1,19 @@ -Class - 类 - Classes, structures, and enumerations can define subscripts \ No newline at end of file + +- Class - 类 - Classes, structures, and enumerations can define subscripts +- attribute - 特性 +- property - 属性 +- initializer - 构造函数?(和Constructor怎么区分?) +- designated initializer - 预设构造函数 +- convenience initializer - 便利构造函数 +- argument - 参数(实际参数) +- parameter - 参数(形式参数) --> argument 和 parameter 的区别参考[这篇文章](http://www.blogjava.net/flysky19/articles/89963.html) +- overload 重载 +- override 覆盖 +- adopt - 适配。但是一般用于被动。如 `a protocol can be adopted by a class`: 协议可以被类适配。 +- conform - 遵循。如果一个类适配了一个协议,我们就说这个类遵循这个协议。`this class conforms to that protocol`。 +- literal value - 字面量 - A literal value is a value that appears directly in your source code, such as 42 and 3.14159 in the examples below. +- as described in - 详见 - You can provide a default value for a stored property as part of its definition, as described in Default Property Values. +- access - 引用 - you can use the element names to access the values of those elements. +- computed propertie - 实例属性 - This behavior is communicated by a getter and setter in the same way as for computed properties: +- example - 示例 - The example below defines a structure called FixedLengthRange, which describes a range of integers whose range length cannot be changed once it is created +