Swift 类和结构体

类和结构体

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

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

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

注意

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

一、结构体和类对比

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

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

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

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

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

1、类型定义的语法

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

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

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

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

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

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

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

2、结构体和类的实例

Resolution 结构体和 VideoMode 类的定义仅描述了什么是 Resolution 和 VideoMode。它们并没有描述一个特定的分辨率(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 引用 someResolution 的 width 属性,返回 width 的初始值 0。

你也可以访问子属性,如 VideoMode 中 resolution 属性的 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 。尽管 hd 和 cinema 有着相同的宽(width)和高(height),但是在幕后它们是两个完全不同的实例。

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

cinema.width = 2048

查看 cinema 的 width 属性,它的值确实改为了 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 实例中。结果就是两个完全独立的实例包含了相同的数值。由于两者相互独立,因此将 cinema 的 width 修改为 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

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

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

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

需要注意的是 tenEighty 和 alsoTenEighty 被声明为常量而不是变量。然而你依然可以改变 tenEighty.frameRate 和 alsoTenEighty.frameRate,这是因为 tenEighty 和 alsoTenEighty 这两个常量的值并未改变。它们并不“存储”这个 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 中引用的定义方式与其它的常量或变量的一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/bicheng/15353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python mp3转mp4工具

成品UI 安装moviepy库 pip install moviepy 转换demo from moviepy.editor import *# 创建一个颜色剪辑,时长与音频相同 audioclip AudioFileClip(r"C:\Users\Administrator\PycharmProjects\pythonProject44\test4\赵照 - 灯塔守望人.mp3") videoclip…

node-nass安装踩坑

编译DSS的前端,用1.1.4编译,没有问题,用1.1.1版本就有问题,一直是node-gyp有问题,怎么也解决了不了。 后来检查发现,是因为要安装node-nass才导致出现node-gyp的问题。 而1.1.4没问题,是因为我…

头歌c语言实验答案

由于头歌C语言实验的具体内容和题目可能随时间变化,我无法直接提供特定实验的完整答案。但我可以基于参考文章中的内容和结构,给出一个通用的回答格式,并结合相关信息进行说明。 通用回答格式 实验名称和描述 实验名称:头歌C语言…

用Python Pygame做的一些好玩的小游戏

有些游戏的代码比较长就不公布了 1.简简单单 1.疯狂的鸡哥 你要准备的图片: 命名为:ji.png 代码: import pygame import random as r pygame.init() pygame.display.set_caption(aaa) pm pygame.display.set_mode((800,600))class Ls(py…

Java进阶学习笔记15——接口概述

认识接口: Java提供了一个关键字Interface,用这个关键字我们可以定义一个特殊的结构:接口。 接口不能创建对象。 注意:接口不能创建对象,接口是用来被类实现(implements)的,实现接口…

中国电子学会(CEIT)2023年05月真题C语言软件编程等级考试三级(含详细解析答案)

中国电子学会(CEIT)考评中心历届真题(含解析答案) C语言软件编程等级考试三级 2023年05月 编程题五道 总分:100分一、找和为K的两个元素(20分) 在一个长度为n (n < 1000)的整数序列中,判断是否存在某两个元素之和为k。 时间限制: 1000 内存限制: 65536 输入 …

基于Spring Boot的高校图书馆管理系统

项目和论文都有企鹅号2583550535 基于Spring Boot的图书馆管理系统||图书管理系统_哔哩哔哩_bilibili 第1章 绪论... 1 1.1 研究背景和意义... 1 1.2 国内外研究现状... 1 第2章 相关技术概述... 2 2.1 后端开发技术... 2 2.1.1 SpringBoot 2 2.1.2 MySQL.. 2 2.1.3 My…

unity中如何插入网页

在Unity中插入自己的网页通常是通过使用Unity的WebGL构建目标和HTML页面来实现的。以下是一些步骤&#xff1a; 构建你的Unity项目为WebGL&#xff1a;在Unity中&#xff0c;选择Build Settings&#xff08;构建设置&#xff09;&#xff0c;将Platform&#xff08;平台&#x…

vr商品全景展示场景编辑软件的优点

3D模型展示网站搭建编辑器以强大的3D编辑引擎和逼真的渲染效果&#xff0c;让您轻松实现模型展示的优化。让用户通过简单的操作&#xff0c;就能满足个人/设计师/商户多样化展示的需求&#xff0c;让您的模型成为独一无二的杰作。 3D模型展示网站搭建编辑器采用国内领先的实时互…

java继承使用细节二

构造器 主类是无参构造器时会默认调用 public graduate() {// TODO Auto-generated constructor stub也就是说我这里要用构造器会直接调用父类。它是默认看不到的 &#xff0c;System.out.println("graduate");} 但当主类是有参构造器如 public father_(int s,doubl…

c语言:将小写字母转换为大写字母

//将小写字母转换为大写字母 #include <stdio.h> #include <ctype.h> int main() { char arr[]"you are low"; int i0; while(arr[i]) { if(islower(arr[i])) { arr[i]arr[i]-32; } i; } printf("%s\n",arr); return 0; }

微调Llama3实现在线搜索引擎和RAG检索增强生成功能

视频中所出现的代码 Tavily SearchRAG 微调Llama3实现在线搜索引擎和RAG检索增强生成功能&#xff01;打造自己的perplexity和GPTs&#xff01;用PDF实现本地知识库_哔哩哔哩_bilibili 一.准备工作 1.安装环境 conda create --name unsloth_env python3.10 conda activate …

周末总结(2024/05/25)

工作 人际关系核心实践&#xff1a; 要学会随时回应别人的善意。执行时间控制在5分钟以内 坚持每天早会打招呼 工作上的要点 现状&#xff08;接受破烂现状&#xff0c;改变状态&#xff09; - 这周使用和执行了生产环境发布流程(2天&#xff09;&#xff0c;2天时间在写Java…

大胆预测:计算机将要回暖

中概财报集体亮眼 虽然最近几天恒指&#xff08;港股&#xff09;稍有回落&#xff0c;但年线仍有 9% 的上涨。 过去三年&#xff0c;恒指分别下跌 14.08%、15.46% 和 13.82%。 而在近期&#xff0c;国内各大互联网都公布了财报&#xff0c;别看各个大厂的作妖不断&#xff0c;…

[前端|vue] v-if 和v-show的区别,为什么功能会类似

v-if 和 v-show 都是 Vue 中用于条件渲染的指令&#xff0c;但它们之间存在几个关键区别&#xff0c;这些区别导致了它们在不同场景下的适用性也有所不同&#xff1a; v-if 的特点&#xff1a; 条件渲染&#xff1a;v-if 是一个动态的条件渲染指令&#xff0c;它会根据表达式的…

dubbo复习:(8)使用sentinel对服务进行降级

一、下载sentinel-dashboard控制台应用并在8080端口启动 二、项目添加springboot 和dubbo相关依赖&#xff08;降级规则并未持久化&#xff0c;如果需要持久化&#xff0c;如果需要持久化降级规则&#xff0c;只需增加nacos相关依赖并在nacos中进行配置&#xff0c;然后配置app…

会话机制:Session

1、什么是会话&#xff1a; 会话对应的英语单词&#xff1a;session 用户打开浏览器&#xff0c;进行一系列操作&#xff0c;然后最终将浏览器关闭&#xff0c;这个整个过程叫做&#xff1a;一次会话。会话在服务器端也有一个对应的java对象&#xff0c;这个java对象叫做&…

使用Python Tkinter创建GUI应用程序

大家好&#xff0c;当我们谈及使用Python Tkinter创建GUI应用程序时&#xff0c;我们涉及的不仅是技术和代码&#xff0c;更是关于创造力和用户体验的故事。Tkinter作为Python标准库中最常用的GUI工具包&#xff0c;提供了丰富的功能和灵活的接口&#xff0c;让开发者能够轻松地…

每日一题(4)——String连接,替换,比较,查找等

主要是一些字符串的连接&#xff0c; 替换&#xff0c;比较&#xff0c;去首尾空格&#xff0c;查找等操作&#xff1b; class ZiFu{public static void main(String []args){String s1"hello world";String s2new String("hello,world");s2" "…

Vue3判断变量和对象不为null和undefined

Vue3判断变量和对象不为null和undefined 一、判断变量二、判断对象 一、判断变量 在 Vue 3 中&#xff0c;你可以使用 JavaScript 提供的常规方式来检查变量是否不为 null 和不为 undefined。你可以分别使用严格不等运算符 ! 来比较变量是否不为 null 和不为 undefined。以下是…