热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Swift4.2语言指南(十一)结构和类

结构和类是通用的,灵活的结构,它们成为程序代码的构建块。您可以使用与定义常量,变量和函数相同的语法来定义属性和方法,以向结构和类添加功能。与其他编程语言不同,Swift不要求您为自定义结构和类创建

结构是通用的,灵活的结构,它们成为程序代码的构建块。您可以使用与定义常量,变量和函数相同的语法来定义属性和方法,以向结构和类添加功能。

与其他编程语言不同,Swift不要求您为自定义结构和类创建单独的接口和实现文件。在Swift中,您可以在单个文件中定义结构或类,并且该类或结构的外部接口可自动用于其他代码以供使用。

注意

类的实例传统上称为对象然而Swift结构和类在功能上比在其他语言更接近,并且多本章的描述适用于实例功能一类或结构类型。因此,使用了更通用的术语实例

结构和类的比较

Swift中的结构和类有许多共同点。两者都可以:

  • 定义存储值的属性
  • 定义提供功能的方法
  • 定义下标以使用下标语法提供对其值的访问
  • 定义初始化程序以设置其初始状态
  • 扩展以扩展其功能,超越默认实现
  • 符合协议以提供某种标准功能

有关更多信息,请参阅属性方法,下初始化扩展协议

类具有结构不具备的其他功能:

  • 继承使一个类可以继承另一个类的特性。
  • 类型转换使您可以在运行时检查和解释类实例的类型。
  • Deinitializers使类的实例可以释放它已分配的任何资源。
  • 引用计数允许对类实例的多个引用。

有关更多信息,请参阅继承类型转换取消初始化自动引用计数

类支持的附加功能是以增加复杂性为代价的。作为一般准则,更喜欢结构和枚举,因为它们更容易推理,并在适当或必要时使用类。实际上,这意味着您定义的大多数自定义数据类型都是结构和枚举。有关更详细的比较,请参阅在结构和类之间进行选择

定义语法

结构和类具有类似的定义语法。您将使用struct关键字和带有class关键字的类来引入结构两者都把它们的整个定义放在一对括号中:

1 struct SomeStructure {
2     // structure definition goes here
3 }
4 class SomeClass {
5     // class definition goes here
6 }

注意

无论何时定义新结构或类,都要定义新的Swift类型。给出类型UpperCamelCase名称(如SomeStructureSomeClass这里)来匹配标准斯威夫特类型的资本(如StringIntBool)。提供属性和方法lowerCamelCase名称(例如frameRateincrementCount)以区别于类型名称。

这是结构定义和类定义的示例:

 1 struct Resolution {
 2     var width = 0
 3     var height = 0
 4 }
 5 class VideoMode {
 6     var resolution = Resolution()
 7     var interlaced = false
 8     var frameRate = 0.0
 9     var name: String?
10 }

上面的示例定义了一个名为的新结构Resolution,用于描述基于像素的显示分辨率。此结构有两个存储的属性,称为widthheight存储的属性是捆绑起来并作为结构或类的一部分存储的常量或变量。Int通过将它们设置为初始整数值,可以推断出这两个属性的类型和初始值为0

上面的示例还定义了一个名为的新类VideoMode,用于描述视频显示的特定视频模式。该类有四个变量存储属性。第一个,resolution用一个新的Resolution结构实例初始化,推断出一个属性类型Resolution对于其他三个属性,新的VideoMode实例将与被初始化interlaced的设置false(意为“逐行视频”),的播放帧速率0.0,和一个可选的String调用值namename属性自动被赋予默认值nil,或“无name值”,因为它是可选类型。

结构和类实例

Resolution结构定义和VideoMode类定义只说明什么ResolutionVideoMode看起来像。它们本身并不描述特定的分辨率或视频模式。为此,您需要创建结构或类的实例。

对于结构和类,创建实例的语法非常相似:

1 let someResolution = Resolution()
2 let someVideoMode = VideoMode()

结构和类都使用初始化器语法用于新实例。最简单的初始化语法形式使用类或结构的类型名称,后跟空括号,例如Resolution()VideoMode()这将创建类或结构的新实例,并将任何属性初始化为其默认值。初始化中更详细地描述了类和结构初始化

访问属性

您可以使用点语法访问实例的属性在点语法中,您在实例名称后面立即编写属性名称,用句点(.分隔,不带任何空格:

1 print("The width of someResolution is \(someResolution.width)")
2 // Prints "The width of someResolution is 0"

在此示例中,someResolution.width引用的width属性为someResolution,并返回其默认初始值0

您可以深入查看子属性,例如width属性中的resolution属性VideoMode

1 print("The width of someVideoMode is \(someVideoMode.resolution.width)")
2 // Prints "The width of someVideoMode is 0"

您还可以使用点语法为变量属性分配新值:

1 someVideoMode.resolution.width = 1280
2 print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
3 // Prints "The width of someVideoMode is now 1280"

结构类型的成员初始化器

所有结构都有一个自动生成的成员初始化程序,您可以使用它初始化新结构实例的成员属性。可以通过名称将新实例的属性的初始值传递给成员初始值设定项:

1 let vga = Resolution(width: 640, height: 480)

与结构不同,类实例不接收默认的成员初始值设定项。初始化中更详细地描述在初始化

结构和枚举都是值类型

值类型是一个类型,其值被复制时,它的分配给一个变量或常数,或当它传递给函数。

在前面的章节中,您实际上已经广泛使用了值类型。实际上,Swift中整数,浮点数,布尔值,字符串,数组和字典中的所有基本类型都是值类型,并且在幕后实现为结构。

所有结构和枚举都是Swift中的值类型。这意味着您创建的任何结构和枚举实例以及它们作为属性的任何值类型在代码中传递时始终会被复制。

注意

标准库(如数组,字典和字符串)定义的集合使用优化来降低复制的性能成本。这些集合不是立即复制,而是共享内存,其中元素存储在原始实例和任何副本之间。如果修改了集合的其中一个副本,则在修改之前复制元素。您在代码中看到的行为总是好像立即发生了复制。

考虑这个示例,它使用Resolution前一个示例中结构:

1 let hd = Resolution(width: 1920, height: 1080)
2 var cinema = hd

此示例声明一个调用的常量hd,并将其设置为使用Resolution全高清视频(1920像素宽,1080像素高)的宽度和高度初始化实例。

然后它声明一个被调用的变量cinema并将其设置为当前值hd因为Resolution是结构,所以会创建现有实例副本,并将此新副本分配给cinema虽然hdcinema现在有相同的宽度和高度,他们是幕后两种完全不同的情况。

接下来,将width属性cinema修改为用于数字电影投影(2048像素宽和1080像素高)的稍宽2K标准的宽度:

cinema.width = 2048

检查width属性cinema显示它确实已更改为2048

1 print("cinema is now \(cinema.width) pixels wide")
2 // Prints "cinema is now 2048 pixels wide"

但是,width原始hd实例属性仍具有旧值1920

1 print("hd is still \(hd.width) pixels wide")
2 // Prints "hd is still 1920 pixels wide"

cinema给出当前值时hd存储hd被复制到新cinema实例中。最终结果是两个完全独立的实例,它们包含相同的数值。但是,因为它们是单独的实例,所以设置宽度cinemato 2048不会影响存储的宽度hd,如下图所示:

../_images/sharedStateStruct_2x.png

相同的行为适用于枚举:

 1 enum CompassPoint {
 2     case north, south, east, west
 3     mutating func turnNorth() {
 4         self = .north
 5     }
 6 }
 7 var currentDirection = CompassPoint.west
 8 let rememberedDirection = currentDirection
 9 currentDirection.turnNorth()
10 
11 print("The current direction is \(currentDirection)")
12 print("The remembered direction is \(rememberedDirection)")
13 // Prints "The current direction is north"
14 // Prints "The remembered direction is west"

rememberedDirection赋值为currentDirection,它实际上设置为该值的副本。更改currentDirection此后的值不会影响存储的原始值的副本rememberedDirection

类是引用类型

与值类型不同,引用类型分配给变量或常量时或者传递给函数时不会被复制。而不是副本,使用对同一现有实例的引用。

这是一个例子,使用VideoMode上面定义类:

1 let tenEighty = VideoMode()
2 tenEighty.resolution = hd
3 tenEighty.interlaced = true
4 tenEighty.name = "1080i"
5 tenEighty.frameRate = 25.0

此示例声明一个调用的新常量tenEighty,并将其设置为引用VideoMode该类的新实例视频模式被分配的HD分辨率的副本,1920通过1080从之前。它被设置为隔行扫描,并且被命名为"1080i"最后,它被设置为25.0每秒帧数的帧速率

接下来,tenEighty分配一个名为的新常量alsoTenEighty,并alsoTenEighty修改帧速率

1 let alsoTenEighty = tenEighty
2 alsoTenEighty.frameRate = 30.0

由于类是引用类型,tenEightyalsoTenEighty实际上都指向同一个 VideoMode实例。实际上,它们只是同一个实例的两个不同名称,如下图所示:

../_images/sharedStateClass_2x.png

检查frameRate属性tenEighty表明它正确地报告30.0了基础VideoMode实例的新帧速率

1 print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
2 // Prints "The frameRate property of tenEighty is now 30.0"

此示例还显示了引用类型如何更难以推理。如果tenEightyalsoTenEighty人相距甚远在你的程序的代码,它可能是很难找到的视频模式改变的所有方式。无论您在何处使用tenEighty,您还必须考虑使用的代码,alsoTenEighty反之亦然。相反,值类型更容易推理,因为与相同值交互的所有代码在源文件中都是紧密相连的。

请注意,tenEighty并将alsoTenEighty其声明为常量,而不是变量。但是,您仍然可以更改tenEighty.frameRatealsoTenEighty.frameRate因为tenEightyalsoTenEighty常量的值本身并未实际更改。tenEighty并且alsoTenEighty他们自己不“存储” VideoMode实例 - 相反,他们都引用VideoMode了幕后实例。是被更改frameRate的底层属性VideoMode,而不是对其的常量引用的值VideoMode

身份运算符

因为类是引用类型,所以多个常量和变量可能在后台引用同一个类的单个实例。(对于结构和枚举,情况也是如此,因为它们在分配给常量或变量或传递给函数时总是被复制。)

有时可以找出两个常量或变量是否指向完全相同的类实例。为了实现这一点,Swift提供了两个身份运算符:

  • 与(===相同
  • 与(!==不一样

使用这些运算符来检查两个常量或变量是否引用同一个实例:

1 if tenEighty === alsoTenEighty {
2     print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
3 }
4 // Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

注意,(由三个等号表示,或===)相同并不等同于(由两个等号表示,或==)。此类似意味着类类型的两个常量或变量引用完全相同的类实例。等于意味着两个实例被认为是相等或等效的值,为的一些适当的含义相同,通过该类型的设计者所定义的。

当您定义自己的自定义结构和类时,您有责任确定两个实例相等的条件。等价运算符中描述了定义自己的“等于”和“不等于”运算符的实现的过程

指针

如果您有使用C,C ++或Objective-C的经验,您可能知道这些语言使用指针来引用内存中的地址。引用某个引用类型的实例的Swift常量或变量类似于C中的指针,但它不是指向内存中地址的直接指针,并且不需要您编写星号(*)来指示你正在创建一个参考。相反,这些引用的定义与Swift中的任何其他常量或变量一样。标准库提供指针和缓冲区类型,如果需要直接与指针交互,可以使用它们 - 请参阅手动内存管理


推荐阅读
author-avatar
灵动的音乐xl
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有