【编程语言】Swift从入门到精通

目录

一、为什么选择 Swift

1.1 简洁性

1.2 安全性

1.3 效率

1.4 与其他编程语言对比

二、搭建开发环境

2.1 下载 Xcode

2.2 安装 Xcode

2.3 首次启动与基本设置

2.4 创建第一个 Swift 项目

三、语法基础入门

3.1 变量与常量

3.2 数据类型

3.3 运算符

3.4 控制流语句

四、函数与闭包

4.1 函数

4.1.2 函数的定义

4.1.3 函数的参数与返回值

4.2 闭包

4.2.1 闭包的概念

4.2.2 闭包的作用

4.2.3 闭包的语法

五、面向对象编程

5.1 类与结构体

5.1.1 类的定义与使用

5.1.2 结构体的定义与使用

5.1.3 类与结构体的区别

5.2 枚举

5.3 继承与多态

5.3.1 继承的实现

5.3.2 多态的体现

六、内存管理与错误处理

6.1 内存管理

6.1.1 ARC 的工作原理

6.1.2 循环引用问题及解决方法

6.2 错误处理

6.2.1 错误的表示与抛出

6.2.2 错误的处理方式

七、实战项目演练

7.1 项目需求分析

7.2 项目搭建

7.3 界面设计

7.4 代码实现

7.5 连接 UI 元素和代码

7.6 运行项目

八、学习资源推荐

8.1 书籍

8.2 在线课程

8.3 博客与论坛


一、为什么选择 Swift

Swift 是苹果公司在 2014 年推出的编程语言 ,它被设计用来开发 iOS、macOS、watchOS 和 tvOS 应用程序。它的诞生,可谓是编程界的一次革新,旨在解决以往 Objective-C 语言的一些痛点,为开发者带来更高效、更安全、更简洁的编程体验。

1.1 简洁性

Swift 的语法简洁明了,摒弃了许多冗余的符号和语法结构。举个例子,在 Swift 中定义一个函数,语法非常简洁:


func greet(name: String) -> String {

return "Hello, (name)!"

}

let message = greet(name: "Swift")

print(message)

而在传统的 Objective-C 中,实现相同功能的代码则要繁琐许多:


#import <Foundation/Foundation.h>

NSString *greet(NSString *name) {

return [NSString stringWithFormat:@"Hello, %@!", name];

}

int main(int argc, const char * argv[]) {

@autoreleasepool {

NSString *message = greet(@"Objective-C");

NSLog(@"%@", message);

}

return 0;

}

对比之下,Swift 的代码量明显减少,可读性大大提高,开发者可以用更少的代码实现更多的功能,减少了出错的概率,提高了开发效率。

1.2 安全性

Swift 在设计上融入了诸多安全特性,以帮助开发者避免常见的编程错误。比如,它的可选类型(Optionals)机制,强制开发者处理可能为空的值,避免了空指针异常(Null Pointer Exception)这一在其他语言中常见的崩溃原因。例如:


let optionalString: String? = nil

// 如果直接使用optionalString会报错,必须先解包处理

if let unwrappedString = optionalString {

print(unwrappedString)

} else {

print("The string is nil")

}

同时,Swift 还拥有严格的类型检查和内存管理机制,在编译时就能发现许多类型不匹配的错误,并且自动管理内存,减少了内存泄漏和野指针的风险,让程序更加稳定可靠。

1.3 效率

Swift 采用了先进的编译器技术,编译后的代码运行效率极高,接近 C 语言的性能。它支持多线程编程,并且对硬件资源的利用更加高效,在处理复杂的计算任务和图形渲染等场景下,能够充分发挥设备的性能优势,为用户带来流畅的使用体验。无论是开发小型应用还是大型游戏,Swift 都能提供出色的性能表现。

1.4 与其他编程语言对比

与 Python 相比,Swift 是强类型语言,在编译时就能发现类型错误,而 Python 是动态类型语言,类型错误往往在运行时才被发现。在性能方面,Swift 作为编译型语言,执行速度通常比解释型的 Python 更快,更适合开发对性能要求较高的应用,如 iOS 系统应用。

和 Java 相比,Swift 的语法更加简洁现代,Java 的语法相对较为冗长。在应用场景上,Java 广泛应用于企业级开发和安卓应用开发,而 Swift 专注于苹果生态系统的开发,与苹果的框架和库结合得更加紧密,能够充分利用苹果设备的特性。

二、搭建开发环境

在开始学习 Swift 编程之前,我们首先需要搭建一个合适的开发环境。对于 Swift 开发来说,Xcode 是官方推荐且功能最全面的集成开发环境(IDE) ,它集成了代码编辑器、编译器、调试器等一系列开发所需的工具,为开发者提供了一站式的开发体验。下面我们就来详细介绍如何搭建 Swift 开发环境。

2.1 下载 Xcode

Xcode 可以从 Mac App Store 免费下载。确保你的 Mac 运行的是兼容 Xcode 版本的 macOS 系统,一般来说,建议使用最新版本的 macOS 以获取最佳的兼容性和性能。打开 Mac App Store,在搜索栏中输入 “Xcode”,然后点击 “获取” 按钮开始下载。由于 Xcode 安装包体积较大,下载过程可能需要一些时间,具体取决于你的网络速度,请耐心等待。

2.2 安装 Xcode

下载完成后,安装过程相对简单。在 App Store 的下载列表中找到 Xcode,点击 “安装”,系统会提示你输入管理员密码,输入后等待安装完成即可。安装成功后,你可以在 “应用程序” 文件夹中找到 Xcode 图标。

2.3 首次启动与基本设置

初次启动 Xcode 时,可能会弹出一些提示框,要求你同意软件许可协议等内容,按照提示操作即可。接着,你可以进行一些基本设置,以满足个人的开发习惯。例如,在 “Xcode” 菜单中选择 “Preferences”,在 “Fonts & Colors” 选项卡中,你可以选择喜欢的代码字体和颜色主题,像 Monokai、Ciapre 等主题都受到很多开发者的喜爱;在 “Text Editing” 选项卡中,你可以设置是否显示行号、代码折叠方式、缩进宽度等,比如勾选 “Show Line Numbers” 来显示行号,选择 “Spaces” 并设置 “Tab width” 为 4 来使用空格缩进,这样能使代码排版更加整齐美观 。

2.4 创建第一个 Swift 项目

当 Xcode 安装和设置完成后,我们就可以创建一个新的 Swift 项目了。打开 Xcode,在欢迎界面点击 “Create a new Xcode project”,或者在菜单栏中选择 “File” -> “New” -> “Project”。在项目模板选择界面,根据你要开发的应用类型选择相应的平台和模板,比如我们选择 “iOS” 平台下的 “App” 模板,这是开发 iOS 应用的标准模板,然后点击 “Next”。接下来填写项目的基本信息,包括:

Product Name:项目名称,建议使用英文,采用驼峰式命名或全小写加连字符的方式,避免使用特殊字符和空格,例如 “SwiftDemo”。

Team:与 Apple Developer Program 关联的开发者账户,如果没有可以先注册一个免费的 Apple ID 登录,后续再加入开发者计划。

Organization Identifier:组织标识符,采用反向域名格式,如 “com.yourcompany”,用于生成唯一的 Bundle Identifier。

Bundle Identifier:应用的唯一标识符,由组织标识符和产品名称组合而成,如 “com.yourcompany.SwiftDemo”,在 App Store 上架时每个应用都需要有自己独特的 Bundle Identifier。

Testing System:选择测试框架,XCTest for Unit and UI Tests 是苹果提供的标准测试框架,支持单元测试和用户界面测试,适合大多数开发需求;Swift Testing with XCTest UI Tests 是新的测试框架,与 XCTest 结合使用,提供更现代化的 API 和更简洁的语法;None 表示不使用任何测试框架,适用于简单项目 。

Storage:数据存储选项,None 表示不使用任何数据存储;SwiftData 是新的数据持久化框架,基于 Core Data,使用声明式 Swift 代码进行数据建模,更简洁易用,适合快速开发;Core Data 适用于存储复杂数据模型和进行高效查询的应用程序,支持对象图管理和强大的数据持久化能力 。

填写完这些信息后,点击 “Next”,选择项目文件的存储目录,然后点击 “Create”,Xcode 就会为你创建一个带有示例代码的 Swift 项目,此时你已经成功搭建好了 Swift 开发环境,可以开始编写代码啦!

三、语法基础入门

学习一门编程语言,语法是基石,Swift 也不例外。掌握 Swift 的基础语法,是开启高效编程之旅的第一步。下面我们就来深入了解 Swift 中变量、数据类型、运算符、控制流语句等核心基础语法知识,并通过简单易懂的代码示例,帮助你轻松理解和掌握 。

3.1 变量与常量

在 Swift 中,使用var关键字声明变量,变量的值可以在程序运行过程中改变;使用let关键字声明常量,常量一旦被赋值,其值就不能再更改。例如:


var myVariable = 42

myVariable = 50

let myConstant = 10

// myConstant = 20 // 这行代码会报错,因为常量不能重新赋值

3.2 数据类型

Swift 拥有丰富的数据类型,常见的有:

整型:Int表示整数,根据平台不同,在 32 位平台上和Int32长度相同,在 64 位平台上和Int64长度相同;UInt表示无符号整数,同样会根据平台有不同的长度 。例如:


let age: Int = 25

let population: UInt = 1000000

浮点型:Float表示 32 位浮点数,精度相对较低,最少只有 6 位数字;Double表示 64 位浮点数,精度更高,最少有 15 位数字,通常在需要高精度计算时使用。比如:


let piFloat: Float = 3.14159

let piDouble: Double = 3.141592653589793

布尔型:Bool只有两个值,true和false,用于逻辑判断。例如:


let isRaining = false

if isRaining {

print("记得带伞")

} else {

print("可以愉快出门啦")

}

字符串型:String用于存储文本,由字符组成。可以使用双引号创建字符串,并且支持字符串插值,方便将变量插入到字符串中。示例如下:


let name = "Swift"

let greeting = "Hello, (name)!"

print(greeting)

3.3 运算符

Swift 提供了丰富的运算符,用于各种数学和逻辑运算:

算术运算符:包括加法(+)、减法(-)、乘法(*)、除法(/)、取余(%)。例如:


let sum = 5 + 3

let difference = 10 - 2

let product = 4 * 6

let quotient = 15 / 3

let remainder = 17 % 5

比较运算符:用于比较两个值的大小或是否相等,包括等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=),这些运算符返回的结果都是Bool类型。比如:


let a = 10

let b = 5

let isEqual = a == b

let isGreater = a > b

逻辑运算符:主要有逻辑与(&&)、逻辑或(||)、逻辑非(!),用于组合或取反布尔值。例如:


let isSunny = true

let isWarm = false

if isSunny && isWarm {

print("适合出去游玩")

} else if isSunny || isWarm {

print("天气还不错")

} else {

print("待在家里也挺好")

}

3.4 控制流语句

控制流语句用于控制程序的执行顺序,是编程中实现复杂逻辑的关键:

条件语句(if-else:根据条件的真假来决定执行哪部分代码。例如:


let score = 85

if score >= 90 {

print("成绩优秀")

} else if score >= 80 {

print("成绩良好")

} else if score >= 60 {

print("成绩及格")

} else {

print("成绩不及格,需要努力")

}

switch语句:用于多分支判断,能更简洁地处理多种情况。与 C 语言和 Objective-C 不同,Swift 中的switch语句不需要在每个case分支结尾写break语句,执行完一个case分支后会自动跳出switch。例如:


let day = "Tuesday"

switch day {

case "Monday":

print("新一周的开始")

case "Tuesday", "Wednesday", "Thursday":

print("努力工作中")

case "Friday":

print("快到周末啦")

case "Saturday", "Sunday":

print("享受周末时光")

default:

print("未知的一天")

}

循环语句(forwhilerepeat-while

for-in循环:常用于遍历数组、字典、区间等序列。例如遍历数组:


let numbers = [1, 2, 3, 4, 5]

for number in numbers {

print(number)

}

遍历区间:


for i in 1...5 {

print(i)

}

while循环:在条件为真时,重复执行循环体。例如:


var count = 0

while count < 5 {

print(count)

count += 1

}

repeat-while循环:先执行一次循环体,再判断条件是否为真,若为真则继续循环。例如:


var num = 1

repeat {

print(num)

num += 1

} while num <= 3

四、函数与闭包

在 Swift 编程中,函数与闭包是极为重要的概念,它们赋予了代码强大的灵活性和复用性 ,能够帮助我们构建更加高效、结构清晰的程序。下面我们就来深入学习 Swift 中的函数与闭包。

4.1 函数

函数是一段独立的代码块,用于执行特定的任务。通过给函数命名,我们可以在需要的时候调用它,从而实现代码的复用,提高开发效率 。

4.1.2 函数的定义

在 Swift 中,使用func关键字来定义函数,其基本语法结构如下:


func functionName(parameters) -> returnType {

// 函数体

return returnValue

}

functionName:函数的名称,用于标识函数,应遵循命名规范,采用驼峰式命名法,且能准确描述函数的功能,比如calculateArea表示计算面积的函数 。

parameters:参数列表,用于接收外部传入的数据,每个参数都包含参数名和参数类型,多个参数之间用逗号分隔。例如(radius: Double)表示接收一个名为radius,类型为Double的参数 。

-> returnType:返回类型,用于指定函数执行完毕后返回的数据类型,如果函数没有返回值,可以省略这部分,或者写成-> Void,Void实际上是一个空元组() 。

函数体:包含执行具体任务的代码语句,是函数的核心部分,在函数体中可以使用传入的参数进行各种计算和操作 。

return returnValue:返回语句,用于返回函数执行的结果,returnValue是返回的值,其类型必须与返回类型returnType一致,如果函数没有返回值,则不需要return语句 。

4.1.3 函数的参数与返回值

函数的参数和返回值让函数具备了更强的通用性和灵活性,可以适应不同的业务需求。

参数:函数可以有零个或多个参数,参数可以有默认值。当调用函数时,如果省略了具有默认值的参数,函数会使用默认值进行计算。例如:


func greet(name: String = "Guest") -> String {

return "Hello, (name)!"

}

let greeting1 = greet() // 使用默认参数,结果为 "Hello, Guest!"

let greeting2 = greet(name: "Swift") // 传入参数,结果为 "Hello, Swift!"

同时,Swift 还支持可变参数,可变参数可以接受零个或多个同类型的值,在参数类型后面加上…来表示。例如:


func sum(numbers: Int...) -> Int {

var total = 0

for number in numbers {

total += number

}

return total

}

let result1 = sum(numbers: 1, 2, 3) // 结果为6

let result2 = sum(numbers: 4, 5) // 结果为9

返回值:函数可以返回一个值,也可以返回多个值。当需要返回多个值时,可以使用元组类型。例如:


func minMax(array: [Int]) -> (min: Int, max: Int)? {

if array.isEmpty { return nil }

var currentMin = array[0]

var currentMax = array[0]

for value in array[1...] {

if value < currentMin {

currentMin = value

} else if value > currentMax {

currentMax = value

}

}

return (currentMin, currentMax)

}

if let bounds = minMax(array: [8, 1, 6, 4, 10]) {

print("min is (bounds.min), max is (bounds.max)")

}

4.2 闭包

闭包是一种自包含的代码块,可以在代码中作为参数传递、返回,或者作为变量、常量进行存储 ,它类似于匿名函数,在 Swift 编程中有着广泛的应用。

4.2.1 闭包的概念

闭包可以捕获并存储其上下文中的变量和常量,即使这些变量和常量的作用域已经结束,闭包仍然可以在其代码体中访问和修改它们 ,这使得闭包在处理一些需要保持状态的任务时非常方便。例如:


func makeIncrementer(amount: Int) -> () -> Int {

var total = 0

let incrementer: () -> Int = {

total += amount

return total

}

return incrementer

}

let incrementByTwo = makeIncrementer(amount: 2)

print(incrementByTwo()) // 输出2

print(incrementByTwo()) // 输出4

在这个例子中,incrementer闭包捕获了total变量,每次调用incrementByTwo时,total的值都会累加amount,并且这个状态会被保留下来 。

4.2.2 闭包的作用

闭包在 Swift 中主要用于以下几个方面:

作为回调函数:在异步操作(如网络请求、文件读取)中,闭包常被用作回调函数,用于处理操作完成后的结果。例如:


func fetchData(completion: @escaping (String) -> Void) {

// 模拟异步网络请求

DispatchQueue.global().async {

let data = "Response Data"

completion(data)

}

}

fetchData { result in

print(result) // 输出 "Response Data"

}

函数式编程:闭包在集合操作(如map、filter、reduce)中发挥着重要作用,使我们能够以更简洁、高效的方式处理集合数据。例如:


let numbers = [1, 2, 3, 4, 5]

let squaredNumbers = numbers.map { $0 * $0 }

print(squaredNumbers) // 输出 [1, 4, 9, 16, 25]

let evenNumbers = numbers.filter { $0 % 2 == 0 }

print(evenNumbers) // 输出 [2, 4]

let sum = numbers.reduce(0) { $0 + $1 }

print(sum) // 输出15

4.2.3 闭包的语法

闭包表达式的基本语法如下:


{ (parameters) -> returnType in

// 闭包体

return returnValue

}

它与函数的语法结构有相似之处,但闭包通常是匿名的,并且语法更加简洁灵活。在实际使用中,Swift 还提供了许多闭包语法的简化形式,以提高代码的可读性和编写效率 :

推断参数和返回值类型:如果闭包的参数和返回值类型可以从上下文推断出来,那么可以省略这些类型声明。例如:


let sumClosure = { (a, b) in

return a + b

}

单表达式闭包可以省略return:如果闭包体是单一表达式,则可以隐式返回该表达式的值,而不需要return关键字。例如:


let sumClosure = { (a, b) in

a + b

}

参数名称的简写:Swift 提供了$0、$1、$2等参数名称的简写,表示闭包的第一个、第二个、第三个参数。例如:


let sumClosure = { $0 + $1 }

尾随闭包:如果闭包是函数的最后一个参数,可以将它写在括号外,这就是尾随闭包语法。例如:


func performOperation(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {

return operation(a, b)

}

let result = performOperation(a: 3, b: 5) { $0 + $1 }

五、面向对象编程

面向对象编程(OOP)是一种强大的编程范式,它将数据和操作数据的方法封装在一起,形成一个个对象 。Swift 作为一门现代化的编程语言,对面向对象编程提供了全面且强大的支持,具备类、结构体、枚举、继承、多态等丰富的特性,这些特性使得代码的组织和维护更加高效、灵活,下面我们就来详细介绍 Swift 中的面向对象编程 。

5.1 类与结构体

在 Swift 中,类(Class)和结构体(Struct)是两种重要的自定义数据类型,它们都可以包含属性和方法,但在使用和特性上存在一些差异 。

5.1.1 类的定义与使用

类是一种引用类型,使用class关键字定义 。以定义一个表示人的类为例:


class Person {

var name: String

var age: Int

init(name: String, age: Int) {

self.name = name

self.age = age

}

func sayHello() {

print("Hello, my name is (name) and I am (age) years old.")

}

}

在这个例子中,Person类有两个属性name和age,用于描述人的姓名和年龄 。init方法是类的初始化器,用于在创建类的实例时初始化属性 。sayHello方法则用于打印人的基本信息 。使用时,可以这样创建Person类的实例并调用其方法:


let person1 = Person(name: "Alice", age: 30)

person1.sayHello()

5.1.2 结构体的定义与使用

结构体是一种值类型,使用struct关键字定义 。比如定义一个表示二维坐标的结构体:


struct Point {

var x: Int

var y: Int

func distance(to other: Point) -> Double {

let deltaX = Double(x - other.x)

let deltaY = Double(y - other.y)

return (deltaX * deltaX + deltaY * deltaY).squareRoot()

}

}

Point结构体包含x和y两个属性表示坐标值,distance(to:)方法用于计算当前点到另一个点的距离 。使用方式如下:


let point1 = Point(x: 1, y: 1)

let point2 = Point(x: 4, y: 5)

let distance = point1.distance(to: point2)

print(distance)

5.1.3 类与结构体的区别

类和结构体在很多方面存在区别:

内存管理与赋值方式:类是引用类型,当一个类的实例被赋值给另一个变量时,两个变量指向同一块内存地址,修改其中一个变量的值,另一个变量也会受到影响;而结构体是值类型,赋值时会将整个结构体的内容复制一份,两个变量相互独立,修改其中一个不会影响另一个 。例如:


class ClassExample {

var value = 10

}

let classInstance1 = ClassExample()

let classInstance2 = classInstance1

classInstance2.value = 20

print(classInstance1.value)


struct StructExample {

var value = 10

}

let structInstance1 = StructExample()

let structInstance2 = structInstance1

structInstance2.value = 20

print(structInstance1.value)

继承性:类支持继承,一个类可以继承另一个类的属性和方法,并可以重写和扩展它们;而结构体不支持继承 。比如:


class Animal {

func makeSound() {

print("Some sound")

}

}

class Dog: Animal {

override func makeSound() {

print("Woof")

}

}

初始化器:结构体如果没有自定义初始化器,编译器会自动提供一个默认的成员逐一初始化器;而类必须手动定义初始化器来初始化所有存储属性 。

5.2 枚举

枚举(Enumeration)是一组相关值的集合,使用enum关键字定义 。它在 Swift 编程中常用于表示有限的、离散的状态或选项 。例如,定义一个表示方向的枚举:


enum Direction {

case north

case south

case east

case west

}

使用枚举时,可以直接访问其成员,并且可以通过switch语句对枚举值进行匹配:


let myDirection = Direction.north

switch myDirection {

case .north:

print("Go north")

case .south:

print("Go south")

case .east:

print("Go east")

case .west:

print("Go west")

}

枚举还可以关联值,这使得枚举成员能够携带额外的信息 。比如,定义一个表示条形码的枚举,它可以是 UPC-A 格式(关联三个整数)或者 QR 码格式(关联一个字符串):


enum Barcode {

case upca(Int, Int, Int)

case qrcode(String)

}

let productBarcode = Barcode.upca(1, 2, 3)

switch productBarcode {

case let .upca(numberSystem, identifier, check):

print("UPC-A: (numberSystem), (identifier), (check)")

case let .qrcode(code):

print("QR code: (code)")

}

5.3 继承与多态

继承是面向对象编程的重要特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法,并且可以在子类中进行扩展和重写 。多态则是指不同类的对象对同一消息作出不同的响应,通过继承和方法重写来实现 。

5.3.1 继承的实现

在 Swift 中,使用冒号(:)来表示继承关系 。例如,定义一个继承自Vehicle类的Car类:


class Vehicle {

var speed = 0.0

func move() {

print("Moving at (speed) speed")

}

}

class Car: Vehicle {

var brand: String

init(brand: String) {

self.brand = brand

super.init()

}

override func move() {

print("(brand) car is moving at (speed) speed")

}

}

在这个例子中,Car类继承自Vehicle类,拥有Vehicle类的speed属性和move方法 。Car类新增了brand属性,并通过init方法进行初始化 。同时,Car类重写了move方法,以提供更具体的实现 。

5.3.2 多态的体现

多态使得我们可以使用父类的引用指向子类的对象,并且在调用方法时,会根据对象的实际类型来决定执行哪个类的方法 。例如:


let vehicle1: Vehicle = Vehicle()

let vehicle2: Vehicle = Car(brand: "BMW")

vehicle1.move()

vehicle2.move()

这里,vehicle1是Vehicle类的实例,调用move方法时执行的是Vehicle类的move方法;vehicle2虽然被声明为Vehicle类型,但实际上指向的是Car类的实例,调用move方法时执行的是Car类重写后的move方法,这就是多态的体现 。通过多态,我们可以编写更通用、灵活的代码,提高代码的可维护性和可扩展性 。

六、内存管理与错误处理

在 Swift 编程中,内存管理和错误处理是确保程序稳定、高效运行的关键环节。合理的内存管理能够避免内存泄漏和程序崩溃,而有效的错误处理则能提升程序的健壮性和用户体验 ,下面我们就来详细了解 Swift 中的内存管理与错误处理机制 。

6.1 内存管理

Swift 采用自动引用计数(ARC)机制来管理内存,这大大简化了开发者的工作,降低了因手动内存管理不当而引发的错误风险 。ARC 会在运行时自动跟踪和管理类实例的引用计数,当一个类实例的引用计数降为 0 时,ARC 会自动释放该实例所占用的内存 。

6.1.1 ARC 的工作原理

当你创建一个类的实例时,ARC 会为该实例分配内存,并将其引用计数设置为 1 。每当有新的强引用指向该实例时,引用计数就会增加;而当一个强引用不再指向该实例(例如将引用赋值为nil)时,引用计数就会减少 。当引用计数变为 0 时,ARC 会自动调用实例的析构函数(deinit),并释放实例占用的内存 。例如:


class Person {

let name: String

init(name: String) {

self.name = name

print("(name) is being initialized")

}

deinit {

print("(name) is being deinitialized")

}

}

var person: Person? = Person(name: "Alice")

// 输出: Alice is being initialized

person = nil

// 输出: Alice is being deinitialized

在这个例子中,当person被赋值为nil时,不再有强引用指向Person实例,其引用计数变为 0,ARC 自动释放该实例并调用deinit方法 。

6.1.2 循环引用问题及解决方法

虽然 ARC 极大地简化了内存管理,但在某些情况下,仍可能出现循环引用的问题 。循环引用是指两个或多个对象之间相互持有强引用,导致它们的引用计数永远不会降为 0,从而造成内存泄漏 。例如,在类与闭包之间可能出现循环引用:


class ViewController {

var name = "ViewController"

var completionHandler: (() -> Void)?

deinit {

print("(name) is being deinitialized")

}

}

let controller = ViewController()

controller.completionHandler = {

print("(controller.name) is handling the completion")

}

controller = nil

// 此时ViewController实例不会被释放,因为闭包中持有对controller的强引用,形成循环引用

为了解决循环引用问题,Swift 提供了弱引用(weak)和无主引用(unowned)两种方式 :

弱引用(weak:弱引用不会增加对象的引用计数,并且在对象被释放后,弱引用会自动设置为nil,因此适用于对象之间的生命周期没有紧密绑定,且允许对象在某个时刻变为nil的情况 。例如,将上述闭包中的引用改为弱引用:


let controller = ViewController()

controller.completionHandler = { [weak controller] in

guard let controller = controller else { return }

print("(controller.name) is handling the completion")

}

controller = nil

// 此时ViewController实例会被正常释放,因为闭包中的弱引用不会阻止其引用计数降为0

无主引用(unowned:无主引用同样不会增加对象的引用计数,但与弱引用不同的是,无主引用在对象被释放后不会自动设置为nil,因此使用无主引用时,必须确保在访问引用时对象仍然存在,适用于对象之间的生命周期紧密绑定,且被引用的对象永远不会为nil的情况 。例如:


class Customer {

let name: String

var card: CreditCard?

init(name: String) {

self.name = name

}

deinit {

print("(name) is being deinitialized")

}

}

class CreditCard {

let number: Int

unowned let customer: Customer

init(number: Int, customer: Customer) {

self.number = number

self.customer = customer

}

deinit {

print("Card #(number) is being deinitialized")

}

}

var customer: Customer? = Customer(name: "John")

customer!.card = CreditCard(number: 1234567890, customer: customer!)

customer = nil

// 输出: John is being deinitialized

// 输出: Card #1234567890 is being deinitialized

// 这里使用unowned引用,因为信用卡的生命周期依赖于客户,且客户在信用卡存在期间不会为nil

6.2 错误处理

在编程过程中,错误是难以避免的 。Swift 提供了一套强大而灵活的错误处理机制,使开发者能够优雅地处理运行时可能出现的错误,提高程序的稳定性和可靠性 。

6.2.1 错误的表示与抛出

在 Swift 中,错误用符合Error协议的值来表示 。通常,我们会使用枚举来定义一组相关的错误类型,枚举的关联值还可以提供更多关于错误的详细信息 。例如,定义一个表示文件操作错误的枚举:


enum FileError: Error {

case fileNotFound

case permissionDenied

case corruptedData

}

要抛出一个错误,可以使用throw关键字 。例如,在一个读取文件的函数中,如果文件不存在,就抛出fileNotFound错误:


func readFileContents(filePath: String) throws -> String {

if !FileManager.default.fileExists(atPath: filePath) {

throw FileError.fileNotFound

}

// 实际的文件读取逻辑

return "File contents"

}

6.2.2 错误的处理方式

当一个函数抛出错误时,调用该函数的代码必须处理这个错误 。Swift 提供了以下几种处理错误的方式:

do-catch语句:使用do-catch语句来捕获和处理错误 。将可能抛出错误的代码放在do块中,catch块用于捕获并处理错误 。例如:


do {

let contents = try readFileContents(filePath: "nonexistent.txt")

print(contents)

} catch FileError.fileNotFound {

print("The file was not found.")

} catch FileError.permissionDenied {

print("Permission to access the file was denied.")

} catch {

print("An unknown error occurred: (error)")

}

try?try!

try?:将可能抛出错误的表达式用try?修饰时,如果表达式抛出错误,整个表达式会返回nil;如果没有抛出错误,则返回一个包含结果的可选值 。例如:


let contents = try? readFileContents(filePath: "nonexistent.txt")

if let contents = contents {

print(contents)

} else {

print("There was an error reading the file.")

}

try!:使用try!时,开发者需要确保表达式不会抛出错误 。如果表达式抛出错误,程序会立即崩溃 。例如:


let contents = try! readFileContents(filePath: "existingFile.txt")

print(contents)

一般在确定不会抛出错误或者能接受程序崩溃的情况下,才使用try! 。

七、实战项目演练

理论知识固然重要,但通过实际项目的演练,才能真正将所学知识融会贯通 。现在,让我们一起动手打造一个简单的 Swift 项目 —— 计算器应用,通过这个项目,巩固之前学到的 Swift 知识,包括语法基础、函数使用、面向对象编程等,同时学习如何将这些知识运用到实际的应用开发中 。

7.1 项目需求分析

我们要开发的计算器应用,需要具备基本的算术运算功能,包括加法、减法、乘法、除法 。用户可以输入两个数字,并选择相应的运算符号,点击计算按钮后,应用能够显示出正确的计算结果 。此外,为了提升用户体验,还需要添加一些基本的交互功能,如清除输入、处理输入错误等 。

7.2 项目搭建

首先,打开 Xcode,创建一个新的 iOS 项目 。在项目模板选择界面,选择 “App” 模板,点击 “Next” 。然后填写项目名称,比如 “SimpleCalculator”,选择 “Swift” 作为编程语言,确保 “User Interface” 选择 “Storyboard”,最后点击 “Next” 选择项目保存位置并点击 “Create”,完成项目创建 。

7.3 界面设计

在项目创建完成后,打开 “Main.storyboard” 文件,开始设计计算器的用户界面 。

显示结果的标签(UILabel):从对象库中拖动一个UILabel到视图控制器的顶部,用于显示用户输入的数字和计算结果 。在属性检查器中,设置文本对齐方式为右对齐,字体大小根据界面布局调整,比如设置为 30,背景颜色设置为白色,文本颜色设置为黑色,以便清晰显示内容 。

数字按钮(UIButton):创建数字按钮 0 – 9,从对象库中拖动 9 个UIButton到视图控制器,分别设置它们的标题为 “1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9” 。为了使按钮布局整齐,可以使用UIStackView来组织它们 。选中这 9 个按钮,点击编辑器中的 “Stack” 按钮,将它们水平排列 。然后,设置UIStackView的属性,比如间距为 10,背景颜色为透明等 。接着,再创建一个数字按钮 “0”,并将它与之前的UIStackView组合在一起,形成一个 4 行 3 列的数字按钮布局 。

操作按钮(UIButton):创建加法(“+”)、减法(“-”)、乘法(“*”)、除法(“/”)、等于(“=”)和清除(“C”)按钮 。同样从对象库中拖动UIButton到视图控制器,设置它们的标题,并使用UIStackView将它们垂直排列,放置在数字按钮的右侧 。

布局调整:使用自动布局(Auto Layout)来确保界面在不同设备和屏幕尺寸上都能正确显示 。点击每个视图元素,在属性检查器中添加约束条件,比如设置按钮与父视图的边距、按钮之间的间距等 。例如,对于数字按钮的UIStackView,可以设置它与父视图的顶部、左侧和底部的间距为 20,与操作按钮UIStackView的右侧间距为 10;对于显示结果的UILabel,设置它与父视图的顶部间距为 20,左侧和右侧间距为 10 。

7.4 代码实现

界面设计完成后,接下来在 “ViewController.swift” 文件中实现计算器的逻辑 。


import UIKit

class ViewController: UIViewController {

// 连接显示结果的UILabel

@IBOutlet weak var resultLabel: UILabel!

// 用于存储第一个输入的数字

var firstNumber: Double?

// 用于存储当前输入的数字字符串

var currentInput = ""

// 用于存储操作符

var operatorSymbol: String?

// 数字按钮点击事件处理函数

@IBAction func numberButtonTapped(_ sender: UIButton) {

if let number = sender.titleLabel?.text {

currentInput += number

resultLabel.text = currentInput

}

}

// 操作符按钮点击事件处理函数

@IBAction func operatorButtonTapped(_ sender: UIButton) {

if let operatorText = sender.titleLabel?.text, let number = Double(currentInput) {

firstNumber = number

operatorSymbol = operatorText

currentInput = ""

}

}

// 等于按钮点击事件处理函数,用于计算结果

@IBAction func equalsButtonTapped(_ sender: UIButton) {

guard let first = firstNumber, let operatorStr = operatorSymbol, let second = Double(currentInput) else {

return

}

var result: Double

switch operatorStr {

case "+":

result = first + second

case "-":

result = first - second

case "*":

result = first * second

case "/":

if second != 0 {

result = first / second

} else {

resultLabel.text = "Error: Divide by zero"

return

}

default:

return

}

resultLabel.text = String(result)

}

// 清除按钮点击事件处理函数,用于清空输入和结果

@IBAction func clearButtonTapped(_ sender: UIButton) {

firstNumber = nil

currentInput = ""

operatorSymbol = nil

resultLabel.text = "0"

}

}

在这段代码中:

resultLabel是显示结果的UILabel,通过IBOutlet与界面上的UILabel进行连接 。

firstNumber用于存储第一个输入的数字 。

currentInput用于存储当前输入的数字字符串 。

operatorSymbol用于存储操作符 。

numberButtonTapped函数处理数字按钮的点击事件,将点击的数字追加到currentInput中,并更新resultLabel的显示 。

operatorButtonTapped函数处理操作符按钮的点击事件,将当前输入的数字转换为Double类型并存储到firstNumber中,记录操作符,并清空currentInput 。

equalsButtonTapped函数处理等于按钮的点击事件,根据存储的firstNumber、operatorSymbol和当前输入的第二个数字进行计算,并将结果显示在resultLabel中 。如果除数为零,会显示错误信息 。

clearButtonTapped函数处理清除按钮的点击事件,重置所有变量,并将resultLabel的文本设置为 “0” 。

7.5 连接 UI 元素和代码

完成代码编写后,需要将界面上的 UI 元素与代码中的IBOutlet和IBAction进行连接 。打开 “Main.storyboard”,选择视图控制器,然后打开 “Assistant Editor”(双窗口模式),确保右侧窗口显示 “ViewController.swift” 。

连接resultLabel:按住 Control 键,从界面上的UILabel拖动到 “ViewController.swift” 文件中,在弹出的对话框中,选择 “Outlet”,命名为 “resultLabel”,点击 “Connect” 完成连接 。

连接数字按钮:按住 Control 键,从每个数字按钮拖动到 “ViewController.swift” 文件中,在弹出的对话框中,选择 “Action”,命名为 “numberButtonTapped”,点击 “Connect” 。这样,所有数字按钮都与同一个点击事件处理函数关联 。

连接操作符按钮:同样按住 Control 键,从每个操作符按钮(“+”、“-”、“*”、“/”)拖动到 “ViewController.swift” 文件中,选择 “Action”,命名为 “operatorButtonTapped”,完成连接 。

连接等于按钮:按住 Control 键,从等于按钮(“=”)拖动到 “ViewController.swift” 文件中,选择 “Action”,命名为 “equalsButtonTapped” 。

连接清除按钮:按住 Control 键,从清除按钮(“C”)拖动到 “ViewController.swift” 文件中,选择 “Action”,命名为 “clearButtonTapped” 。

7.6 运行项目

完成上述步骤后,点击 Xcode 顶部的 “Run” 按钮(或按 Cmd + R),选择一个模拟器或连接一个物理设备,运行我们的计算器应用 。在模拟器或设备上,你可以点击数字按钮输入数字,点击操作符按钮选择运算,点击等于按钮得到计算结果,点击清除按钮清空输入 。通过实际操作,验证应用是否按照预期工作,如果发现问题,可以在 Xcode 中进行调试,检查代码逻辑和 UI 连接是否正确 。

通过这个简单的计算器项目,我们不仅巩固了 Swift 的基础知识,还学习了如何使用 Xcode 进行 iOS 应用开发,包括界面设计、代码编写以及 UI 元素与代码的连接 。希望你能在这个项目的基础上,进一步拓展和优化计算器的功能,比如支持更多的运算(如取余、开方等)、添加历史记录功能等,不断提升自己的 Swift 编程能力 。

八、学习资源推荐

想要更深入地学习 Swift,丰富的学习资源必不可少,以下为你推荐一些优质的学习资料,助力你在 Swift 的学习道路上不断进阶 。

8.1 书籍

《Swift 编程权威指南》:Big Nerd Ranch 出品的经典教材,系统讲解 iOS 和 macOS 平台上 Swift 开发的基本概念与编程技巧,通过大量代码示例,帮助读者掌握利用高级特性开发真实应用的能力 。

《Effective Swift》:深入剖析 Swift 语言特性,涵盖 50 个编写高质量 Swift 代码的有效方法,能让开发者写出更简洁、高效、安全的代码 。

《函数式 Swift》:引领读者进入 Swift 函数式编程世界,介绍函数式思想、一等函数、类型驱动开发等内容,通过案例研究讲解如何运用函数式编程解决实际问题 。

8.2 在线课程

斯坦福大学公开课《Developing iOS Apps with Swift》:课程内容全面深入,从 Swift 基础语法到 iOS 应用开发的各个方面都有涉及,由斯坦福大学的资深教授授课,讲解详细,案例丰富,适合系统学习 iOS 开发和 Swift 语言 。

慕课网的 Swift 相关课程:如《Swift 从入门到精通》,课程循序渐进,从基础语法开始,逐步深入到面向对象编程、内存管理、错误处理等高级主题,并通过实际项目案例,帮助学习者巩固所学知识,提升实战能力 。

8.3 博客与论坛

Swift 官方博客:苹果官方为推广 Swift 建立的博客,提供 Swift 语言的最新消息、语法更新以及学习信息,能让开发者及时了解 Swift 的发展动态和新特性 。

Ray Wenderlich:专注于 iOS 开发及 App 游戏设计的专业教学网站,有大量 Swift 相关教学文章,内容丰富,讲解细致,适合不同层次的开发者学习 。

Stack Overflow:知名 IT 技术问答网站,关于 Swift 语言的问题和解答众多,开发者在学习和开发过程中遇到问题时,可以在这里查找解决方案,与其他开发者交流经验 。

希望通过以上对 Swift 学习的全面介绍,你能对 Swift 这门强大的编程语言有更深入的了解和掌握 。从基础语法到高级特性,再到实际项目应用,每一步的学习都是成长的积累 。利用好丰富的学习资源,不断实践,相信你一定能在 Swift 编程的世界里游刃有余,创造出优秀的应用 。如果你在学习过程中有任何疑问或心得,欢迎在评论区留言交流,让我们一起在编程的道路上共同进步 。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容