百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

Swift 4.1 官方文档大全(第十章)类和结构

connygpt 2024-09-29 11:06 6 浏览

类和结构(Classes and structures)

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

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

注意

传统上将类的实例称为对象。但是,Swift类和结构在功能上比其他语言更接近,本章的大部分内容都描述了可以应用于类或结构类型实例的功能。因此,使用更一般的术语实例。

比较类和结构

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

定义属性以存储值

定义提供功能的方法

使用下标语法定义下标以提供对其值的访问

定义初始化程序以设置其初始状态

扩展到超出默认实现范围的功能

符合协议以提供某种标准功能

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

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

继承. 使一个类能够继承另一个类的特性。

类型转换 使您能够在运行时检查和解释类实例的类型。

去初始化器 使类的一个实例释放它分配的任何资源。

引用计数 允许对一个类实例的多个引用。

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

注意

结构在代码中传递时总是被复制,并且不使用引用计数。

定义语法

类和结构具有类似的定义语法。您可以使用struct关键字class关键字。两者都将其整个定义放在一对大括号内:

class SomeClass {

// class definition goes here

}

struct SomeStructure {

// structure definition goes here

}

注意

每当你定义一个新的类或结构时,你都可以有效地定义一个全新的Swift类型。 给类型UpperCamelCase名称(如SomeClass和SomeStructure这里)以匹配标准Swift类型(如String,Int和Bool)的大小写。 相反,总是给lowerCamelCase名称(如frameRate和incrementCount)的属性和方法区分它们和类型名称。

下面是一个结构定义和类定义的例子:

struct Resolution {

var width = 0

var height = 0

}

classVideoMode {

var resolution = Resolution()

var interlaced = false

var frameRate = 0.0

var name: String?

}

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

上面的例子还定义了一个名为VideoMode的新类,用于描述视频显示的特定视频模式。这个类有四个变量存储的属性。第一个解决方案使用新的Resolution结构实例进行初始化,该实例推断出Resolution的属性类型。对于其他三个属性,新的VideoMode实例将使用隔行设置为false(意为“非隔行视频”),0.0的回放帧率和一个名为name的可选String值进行初始化。 name属性会自动给出默认值nil或“no name value”,因为它是可选类型。

类和结构实例

分辨率结构定义和VideoMode类定义仅描述分辨率或视频模式的外观。他们自己没有描述特定的分辨率或视频模式。要做到这一点,你需要创建一个结构或类的实例。

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

let someResolution = Resolution()

let someVideoMode = VideoMode()

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

访问属性

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

print("The width of someResolution is \(someResolution.width)")

// Prints "The width of someResolution is 0"

在这个例子中,someResolution.width指向someResolution的width属性,并返回其默认的初始值0。

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

print("The width of someVideoMode is \(someVideoMode.resolution.width)")

// Prints "The width of someVideoMode is 0"

您也可以使用(.)语法为变量属性指定一个新值:

someVideoMode.resolution.width = 1280

print("The width of someVideoMode is now \(someVideoMode.resolution.width)")

// Prints "The width of someVideoMode is now 1280"

注意

与Objective-C不同的是,Swift使您能够直接设置结构属性的子属性。 在上面的最后一个例子中,someVideoMode的resolution属性的width属性是直接设置的,不需要将整个分辨率属性设置为新值。

结构类型的成员初始化程序

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

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

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

结构和枚举是值类型

值类型是一种类型,其值在分配给变量或常量或将其传递给函数时被复制

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

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

考虑这个例子,它使用了前面例子中的Resolution结构:

let hd = Resolution(width: 1920, height: 1080)

var cinema = hd

这个例子声明了一个叫做hd的常量,并将其设置为一个分辨率实例,该实例使用全高清视频的宽度和高度(1920像素宽×1080像素高)初始化。

然后它声明一个名为cinema的变量并将其设置为hd的当前值。 由于“Resolution”是一个结构,现有实例的副本已制作完成,并且此新副本将分配给cinema。 尽管高清和电影现在具有相同的宽度和高度,但它们是幕后的两个完全不同的实例。

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

cinema.width = 2048

检查电影的宽度属性表明它确实已经改变为2048:

print("cinema is now \(cinema.width) pixels wide")

// Prints "cinema is now 2048 pixels wide"

但是,原始HD实例的宽度属性仍旧具有1920的旧值:

print("hd is still \(hd.width) pixels wide")

// Prints "hd is still 1920 pixels wide"

当电影获得hd的当前值时,存储在hd中的值被复制到新的电影实例中。 最终结果是两个完全分离的实例,它们恰好包含相同的数值。 由于它们是独立的实例,因此将电影的宽度设置为2048不会影响存储在hd中的宽度。

相同的行为适用于枚举:

enum CompassPoint {

casenorth, 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"

当rememberedDirection被分配了currentDirection的值时,它实际上被设置为该值的一个副本。 此后更改currentDirection的值不会影响存储在rememberedDirection中的原始值的副本。

类是引用类型

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

这里有一个例子,使用上面定义的VideoMode类:

let tenEighty = VideoMode()

tenEighty.resolution = hd

tenEighty.interlaced = true

tenEighty.name = "1080i"

tenEighty.frameRate = 25.0

此示例声明了一个名为tenEighty的新常量,并将其设置为引用VideoMode类的新实例。 视频模式被分配了之前1920 x 1080的高清分辨率的副本。 它被设置为隔行扫描,并被命名为“1080i”。 最后,它被设置为每秒25.0帧的帧速率。

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

let alsoTenEighty = tenEighty

alsoTenEighty.frameRate = 30.0

因为类是引用类型,所以tenEighty和alsoTenEighty实际上都指向相同的VideoMode实例。 实际上,它们只是同一个实例的两个不同名称。

检查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"

请注意,tenEighty和alsoTenEighty被声明为常量,而不是变量。但是,您仍然可以更改tenEighty.frameRate和alsoTenEighty.frameRate,因为tenEighty和alsoTenEighty常量的值本身并未发生实际变化。 tenEighty和另外TenEighty本身不会“存储”VideoMode实例 - 相反,它们都指向幕后的VideoMode实例。它是基础VideoMode的frameRate属性发生更改,而不是对该VideoMode的常量引用的值。

Identity Operators 身份运算符

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

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

Identical to (===)

Not identical to (!==)

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

if tenEighty === alsoTenEighty {

print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")

}

// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

请注意,“与......相同”(由三个等号或===表示)并不等同于“等于”(由两个等号或==表示):

“与......相同”表示类型的两个常量或变量指向完全相同的类实例。

“等于”意味着两个实例在值中被认为是“相等的”或“等价的”,对于类型的设计者定义的“相等”的某些适当的含义。

当您定义自己的自定义类和结构时,您有责任决定两个“平等”实例的合格性。在等价运算符中描述定义您自己的“等于”和“不等于”运算符的实现的过程。

指针

如果您有使用C,C ++或Objective-C的经验,您可能会知道这些语言使用指针来引用内存中的地址。引用某个引用类型的实例的Swift常量或变量类似于C中的指针,但不是指向内存中地址的直接指针,也不需要您编写星号(*)来表示您正在创造一个参考。相反,这些引用是像Swift中的其他常量或变量一样定义的。

选择类和结构

您可以使用类和结构来定义自定义数据类型,以用作程序代码的构建块。

但是,结构实例总是按值传递,而类实例总是按引用传递。这意味着它们适合于不同类型的任务。在考虑项目所需的数据结构和功能时,请确定每个数据结构是应该定义为类还是结构。

作为一般指导原则,考虑在适用以下一个或多个条件时创建结构:

该结构的主要目的是封装一些相对简单的数据值。

当您分配或传递该结构的实例时,期望封装值将被复制而不是引用是合理的。

结构存储的任何属性都是它们自己的值类型,也可能被复制而不是引用。

该结构不需要继承其他现有类型的属性或行为。

良好的结构候选人的例子包括:

几何形状的大小,可能包含一个宽度属性和一个高度属性,这两个类型都是Double类型。

一种引用一系列范围内的范围的方法,可能封装一个类型为Int的start属性和一个length属性。

3D坐标系中的一个点,可能会封装x,y和z属性,每种属性都是Double类型。

在所有其他情况下,定义一个类,并创建该类的实例,以便通过引用进行管理和传递。实际上,这意味着大多数自定义数据结构应该是类而不是结构。

字符串,数组和字典的赋值和复制行为

在Swift中,许多基本数据类型(如String,Array和Dictionary)都以结构体的形式实现。这意味着如果将字符串,数组和字典等数据分配给新的常量或变量,或者将它们传递给函数或方法时,它们将被复制。

此行为与Foundation不同:NSString,NSArray和NSDictionary作为类实现,而不是结构。Foundation中的字符串,数组和字典始终作为对现有实例的引用进行分配和传递,而不是作为副本。

注意

上面的描述涉及字符串,数组和字典的“复制”。您在代码中看到的行为将始终像发生副本一样。但是,当绝对必要时,Swift仅在幕后执行实际的副本。 Swift管理所有值复制以确保最佳性能,并且您不应该避免分配尝试抢占此优化。

相关推荐

自学Python,写一个挨打的游戏代码来初识While循环

自学Python的第11天。旋转~跳跃~,我~闭着眼!学完循环,沐浴着while的光芒,闲来无事和同事一起扯皮,我说:“编程语言好神奇,一个小小的循环,竟然在生活中也可以找到原理和例子”,同事也...

常用的 Python 工具与资源,你知道几个?

最近几年你会发现,越来越多的人开始学习Python,工欲善其事必先利其器,今天纬软小编就跟大家分享一些常用的Python工具与资源,记得收藏哦!不然下次就找不到我了。1、PycharmPychar...

一张思维导图概括Python的基本语法, 一周的学习成果都在里面了

一周总结不知不觉已经自学Python一周的时间了,这一周,从认识Python到安装Python,再到基本语法和基本数据类型,对于小白的我来说无比艰辛的,充满坎坷。最主要的是每天学习时间有限。只...

三日速成python?打工人,小心钱包,别当韭菜

随着人工智能的热度越来越高,许多非计算机专业的同学们也都纷纷投入到学习编程的道路上来。而Python,作为一种相对比较容易上手的语言,也越来越受欢迎。网络上各类网课层出不穷,各式广告令人眼花缭乱。某些...

Python自动化软件测试怎么学?路线和方法都在这里了

Python自动化测试是指使用Python编程语言和相关工具,对软件系统进行自动化测试的过程。学习Python自动化测试需要掌握以下技术:Python编程语言:学习Python自动化测试需要先掌握Py...

Python从放弃到入门:公众号历史文章爬取为例谈快速学习技能

这篇文章不谈江流所专研的营销与运营,而聊一聊技能学习之路,聊一聊Python这门最简单的编程语言该如何学习,我完成的第一个Python项目,将任意公众号的所有历史文章导出成PDF电子书。或许我这个Py...

【黑客必会】python学习计划

阅读Python文档从Python官方网站上下载并阅读Python最新版本的文档(中文版),这是学习Python的最好方式。对于每个新概念和想法,请尝试运行一些代码片段,并检查生成的输出。这将帮助您更...

公布了!2025CDA考试安排

CDA数据分析师报考流程数据分析师是指在不同行业中专门从事行业数据搜集、整理、分析依据数据作出行业研究评估的专业人员CDA证书分为1-3级,中英文双证就业面广,含金量高!!?报考条件:满18...

一文搞懂全排列、组合、子集问题(经典回溯递归)

原创公众号:【bigsai】头条号:程序员bigsai前言Hello,大家好,我是bigsai,longtimenosee!在刷题和面试过程中,我们经常遇到一些排列组合类的问题,而全排列、组合...

「西法带你学算法」一次搞定前缀和

我花了几天时间,从力扣中精选了五道相同思想的题目,来帮助大家解套,如果觉得文章对你有用,记得点赞分享,让我看到你的认可,有动力继续做下去。467.环绕字符串中唯一的子字符串[1](中等)795.区...

平均数的5种方法,你用过几种方法?

平均数,看似很简单的东西,其实里面包含着很多学问。今天,分享5种经常会用到的平均数方法。1.算术平均法用到最多的莫过于算术平均法,考试平均分、平均工资等等,都是用到这个。=AVERAGE(B2:B11...

【干货收藏】如何最简单、通俗地理解决策树分类算法?

决策树(Decisiontree)是基于已知各种情况(特征取值)的基础上,通过构建树型决策结构来进行分析的一种方式,是常用的有监督的分类算法。决策树算法是机器学习中的一种经典算法,它通过一系列的规则...

面试必备:回溯算法详解

我们刷leetcode的时候,经常会遇到回溯算法类型题目。回溯算法是五大基本算法之一,一般大厂也喜欢问。今天跟大家一起来学习回溯算法的套路,文章如果有不正确的地方,欢迎大家指出哈,感谢感谢~什么是回溯...

「机器学习」决策树——ID3、C4.5、CART(非常详细)

决策树是一个非常常见并且优秀的机器学习算法,它易于理解、可解释性强,其可作为分类算法,也可用于回归模型。本文将分三篇介绍决策树,第一篇介绍基本树(包括ID3、C4.5、CART),第二篇介绍Ran...

大话AI算法: 决策树

所谓的决策树算法,通俗的说就是建立一个树形的结构,通过这个结构去一层一层的筛选判断问题是否好坏的算法。比如判断一个西瓜是否好瓜,有20条西瓜的样本提供给你,让你根据这20条(通过机器学习)建立起...