当前位置: 首页 > news >正文

Swift 类和结构体

类和结构体

  • 一、结构体和类对比
    • 1、类型定义的语法
    • 2、结构体和类的实例
    • 3、属性访问
    • 4、结构体类型的成员逐一构造器
  • 二、结构体和枚举是值类型
  • 三、类是引用类型
    • 1、恒等运算符
    • 2、指针

结构体和类作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。

与其他编程语言所不同的是,Swift 并不要求你为自定义的结构体和类的接口与实现代码分别创建文件。你只需在单一的文件中定义一个结构体或者类,系统将会自动生成面向其它代码的外部接口。

注意

通常一个类的实例被称为对象。然而相比其他语言,Swift 中结构体和类的功能更加相近,本章中所讨论的大部分功能都可以用在结构体或者类上。因此,这里会使用实例这个更通用的术语。

一、结构体和类对比

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

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作用于通过下标语法访问它们的值
  • 定义构造器用于设置初始值
  • 通过扩展以增加默认实现之外的功能
  • 遵循协议以提供某种标准功能

与结构体相比,类还有如下的附加功能:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 析构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用

类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为他们更容易理解,仅在适当或必要时才使用类。实际上,这意味着你的大多数自定义数据类型都会是结构体和枚举。

1、类型定义的语法

结构体和类有着相似的定义方式。你通过 struct 关键字引入结构体,通过 class 关键字引入类,并将它们的具体定义放在一对大括号中:

struct SomeStructure {// 在这里定义结构体
}
class SomeClass {// 在这里定义类
}

注意
每当你定义一个新的结构体或者类时,你都是定义了一个新的 Swift 类型。请使用 UpperCamelCase这种方式来命名类型(如这里的 SomeClassSomeStructure ),以便符合标准 Swift 类型的大写命名风格(如 StringIntBool)。请使用 lowerCamelCase 这种方式来命名属性和方法(如 frameRateincrementCount),以便和类型名区分。

以下是定义结构体和定义类的示例:

struct Resolution {var width = 0var height = 0
}
class VideoMode {var resolution = Resolution()var interlaced = falsevar frameRate = 0.0var name: String?
}

在上面的示例中定义了一个名为 Resolution 的结构体,用来描述基于像素的分辨率。这个结构体包含了名为 widthheight 的两个存储属性。存储属性是与结构体或者类绑定的,并存储在其中的常量或变量。当这两个属性被初始化为整数 0 的时候,它们会被推断为 Int 类型。

在上面的示例还定义了一个名为 VideoMode 的类,用来描述视频显示器的某个特定视频模式。这个类包含了四个可变的存储属性。第一个, resolution,被初始化为一个新的 Resolution 结构体的实例,属性类型被推断为 Resolution。新 VideoMode 实例同时还会初始化其它三个属性,它们分别是初始值为 falseinterlaced(意为“非隔行视频”),初始值为 0.0frameRate,以及值为可选 Stringname。因为 name 是一个可选类型,它会被自动赋予一个默认值 nil,意为“没有 name 值”。

2、结构体和类的实例

Resolution 结构体和 VideoMode 类的定义仅描述了什么是 ResolutionVideoMode。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为此,你需要创建结构体或者类的一个实例。

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

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

结构体和类都使用构造器语法来创建新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如 Resolution()VideoMode()。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。构造过程 章节会对类和结构体的初始化进行更详细的讨论。

3、属性访问

你可以通过使用点语法访问实例的属性。其语法规则是,实例名后面紧跟属性名,两者以点号(.)分隔,不带空格:

print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"

在上面的例子中,someResolution.width 引用 someResolutionwidth 属性,返回 width 的初始值 0

你也可以访问子属性,如 VideoModeresolution 属性的 width 属性:

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"

你也可以使用点语法为可变属性赋值:

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is now 1280"

4、结构体类型的成员逐一构造器

所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:

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

注意:
与结构体不同,类实例没有默认的成员逐一构造器。

二、结构体和枚举是值类型

值类型是这样一种类型,当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。

在之前的章节中,你已经大量使用了值类型。实际上,Swift 中所有的基本类型:整数(integer)、浮点数(floating-point number)、布尔值(boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,其底层也是使用结构体实现的。

Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型的属性,在代码中传递的时候都会被复制。

注意

标准库定义的集合,例如数组,字典和字符串,都对复制进行了优化以降低性能成本。新集合不会立即复制,而是跟原集合共享同一份内存,共享同样的元素。在集合的某个副本要被修改前,才会复制它的元素。而你在代码中看起来就像是立即发生了复制。

请看下面这个示例,其使用了上一个示例中的 Resolution 结构体:

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

在以上示例中,声明了一个名为 hd 的常量,其值为一个初始化为全高清视频分辨率(1920 像素宽,1080 像素高)的 Resolution 实例。

然后示例中又声明了一个名为 cinema 的变量,并将 hd 赋值给它。因为 Resolution 是一个结构体,所以会先创建一个现有实例的副本,然后将副本赋值给 cinema 。尽管 hdcinema 有着相同的宽(width)和高(height),但是在幕后它们是两个完全不同的实例。

下面,为了符合数码影院放映的需求(2048 像素宽,1080 像素高),cinemawidth 属性被修改为稍微宽一点的 2K 标准:

cinema.width = 2048

查看 cinemawidth 属性,它的值确实改为了 2048

print("cinema is now  \(cinema.width) pixels wide")
// 打印 "cinema is now 2048 pixels wide"

然而,初始的 hd 实例中 width 属性还是 1920

print("hd is still \(hd.width) pixels wide")
// 打印 "hd is still 1920 pixels wide"

hd 赋值给 cinema 时,hd 中所存储的值会拷贝到新的 cinema 实例中。结果就是两个完全独立的实例包含了相同的数值。由于两者相互独立,因此将 cinemawidth 修改为 2048 并不会影响 hd 中的 width 的值,如下图所示:
在这里插入图片描述
枚举也遵循相同的行为准则:

enum CompassPoint {case north, south, east, westmutating func turnNorth() {self = .north}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// 打印 "The current direction is north"
// 打印 "The remembered direction is west"

rememberedDirection 被赋予了 currentDirection 的值,实际上它被赋予的是值的一个拷贝。赋值过程结束后再修改 currentDirection 的值并不影响 rememberedDirection 所储存的原始值的拷贝。

三、类是引用类型

与值类型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引用,而不是其拷贝。

请看下面这个示例,其使用了之前定义的 VideoMode 类:

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

以上示例中,声明了一个名为 tenEighty 的常量,并让其引用一个 VideoMode 类的新实例。它的视频模式(video mode)被赋值为之前创建的 HD 分辨率(1920*1080)的一个拷贝。然后将它设置为隔行视频,名字设为 “1080i”,并将帧率设置为 25.0 帧每秒。

接下来,将 tenEighty 赋值给一个名为 alsoTenEighty 的新常量,并修改 alsoTenEighty 的帧率:

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因为类是引用类型,所以 tenEightalsoTenEight 实际上引用的是同一个 VideoMode 实例。换句话说,它们是同一个实例的两种叫法,如下图所示:
在这里插入图片描述
通过查看 tenEightyframeRate 属性,可以看到它正确地显示了底层的 VideoMode 实例的新帧率 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"

这个例子也显示了为何引用类型更加难以理解。如果 tenEightyalsoTenEighty 在你代码中的位置相距很远,那么就很难找到所有修改视频模式的地方。无论在哪使用 tenEighty,你都要考虑使用 alsoTenEighty 的代码,反之亦然。相反,值类型就更容易理解了,因为你的源码中与同一个值交互的代码都很近。

需要注意的是 tenEightyalsoTenEighty 被声明为常量而不是变量。然而你依然可以改变 tenEighty.frameRatealsoTenEighty.frameRate,这是因为 tenEightyalsoTenEighty 这两个常量的值并未改变。它们并不“存储”这个 VideoMode 实例,而仅仅是对 VideoMode 实例的引用。所以,改变的是底层 VideoMode 实例的 frameRate 属性,而不是指向 VideoMode 的常量引用的值。

1、恒等运算符

因为类是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。)

判定两个常量或者变量是否引用同一个类实例有时很有用。为了达到这个目的,Swift 提供了两个恒等运算符:

  • 相同(===
  • 不相同(!==

使用这两个运算符检测两个常量或者变量是否引用了同一个实例:

if tenEighty === alsoTenEighty {print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// 打印 "tenEighty and alsoTenEighty refer to the same VideoMode instance."

请注意,“相同”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同。“相同”表示两个类类型(class type)的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”,判定时要遵照设计者定义的评判标准。

当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准

2、指针

如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(*)来表明你在创建一个引用。相反,Swift 中引用的定义方式与其它的常量或变量的一样。

相关文章:

Swift 类和结构体

类和结构体 一、结构体和类对比1、类型定义的语法2、结构体和类的实例3、属性访问4、结构体类型的成员逐一构造器 二、结构体和枚举是值类型三、类是引用类型1、恒等运算符2、指针 结构体和类作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常…...

网络安全相关面试题(hw)

网络安全面试题 报错注入有哪些函数 updatexml注入 载荷注入 insert注入 updata注入 delete注入 extractvalue()注入 注入防御方法 涵数过滤 直接下载相关防范注入文件,通过incloud包含放在网站配置文件里面 PDO预处理,从PHP 5.1开始&…...

前端开发攻略---三种方法解决Vue3图片动态引入问题

目录 1、将图片放入public文件夹中 2、使用 /src/.... 路径开头 3、生成图片的完整URL地址&#xff08;推荐&#xff09; 1、将图片放入public文件夹中 使用图片&#xff1a;路径为 /public 开头 <template><div><img :src"/public/${flag ? 01 : 02}.jp…...

零售EDI:Target DVS EDI项目案例

Target塔吉特是美国一家巨型折扣零售百货集团&#xff0c;与全球供应商建立长远深入的合作关系&#xff0c;目前国内越来越多的零售产品供应商计划入驻Target。完成入驻资格审查之后&#xff0c;Target会向供应商提出EDI对接邀请&#xff0c;企业需要根据指示完成供应商EDI信息…...

AWS安全性身份和合规性之AWS Firewall Manager

AWS Firewall Manager是一项安全管理服务&#xff0c;可让您在AWS Organizations中跨账户和应用程序集中配置和管理防火墙规则。在创建新应用程序时&#xff0c;您可以借助Firewall Manager实施一套通用的安全规则&#xff0c;更轻松地让新应用程序和资源从一开始就达到合规要求…...

R实验 随机变量及其分布

实验目的&#xff1a; 掌握常见几种离散性随机变量及其分布在R语言中对应的函数用法&#xff1b;掌握常见几种连续性随机变量及其分布在R语言中对应的函数用法&#xff1b;掌握统计量的定义及统计三大抽样分布在R语言中对应的函数用法。 实验内容&#xff1a; &#xff08;习题…...

rapidssl泛域名https600元一年

泛域名https证书也可以称之为通配符https证书&#xff0c;指的是可以用一张https证书为多个网站(主域名以及主域名下的所有子域名网站)传输数据加密&#xff0c;并且提供身份认证服务的数字证书产品。RapidSSL旗下的泛域名https证书性价比高&#xff0c;申请速度快&#xff0c;…...

月薪5万是怎样谈的?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测技术社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;目前是晶圆厂的PE&#xff0c;但是想跳槽谈了几次薪水&#xff0c;都没法有大幅度的增长&#xff0c;该怎么办&#xff1f;“学得文武…...

linux下宝塔负载100%解决方法

今天发现服务器宝塔面板负载居然是100% 但是cpu 和内存其实并不高 通过命令查看主机 uptime 中load average 居然高达18.23 看来负载是真的高了 通过vmstat 看看具体问题 procs&#xff1a; ​ r 表示运行和等待CPU时间片的进程数&#xff0c;这个值如果长期大于系统CPU个数…...

【C++】STL快速入门基础

文章目录 STL&#xff08;Standard Template Library&#xff09;1、一般介绍2、STL的六大组件2.1、STL容器2.2、STL迭代器2.3、相关容器的函数vectorpairstringqueuepriority_queuestackdequeset, map, multiset, multimapunordered_set, unordered_map, unordered_multiset, …...

面向对象编程的魅力与实战:以坦克飞机大战为例

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、面向对象编程的引言 二、理解面向对象编程与面向过程编程的差异 三、创建类与对象&…...

二叉树——堆的实现

一.前言 前面我们讲解了二叉树的概念以及二叉树的存储结构&#xff1a;https://blog.csdn.net/yiqingaa/article/details/139224974?spm1001.2014.3001.5502 今天我们主要讲讲二叉树的存储结构&#xff0c;以及堆的实现。 二.正文 1.二叉树的顺序结构及实现 1.1二叉树的顺序…...

【Spring】DynamicDataSourceHolder 动态数据源切换

【Spring】DynamicDataSourceHolder 动态数据源切换 常见场景常见工具一、AbstractRoutingDataSource1.1、 定义 DynamicDataSourceHolder1.2、 配置动态数据源1.3、 在Spring配置中定义数据源1.4、在业务代码中切换数据源 二、Dynamic Datasource for Spring Boot2.1. 添加依赖…...

LeeCode 3165 线段树

题意 传送门 LeeCode 3165 不包含相邻元素的子序列的最大和 题解 考虑不含相邻子序列的最大和&#xff0c;在不带修改的情况下容易想到&#xff0c;以最后一个元素是否被选取为状态进行DP。从线性递推的角度难以处理待修改的情况。 从分治的角度考虑&#xff0c;使用线段树…...

修改元组元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 场景模拟&#xff1a;伊米咖啡馆&#xff0c;由于麝香猫咖啡需求量较大&#xff0c;库存不足&#xff0c;店长想把它换成拿铁咖啡。 实例08 将麝香猫…...

【模版方法设计模式】

文章目录 模板方法设计模式模板方法的设计原则模板方法设计模式组成部分代码实现抽象类实现具体实现类执行 模板方法设计模式 模版方法设计模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;而将一…...

rust语言初识

程序设计实践课上水一篇ing 来源&#xff1a;rust基础入门-1.初识rust-酷程网 (kucoding.com) rust作为一名新兴语言&#xff0c;与go又有些许不同&#xff0c;因为它的目标是对标系统级开发&#xff0c;也就是C、C这两位在编程界的位置。比如我们最常用的windows系统&#x…...

知识图谱数据预处理笔记

知识图谱数据预处理笔记 0. 引言1. 笔记1-1. \的转义1-2. 特殊符号的清理1-3. 检查结尾是否正常1-4. 检查<>是否存在1-5. 两端空格的清理1-6. 检查object内容长时是否以<开始 0. 引言 最近学习知识图谱&#xff0c;发现数据有很多问题&#xff0c;这篇笔记记录遇到的…...

Unity面试八股文之基础篇

文章目录 前言1. Unity的生命周期加载第一个场景Editor在第一次帧更新之前帧之间更新顺序协程销毁对象时退出时 2. Unity 协程和线程,进程的区别3. 本地坐标系 世界坐标系4. 碰撞器和触发器的区别后话 前言 开设这个栏目的博文会写一些有关unity的面试题目&#xff0c;在面试的…...

HTTPS能否避免流量劫持?如何实现HTTPS

在当今数字化时代&#xff0c;网站安全已经成为企业和个人的头等大事。随着网络犯罪和数据泄露的增加&#xff0c;保护您的网站免受潜在威胁比以往任何时候都更加重要。网站安全的一个关键组成部分是HTTPS&#xff0c;它代表着安全的超文本传输协议。HTTPS是标准HTTP协议的安全…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...