从今天开始,我将从最简单最基础的内容开始,和大家一起学习Swift和SwiftUI。
我们现在能在一个应用程序中看到的所有内容都是依靠SwiftUI来设计的,但是它的背后则是完全依靠Swift这种计算机语言来实现的。
Swift 本身只是一种语言,并不是用来在屏幕上绘制任何东西。在使用 Swift 构建软件时,你将使用 SwiftUI: Apple 强大的框架,可以创建文本、按钮、图像、用户交互等。顾名思义,SwiftUI 就是为 Swift 而设计的,它的设计目的就是利用 Swift 语言提供的强大功能和安全性,从而快速构建功能强大的应用程序。
Swift 是一门相对年轻的语言,2014 年才推出。这意味着它不像老式语言那样存在很多语言缺陷,而且通常意味着只有一种方法可以解决特定问题。
与此同时,作为一种全新的编程语言,Swift 利用了各种建立在旧语言成功(有时是错误)基础上的新思路。例如,它让你很难不小心写出不安全的代码;它让你很容易写出清晰易懂的代码;它支持所有世界语言,所以你永远不会看到那些困扰其他语言的奇怪字符错误。
所以从今天开始,让我们一起从学习Swift开始!
在开始之前
在开始学习之前,我希望所有这篇教程的读者都能先准备学习Swift语言的工具,这些工具包括硬件工具和软件工具。
硬件工具是:一台Mac电脑或是一部iPad。
软件工具是:Xcode 15(免费从Mac App Store下载)或Playgrounds 4(从iPad的App Store下载)
如果你都已经准备好了,那么我们现在就可以开始了!
如何创建变量和常量
无论何时创建程序,都需要存储一些数据。也许是刚输入的用户名,也许是从网上下载的一些新闻报道,也许是刚执行的复杂计算的结果。
Swift 为我们提供了两种存储数据的方法,这取决于你是否希望数据随时间而改变。当你创建一个新的 playground 时,会自动使用第一种方法,因为它将包含这一行:
(如何创建一个新的playground?以Xcode为例:启动Xcode,然后进入File菜单,选择new,选择Playground,在iOS或者macOS选项卡中(任意一个都可以)选择Blank,然后将其保存在你想要保存的位置即可。)
var greeting = "Hello, playground"
这将创建一个名为 greeting 的新变量,因为它是一个变量,所以其值可以变化,即随着程序的运行而变化。
小贴士:macOS playground 中的另一行是 import Cocoa(如果是iOS的话,这一行是import UIKit),它引入了苹果公司提供的大量代码,让应用程序的构建变得更容易。其中包括许多重要功能,因此请不要删除。
这里需要解释的有四种语法:
- var 关键字的意思是创建一个新的变量。
- 我们将变量称为 “greeting”。你可以给变量起任何你想要的名字,但大多数情况下,你会想让它具有一定的意义。
- 等号为变量赋值。如果你不想在等号两边留空格,也可以不留,但这是最常见的样式。
- 我们将”Hello, playground”赋值给了greeting。请注意,文本是写在双引号内的,这样 Swift 就能看到文本的起点和终点。
如果你使用过其他语言,可能会注意到我们的代码在行尾不需要分号。Swift 确实允许使用分号,但非常罕见,只有当你出于某种原因想在同一行中写两段代码时才会用到它们。
当你创建一个变量后,你可以改变它的赋值:
var name = "Jojo"
name = "Emily"
name = "Daisy"
这将创建一个名为 name 的新变量,并赋予其值 “Jojo”。然后它被修改了两次,第一次是 “Emily”,然后是 “Daisy”。我们没有再使用 var,因为我们是在修改一个现有变量,而不是创建一个新变量。你可以根据需要随意更改变量,每次更改都会丢弃旧值。
如果你不想改变某个值,就需要使用常量来代替。创建常量的方法与创建变量几乎相同,只不过我们使用的是 let 而不是 var,就像这样:
let character = "Hope"
当我们使用let时,就会创建一个常量,这是一个无法被更改的值。Swift 从字面上就不允许我们这样做,如果我们尝试,就会显示一个很大的错误。
如果你不信,你可以尝试把以下代码输入到Xcode中试试看:
let character = "Hope"
character = "Daphne"
character = "Francesca"
同样,第二行和第三行中没有 let 关键字,因为我们并没有创建新的常量,我们只是试图更改已有的常量。然而,正如我们在Xcode的报错信息中看到的,这行不通——你不能改变一个常量,否则它就不是常量了!
我们可以使用print命令来要求Xcode打印出任何变量或常量的值。在实际应用中,不会经常用到这个功能,因为用户看不到打印出来的内容,但作为一种查看数据内部内容的简单方法,它确实很有帮助。
例如,我们可以在每次设置变量时将其值打印出来,试着在你的playground中输入这个值:
var playerName = "John"
print(playerName)
playerName = "Andy"
print(playerName)
提示:您可以通过点击 Xcode playground 左边的蓝色播放图标来运行代码。如果你沿着蓝色条带上下移动,你会看到播放图标也在移动–如果你愿意,这可以让它运行你的代码到某一点,但大多数情况下,你会希望运行到最后一行。
你可能已经注意到,我们将变量命名为 playerName,而不是 playername、player_name 或其他名字。这是一种选择:Swift 并不在乎你如何命名常量和变量,只要你在任何地方都以相同的方式引用它们。因此,我们不能先使用 playerName,然后再使用 playername,因为 Swift 认为这两个名字是不同的。
虽然 Swift 并不关心我们如何命名数据,但我们使用的命名方式是 Swift 开发人员的标准——我们称之为约定。这种命名方式被称为 “驼峰式”(camel case),因为名字中的第二个单词和后面的单词都是以一个小凸起作为大写字母开头的:
let managerName = "Mike Bolton"
let dogName = "Wang Wang"
let dayAfterTomorrow = "Sunday"
提示:如果可以,最好使用常量而不是变量——这不仅能让 Swift 更好地优化代码,还能确保 Swift 不会意外更改常量的值。
提示:如果你已经创建了一个变量,并为其赋值了,然后你想再次为其赋值(新值),请不要再使用var。如果你使用了var为一个已经存在的变量赋新值,那么Swift会将此标记为错误,这意味着它不会让你运行代码,直到你为变量取了一个不同的名字。这种报错可能看起来没必要,但相信我:其实它很有用!Swift希望你能明确:你是想要修改一个现有的变量,还是想要创建一个新变量。
Swift为什么要有常量和变量?
变量是在程序中存储临时数据的好方法,但 Swift 还为我们提供了更好的第二种选择:常量。常量在各方面都与变量相同,但有一个重要区别:常量的值一旦设置,就无法更改。
Swift 非常喜欢常量,事实上,如果你创建了一个变量却从未更改过它的值,那么它会建议你使用常量。这样做的原因是为了避免出现问题:您创建的任何变量都可以随时随地进行更改,因此您会失去一些控制权,您存储的重要用户数据可能会在将来的任何时候被删除或替换。
常量一旦被设置,我们就不能更改它,所以这有点像 与Swift 签订了契约:你在说 “这个值很重要,无论我做什么,都不要让我更改它”。当然,你也可以尝试用变量来订立同样的契约,但键盘上的一个疏忽就可能把事情搞砸,Swift 也无能为力。如果使用常量来代替变量,只需将 var 改为 let,就能让 Swift 确保值永远不会改变,这使得你无需再担心将来不小心改变了它。
如何创建字符串
当您将文本赋值给一个常量或变量时,我们称之为字符串。
Swift 的字符串以双引号开始和结束,但引号内的内容则由您自己决定。你可以使用简短的字母文本,比如下面这样:
let actor = "Tom Hanks"
您可以使用标点符号、表情符号和其他字符,就像这样:
let filename = "Beijing.jpg"
let result = "⭐️ You win! ⭐️"
你甚至可以在字符串中使用其他双引号,只要注意在双引号前加上反斜杠,让 Swift 明白双引号是在字符串内而不是字符串的结尾:
let quote = "Then he tapped a sign saying \"Believe\" and walked away."
如果你漏掉了反斜杠,Swift 一定会指出你的代码不正确。
字符串的长度没有实际限制,这意味着你可以使用字符串存储很长的内容,例如红楼梦全集。不过,你会发现 Swift 不喜欢在字符串中使用换行符。因此,这种代码是不允许的:
let movie = "A day in
the life of an
Apple engineer"
如果我们需要创建多行的字符串,我们需要使用三个引号,就像这样:
let movie = """
A day in
the life of an
Apple engineer
"""
开始和结束处的三引号各占一行,而你的字符串则在中间。
创建字符串后,你会发现 Swift 提供了一些有用的功能来处理字符串的内容。随着将来学习的深入,你会了解到更多关于这些功能的信息,但今天我们只提到三个功能。
首先,您可以通过在变量或常量的名称后写入 .count 来读取字符串的长度:
print(actor.count)
由于演员的文本是 “Tom Hanks”,因此将打印 9 个字符——名字中的每个字母一个,加上中间的空格。
如果不想直接打印字符串的长度,可以将其赋值给另一个常量,就像这样:
let nameLength = actor.count
print(nameLength)
第二个有用的功能是 uppercased(),它可以发回相同的字符串,但每个字母都大写:
print(result.uppercased())
提示:是的,这里需要括号,但字符计数不需要。其中的原因会随着你的学习变得越来越清楚,但在学习 Swift 的早期阶段,只能这样解释这种区别:如果你要求 Swift 读取一些数据,你就不需要括号,但如果你要求 Swift 做一些工作,你就需要括号。这并不完全正确,稍后你会了解到,但现在已经足够让我们解释目前的情况了。
最后一个有用的字符串功能叫做 hasPrefix(),它可以让我们知道一个字符串是否以我们选择的字母开头:
print(movie.hasPrefix("A day"))
还有一个类似的 hasSuffix() 函数,用于检查字符串是否以某些文本结尾:
print(filename.hasSuffix(".jpg"))
提示:在Swift中,字符串是区分大小写的。
在 Swift 中,字符串的功能非常强大,而我们今天只是触及了它的一点皮毛!
整数
在处理 3、5、50 或 500 万等整数时,Swift 将其称为整数,简称 Int。
创建一个新的整数就像创建一个字符串一样:使用 let 或 var(取决于你想要的是常量还是变量),提供一个名称,然后给它一个值。例如,我们可以这样创建一个 score 常量:
let score = 100
整数可以非常大,超过数十亿、数万亿、四万亿,甚至五万亿,但它们也可以非常小,可以是负数。
当你手写数字时,可能很难看清它是多大。例如,这是什么数字?
let aBigNumber = 100000000
如果是手写,我们可能会写成 “100,000,000”,这时数字显然是 1 亿。Swift 也有类似的功能:您可以使用下划线 _ 来分割数字。
因此,我们可以将之前的代码改成这样:
let aBigNumber = 100_000_000
Swift 实际上并不关心下划线,所以如果你愿意,甚至可以这样写:
let aBigNumber = 1_00__00___00____00
最终结果是一样的:aBigNumber 被设置为一个值为 100,000,000 的整数。
当然,你也可以使用在学校学到的算术运算符从其他整数创建整数: + 表示加法,- 表示减法,* 表示乘法,/ 表示除法。
例如:
let lowerScore = score - 2
let higherScore = score + 10
let doubledScore = score * 2
let squaredScore = score * score
let halvedScore = score / 2
print(score)
Swift 不会每次都创建新的常量,而是通过一些特殊的操作,以某种方式调整一个整数,并将结果赋回给原始数字。
例如,这会创建一个等于 10 的计数器变量,然后再增加 5:
var counter = 10
counter = counter + 5
与其写counter = counter + 5,不如使用速记运算符 +=,它可以直接在相关整数上添加一个数字:
counter += 5
print(counter)
它们的作用完全相同,只是键入量更少。我们称这些为复合赋值运算符,它们还有其他形式:
counter *= 2
print(counter)
counter -= 10
print(counter)
counter /= 2
print(counter)
在介绍完整数之前,我还想提最后一件事:和字符串一样,整数也附带了一些有用的功能。例如,你可以在一个整数上调用 isMultiple(of:),找出它是否是另一个整数的倍数。
因此,我们可以这样询问 100 是否是 5 的倍数:
let number = 100
print(number.isMultiple(of: 5))
如果你愿意,也可以直接使用数字:
print(100.isMultiple(of: 5))
小数
在处理诸如 3.1、5.56 或 3.141592654 等十进制数时,Swift 将其称为浮点数。浮点数的名称来源于计算机存储浮点数的复杂方式:计算机会尝试将123,456,789这样的大数与0.0000000001这样的小数存储在相同的空间内,而唯一的办法就是根据数字的大小移动小数点。
这种存储方法使得十进制数给程序员带来了一个个臭名昭著的问题,你只需两行 Swift 代码就能体会到这一点:
let number = 0.1 + 0.2
print(number)
运行时,它不会打印结果0.3。相反,它将打印 0.30000000000000004,变成了0.3后面带有15个0,然后是 4,因为……因为……总之原因非常复杂。
将来我们或许会知道它为什么那么复杂,但先让我们来关注一下更重要的事情。
首先,当你创建一个浮点数时,Swift 会认为它是一个 Double。这是 “双精度浮点数 “的简称,我知道这个名字很奇怪——多年来,我们处理浮点数的方式发生了很大变化,虽然 Swift 很好地简化了这一过程,但有时你可能会遇到一些更复杂的旧代码。在这种情况下,这意味着 Swift 分配的存储空间是一些旧语言的两倍,这意味着 Double 可以存储绝对庞大的数字。
其次,Swift 将小数视为与整数完全不同的数据类型,这意味着您不能将它们混合在一起。毕竟,整数总是 100% 准确的,而小数并非如此,所以除非你明确要求,否则 Swift 不会让您将它们放在一起。
在实践中,这意味着您不能将整数加到小数中,因此这种代码会产生错误:
let a = 1
let b = 2.0
let c = a + b
是的,我们可以看到 b 其实只是伪装成小数的整数 2,但 Swift 仍然不允许运行这段代码。这就是所谓的类型安全: Swift 不会让我们意外混合不同类型的数据。
如果您希望发生这种情况,您需要明确告诉 Swift,它应该将 b 中的 Double 视为 Int:
let c = a + Int(b)
或者将 a 中的 Int 视为 Double:
let c = Double(a) + b
第三,Swift 会根据你提供的数字来决定你要创建的是 Double 还是 Int,如果数字中有小数点,则是 Double,否则是 Int,即使点后面的数字是 0。
那么:
let double1 = 3.1
let double2 = 3131.3131
let double3 = 3.0
let int1 = 3
结合类型安全,这意味着一旦 Swift 确定了常量或变量的数据类型,它就必须始终保持相同的数据类型。这意味着这段代码没有问题:
var name = "Tom Hanks"
name = "Nicolas Cage"
但这种代码就不行:
var name = "Tom Hanks"
name = 57
这就告诉 Swift name 将存储一个字符串,但随后它又试图将一个整数放入其中。
最后,小数具有与整数相同的运算符和复合赋值运算符:
var rating = 5.0
rating *= 2
许多旧版 API 使用略有不同的小数数字存储方式,称为 CGFloat。幸运的是,Swift 让我们可以在任何需要使用 CGFloat 的地方使用普通的 Double 数,因此虽然你会不时看到 CGFloat 出现,但你可以忽略它。
浮点数之所以复杂,是因为计算机试图使用二进制来存储复杂的数字。例如,如果你用 1 除以 3,我们知道得到的是 1/3,但这无法用二进制来存储,因此系统被设计为创建非常接近的近似值。这样做效率极高,而且误差很小,通常无关紧要,但至少你知道为什么 Swift 不会让我们意外地将 Int 和 Double 混合使用!
提示:为什么Swift不允许我们将整数的变量和小数的变量放在一起进行运算呢?答案是Swift在确保安全。因为Swift无法确定你使用的整数和小数放在一起的结果是一定安全的,因为本来它们的存储方式就是不同的。一开始你可能会觉得这样的做法很麻烦,但是请相信:这真的很有帮助。
扩展知识:为什么说Swift是一种类型安全语言?
Swift 允许我们将变量创建为字符串和整数,但也允许创建许多其他类型的数据。创建变量时,Swift 可以根据您为其分配的数据类型来确定变量的类型,从那时起,该变量将始终具有一种特定的类型。
例如,我们创建了一个名为 numberOfOrange 的新变量,其值等于 12:
var numberOfOrange = 12
因为我们将 12 赋值为 numberOfOrange 的初始值,所以 Swift 会将其赋值为整数类型——Int。这是一个变量,这意味着我们可以根据需要随时更改它的值,但不能更改它的类型:它将始终是一个整数。
这在构建应用程序时非常有用,因为这意味着 Swift 将确保我们不会在数据上犯错。例如,我们不能这样写:
numberOfOrange = "Apple"
试图将字符串赋值给一个整型变量,这是不允许的。虽然我们很少犯这种明显的错误,但你会发现,在使用 Swift 编写代码的每一天,这种类型安全都会帮到你。
想想看:我们刚刚创建了一个变量,然后试图改变它的类型,这显然会失败。但是,随着程序规模和复杂性的增加,你不可能随时在脑海中记住变量的类型,在将来很有可能会发生错将其他类型数据赋值给原先定义好的变量上,因此我们实际上是将防止犯错误的工作转移给了 Swift。
第1章的结尾
至此,我们一起学习了Swift简单数据类型的第一部份,它们是“变量和常量”、“字符串”、“整数”和“小数”。第2章将继续介绍简单数据类型,敬请期待!