Classesandstructuresare 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 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
NOTE
An instance of aclassis traditionally known as anobject. 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 ofeithera class or a structure type. Because of this, the more general terminstanceis used.
通常一个类的实例被称为对象。然而在 Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用实例。
Comparing Classes and Structures (类和结构体对比)
Classes and structures in Swift have many things in common. Both can:
Swift 中类和结构体有很多共同点。共同处在于:
1. Define properties to store values
定义属性用于存储值
2. Define methods to provide functionality
定义方法用于提供功能
3. Define subscripts to provide access to their values using subscript syntax
定义下标操作使得可以通过下标语法来访问实例所包含的值
4. Define initializers to set up their initial state
定义构造器用于生成初始化值
5. Be extended to expand their functionality beyond a default implementation
通过扩展以增加默认实现的功能
6. Conform to protocols to provide standard functionality of a certain kind
实现协议以提供某种标准功能
For more information, seeProperties,Methods,Subscripts,Initialization,Extensions, andProtocols.
更多信息请参见属性,方法,下标,构造过程,扩展,和协议。
Classes have additional capabilities that structures do not:
与结构体相比,类还有如下的附加功能:
1. Inheritance enables one class to inherit the characteristics of another.
继承允许一个类继承另一个类的特征
2. Type casting enables you to check and interpret the type of a class instance at runtime.
类型转换允许在运行时检查和解释一个类实例的类型
3. Deinitializers enable an instance of a class to free up any resources it has assigned.
析构器允许一个类实例释放任何其所被分配的资源
4. Reference counting allows more than one reference to a class instance.
引用计数允许对一个类的多次引用
For more information, seeInheritance,Type Casting,Deinitialization, andAutomatic 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 theclasskeyword and structures with thestructkeyword. 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 typesUpperCamelCasenames (such asSomeClassandSomeStructurehere) to match the capitalization of standard Swift types (such asString,Int, andBool). Conversely, always give properties and methodslowerCamelCasenames (such asframeRateandincrementCount) to differentiate them from type names.
在你每次定义一个新类或者结构体的时候,实际上你是定义了一个新的 Swift 类型。因此请使用UpperCamelCase这种方式来命名(如SomeClass和SomeStructure等),以便符合标准 Swift 类型的大写命名风格(如String,Int和Bool)。相反的,请使用lowerCamelCase这种方式为属性和方法命名(如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 calledResolution, to describe a pixel-based display resolution. This structure has two stored properties calledwidthandheight. 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 typeIntby setting them to an initial integer value of0.
在上面的示例中我们定义了一个名为Resolution的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为width和height的存储属性。存储属性是被捆绑和存储在类或结构体中的常量或变量。当这两个属性被初始化为整数0的时候,它们会被推断为Int类型。
The example above also defines a new class calledVideoMode, to describe a specific video mode for video display. This class has four variable stored properties. The first,resolution, is initialized with a newResolutionstructure instance, which infers a property type ofResolution. For the other three properties, newVideoModeinstances will be initialized with aninterlacedsetting offalse(meaning “noninterlaced video”), a playback frame rate of0.0, and an optionalStringvalue calledname. Thenameproperty is automatically given a default value ofnil, or “nonamevalue”, because it is of an optional type.
在上面的示例中我们还定义了一个名为VideoMode的类,用来描述一个视频显示器的特定模式。这个类包含了四个变量存储属性。第一个是分辨率,它被初始化为一个新的Resolution结构体的实例,属性类型被推断为Resolution。新VideoMode实例同时还会初始化其它三个属性,它们分别是,初始值为false的interlaced,初始值为0.0的frameRate,以及值为可选String的name。name属性会被自动赋予一个默认值nil,意为“没有name值”,因为它是一个可选类型。
Class and Structure Instances (类和结构体实例)
TheResolutionstructure definition and theVideoModeclass definition only describe what aResolutionorVideoModewill 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。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为了描述一个特定的分辨率或者视频模式,我们需要生成一个它们的实例。
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 asResolution()orVideoMode(). 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 inInitialization.
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如Resolution()或VideoMode()。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。构造过程章节会对类和结构体的初始化进行更详细的讨论。
Accessing Properties (属性访问)
You can access the properties of an instance usingdot syntax. In dot syntax, you write the property name immediately after the instance name, separated by a period (.), without any spaces:
通过使用点语法,你可以访问实例的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
print("The width of someResolution is\(someResolution.width)")
// Prints "The width of someResolution is 0"
In this example,someResolution.widthrefers to thewidthproperty ofsomeResolution, and returns its default initial value of0.
在上面的例子中,someResolution.width引用someResolution的width属性,返回width的初始值0。
You can drill down into sub-properties, such as thewidthproperty in theresolutionproperty of aVideoMode:
你也可以访问子属性,如VideoMode中Resolution属性的width属性:
print("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
print("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, thewidthproperty of theresolutionproperty ofsomeVideoModeis set directly, without your needing to set the entireresolutionproperty to a new value.
与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了someVideoMode中resolution属性的width这个子属性,以上操作并不需要重新为整个resolution属性设置新值。
Memberwise Initializers for Structure Types (结构体类型的成员逐一构造器)
All structures have an automatically-generatedmemberwise 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 inInitialization.
与结构体不同,类实例没有默认的成员逐一构造器。构造过程章节会对构造器进行更详细的讨论。
Structures and Enumerations Are Value Types (结构体和枚举是值类型)
Avalue typeis a type whose value iscopiedwhen 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 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,并且在底层都是以结构体的形式所实现。
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 theResolutionstructure from the previous example:
请看下面这个示例,其使用了前一个示例中的Resolution结构体:
let hd = Resolution(width:1920,height:1080)
var cinema = hd
This example declares a constant calledhdand sets it to aResolutioninstance initialized with the width and height of full HD video (1920pixels wide by1080pixels high).
在以上示例中,声明了一个名为hd的常量,其值为一个初始化为全高清视频分辨率(1920像素宽,1080像素高)的Resolution实例。
It then declares a variable calledcinemaand sets it to the current value ofhd. BecauseResolutionis a structure, acopyof the existing instance is made, and this new copy is assigned tocinema. Even thoughhdandcinemanow have the same width and height, they are two completely different instances behind the scenes.
然后示例中又声明了一个名为cinema的变量,并将hd赋值给它。因为Resolution是一个结构体,所以cinema的值其实是hd的一个拷贝副本,而不是hd本身。尽管hd和cinema有着相同的宽(width)和高(height),但是在幕后它们是两个完全不同的实例。
Next, the width property of cinema is amended to be the width of the slightly-wider 2K standard used for digital cinema projection (2048pixels wide and1080pixels high):
下面,为了符合数码影院放映的需求(2048像素宽,1080像素高),cinema的width属性需要作如下修改:
cinema.width=2048
Checking the width property of cinema shows that it has indeed changed to be2048:
这里,将会显示cinema的width属性确已改为了2048:
print("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 of1920:
然而,初始的hd实例中width属性还是1920:
print("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实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将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 {
print("The remembered direction is still .west")
}
// Prints "The remembered direction is still .west"
WhenrememberedDirectionis assigned the value ofcurrentDirection, it is actually set to a copy of that value. Changing the value ofcurrentDirectionthereafter does not affect the copy of the original value that was stored inrememberedDirection.
上例中rememberedDirection被赋予了currentDirection的值,实际上它被赋予的是值的一个拷贝。赋值过程结束后再修改currentDirection的值并不影响rememberedDirection所储存的原始值的拷贝。
Classes Are Reference Types (类是引用类型)
Unlike value types,reference typesarenotcopied 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 theVideoModeclass 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 calledtenEightyand sets it to refer to a new instance of theVideoModeclass. The video mode is assigned a copy of the HD resolution of1920by1080from before. It is set to be interlaced, and is given a name of"1080i". Finally, it is set to a frame rate of25.0frames per second.
以上示例中,声明了一个名为tenEighty的常量,其引用了一个VideoMode类的新实例。在之前的示例中,这个视频模式(video mode)被赋予了HD分辨率(1920*1080)的一个拷贝(即hd实例)。同时设置为interlaced,命名为“1080i”。最后,其帧率是25.0帧每秒。
Next,tenEightyis assigned to a new constant, calledalsoTenEighty, and the frame rate ofalsoTenEightyis modified:
然后,tenEighty被赋予名为alsoTenEighty的新常量,同时对alsoTenEighty的帧率进行修改:
let alsoTenEighty=tenEighty
alsoTenEighty.frameRate=30.0
Because classes are reference types,tenEightyandalsoTenEightyactually both refer to thesameVideoModeinstance. Effectively, they are just two different names for the same single instance.
因为类是引用类型,所以tenEight和alsoTenEight实际上引用的是相同的VideoMode实例。换句话说,它们是同一个实例的两种叫法。
Checking theframeRateproperty oftenEightyshows that it correctly reports the new frame rate of30.0from the underlyingVideoModeinstance:
下面,通过查看tenEighty的frameRate属性,我们会发现它正确的显示了所引用的VideoMode实例的新帧率,其值为30.0:
print("The frameRate property of tenEighty is now\(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
Note thattenEightyandalsoTenEightyare declared asconstants, rather than variables. However, you can still changetenEighty.frameRateandalsoTenEighty.frameRatebecause the values of thetenEightyandalsoTenEightyconstants themselves do not actually change.tenEightyandalsoTenEightythemselves do not “store” theVideoModeinstance—instead, they bothreferto aVideoModeinstance behind the scenes. It is theframeRateproperty of the underlyingVideoModethat is changed, not the values of the constant references to thatVideoMode.
需要注意的是tenEighty和alsoTenEighty被声明为常量而不是变量。然而你依然可以改变tenEighty.frameRate和alsoTenEighty.frameRate,因为tenEighty和alsoTenEighty这两个常量的值并未改变。它们并不“存储”这个VideoMode实例,而仅仅是对VideoMode实例的引用。所以,改变的是被引用的VideoMode的frameRate属性,而不是引用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 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:
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:
Identical to (===)
Not identical to (!==)
Use these operators to check whether two constants or variables refer to the same single instance:
运用这两个运算符检测两个常量或者变量是否引用同一个实例:
if tenEighty === alsoTenEighty{
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode 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==):
请注意,“等价于”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同:
1. “Identical to” means that two constants or variables of class type refer to exactly the same class instance.
“等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
2. “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 inEquivalence Operators.
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节等价操作符中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
Pointers (指针)
If you have experience with C, C++, or Objective-C, you may know that these languages usepointersto 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 byvalue, and class instances are always passed byreference. 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:
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
1. The structure’s primary purpose is to encapsulate a few relatively simple data values.
该数据结构的主要目的是用来封装少量相关简单数据值。
2. 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.
有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用。
3. Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
该数据结构中储存的值类型属性,也应该被拷贝,而不是被引用。
4. The structure does not need to inherit properties or behavior from another existing type.
该数据结构不需要去继承另一个既有类型的属性或者行为。
Examples of good candidates for structures include:
举例来说,以下情境中适合使用结构体:
1. The size of a geometric shape, perhaps encapsulating awidthproperty and aheightproperty, both of typeDouble.
几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
2. A way to refer to ranges within a series, perhaps encapsulating astartproperty and alengthproperty, both of typeInt.
一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
3. A point in a 3D coordinate system, perhaps encapsulatingx,yandzproperties, each of typeDouble.
三维坐标系内一点,封装x,y和z属性,三者均为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.
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
Assignment and Copy Behavior for Strings, Arrays, and Dictionaries (字符串、数组、和字典类型的赋值与复制行为)
In Swift, many basic data types such asString,Array, andDictionaryare implemented as structures. This means that data such as strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.
Swift 中,许多基本类型,诸如String,Array和Dictionary类型均以结构体的形式实现。这意味着被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会被拷贝。
This behavior is different from Foundation:NSString,NSArray, andNSDictionaryare implemented as classes, not structures. Strings, arrays, and dictionaries in Foundation are always assigned and passed around as a reference to an existing instance, rather than as a copy.
Objective-C 中NSString,NSArray和NSDictionary类型均以类的形式实现,而并非结构体。它们在被赋值或者被传入函数或方法时,不会发生值拷贝,而是传递现有实例的引用。
NOTE
The description above refers to the “copying” of strings, arrays, and dictionaries. The behavior you see in your code will always be as if a copy took place. However, Swift only performs anactualcopy 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 管理所有的值拷贝以确保性能最优化,所以你没必要去回避赋值来保证性能最优化。