Swift 详细教程
Swift 是苹果公司开发的强大、直观的编程语言,用于 iOS、macOS、watchOS 和 tvOS 应用开发。以下是 Swift 的全面教程:
1. Swift 简介
主要特点
- 安全:类型安全和内存安全设计
- 快速:高性能,接近 C 语言的效率
- 现代:简洁的语法和强大的功能
- 交互式:Playground 提供实时反馈
- 开源:自 Swift 2.0 起完全开源
应用领域
- iOS 应用开发
- macOS 应用开发
- watchOS 应用开发
- tvOS 应用开发
- 服务器端开发 (使用 Vapor 等框架)
2. Swift 开发环境搭建
Xcode 安装 (macOS)
- 从 Mac App Store 下载 Xcode
- 安装完成后,打开 Xcode 接受许可协议
- 安装额外组件
命令行工具
# 检查 Swift 版本
swift --version
# 运行 Swift 文件
swift filename.swift
# 进入 REPL 交互环境
swift
Playground 使用
- 打开 Xcode
- 选择 "Get started with a playground"
- 选择平台和模板
- 开始编写代码并实时查看结果
3. Swift 基础语法
Hello World
print("Hello, World!")
注释
// 单行注释
/*
多行
注释
*/
/// 文档注释
/// - Parameter name: 用户名
/// - Returns: 问候语
func greet(name: String) -> String {
return "Hello, \(name)!"
}
变量和常量
var variable = "可以修改" // 变量
let constant = "不可修改" // 常量
// 类型注解
var age: Int = 25
var name: String = "张三"
var height: Double = 175.5
var isStudent: Bool = true
4. 基本数据类型
整数类型
let minInt8 = Int8.min // -128
let maxInt8 = Int8.max // 127
let minUInt8 = UInt8.min // 0
let maxUInt8 = UInt8.max // 255
let decimal = 17 // 十进制
let binary = 0b10001 // 二进制
let octal = 0o21 // 八进制
let hex = 0x11 // 十六进制
浮点数类型
let double: Double = 3.141592653589793 // 64位
let float: Float = 3.1415926 // 32位
let scientific = 1.25e2 // 1.25 × 10² = 125.0
let hexFloat = 0xFp2 // 15 × 2² = 60.0
布尔类型
let isSwiftAwesome = true
let isObjectiveCOld = false
if isSwiftAwesome {
print("Swift is awesome!")
} else {
print("Well...")
}
字符串
var str = "Hello"
str += " Swift" // "Hello Swift"
// 多行字符串
let multiline = """
This is a
multi-line
string.
"""
// 字符串插值
let age = 25
let message = "I am \(age) years old."
// 字符串操作
let count = str.count // 字符数
let isEmpty = str.isEmpty // 是否为空
let hasPrefix = str.hasPrefix("H") // 是否有前缀
let hasSuffix = str.hasSuffix("t") // 是否有后缀
字符
let char: Character = "A"
for character in "Swift" {
print(character)
}
5. 集合类型
数组 (Array)
var numbers = [1, 2, 3, 4, 5] // [Int]
var names = ["Alice", "Bob"] // [String]
// 访问和修改
let first = numbers[0] // 1
numbers[1] = 20 // [1, 20, 3, 4, 5]
numbers.append(6) // [1, 20, 3, 4, 5, 6]
numbers.insert(0, at: 0) // [0, 1, 20, 3, 4, 5, 6]
numbers.remove(at: 2) // [0, 1, 3, 4, 5, 6]
// 遍历
for number in numbers {
print(number)
}
for (index, number) in numbers.enumerated() {
print("\(index): \(number)")
}
字典 (Dictionary)
var person = [
"name": "Alice",
"age": "25",
"city": "New York"
] // [String: String]
// 访问和修改
person["name"] = "Bob" // 修改
person["country"] = "USA" // 添加
let age = person["age"] // Optional("25")
// 遍历
for (key, value) in person {
print("\(key): \(value)")
}
for key in person.keys {
print(key)
}
for value in person.values {
print(value)
}
集合 (Set)
var colors: Set<String> = ["red", "green", "blue"]
colors.insert("yellow") // 插入
colors.remove("red") // 删除
let hasGreen = colors.contains("green") // 检查包含
// 集合操作
let a: Set = [1, 2, 3]
let b: Set = [3, 4, 5]
let union = a.union(b) // [1, 2, 3, 4, 5]
let intersection = a.intersection(b) // [3]
let difference = a.subtracting(b) // [1, 2]
let symmetricDiff = a.symmetricDifference(b) // [1, 2, 4, 5]
6. 控制流
if 语句
let score = 85
if score >= 90 {
print("优秀")
} else if score >= 80 {
print("良好")
} else if score >= 60 {
print("及格")
} else {
print("不及格")
}
switch 语句
let grade = "B"
switch grade {
case "A":
print("优秀")
case "B", "C":
print("良好")
case "D":
print("及格")
case "F":
print("不及格")
default:
print("无效成绩")
}
// 区间匹配
let score = 85
switch score {
case 90...100:
print("A")
case 80..<90:
print("B")
case 70..<80:
print("C")
case 60..<70:
print("D")
case 0..<60:
print("F")
default:
print("无效分数")
}
for 循环
for i in 1...5 {
print(i) // 1, 2, 3, 4, 5
}
for i in stride(from: 0, to: 10, by: 2) {
print(i) // 0, 2, 4, 6, 8
}
for _ in 1...3 {
print("Hello")
}
while 循环
var count = 0
while count < 5 {
print(count)
count += 1
}
repeat {
print(count)
count -= 1
} while count > 0
控制转移语句
// break
for i in 1...10 {
if i == 5 {
break
}
print(i) // 1, 2, 3, 4
}
// continue
for i in 1...5 {
if i == 3 {
continue
}
print(i) // 1, 2, 4, 5
}
// 带标签的语句
outerLoop: for i in 1...3 {
innerLoop: for j in 1...3 {
if j == 2 {
continue outerLoop
}
print("i: \(i), j: \(j)")
}
}
7. 函数
定义和调用
func greet(name: String) -> String {
return "Hello, \(name)!"
}
let message = greet(name: "Alice") // "Hello, Alice!"
参数标签
func greet(to name: String, from city: String) -> String {
return "Hello \(name) from \(city)!"
}
let message = greet(to: "Bob", from: "Beijing")
默认参数值
func greet(name: String = "Guest") -> String {
return "Hello, \(name)!"
}
let message1 = greet() // "Hello, Guest!"
let message2 = greet(name: "Alice") // "Hello, Alice!"
可变参数
func average(_ numbers: Double...) -> Double {
var total = 0.0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
let avg = average(1, 2, 3, 4, 5) // 3.0
输入输出参数
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 3
var y = 5
swapTwoInts(&x, &y)
print("x: \(x), y: \(y)") // x: 5, y: 3
函数类型
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiply(_ a: Int, _ b: Int) -> Int {
return a * b
}
var mathFunction: (Int, Int) -> Int = add
print(mathFunction(2, 3)) // 5
mathFunction = multiply
print(mathFunction(2, 3)) // 6
// 函数作为参数
func printMathResult(_ mathFunc: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunc(a, b))")
}
printMathResult(add, 3, 5) // Result: 8
// 函数作为返回值
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(_ input: Int) -> Int { return input + 1 }
func stepBackward(_ input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveToZero = chooseStepFunction(backward: currentValue > 0)
while currentValue != 0 {
print(currentValue)
currentValue = moveToZero(currentValue)
}
8. 闭包
闭包表达式
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
// 函数方式
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// 闭包表达式方式
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// 简化
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
reversedNames = names.sorted(by: { $0 > $1 })
reversedNames = names.sorted(by: >)
尾随闭包
func someFunction(takesClosure: () -> Void) {
// 函数体
}
// 不使用尾随闭包
someFunction(takesClosure: {
// 闭包体
})
// 使用尾随闭包
someFunction() {
// 闭包体
}
// 如果闭包是唯一参数,可以省略括号
someFunction {
// 闭包体
}
// sorted 示例
reversedNames = names.sorted { $0 > $1 }
值捕获
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
逃逸闭包
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
自动闭包
var customers = ["Alex", "Ewa", "Barry", "Daniella"]
print(customers.count) // 4
let customerProvider = { customers.remove(at: 0) }
print(customers.count) // 4
print("Now serving \(customerProvider())!") // "Now serving Alex!"
print(customers.count) // 3
// 自动闭包
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customers.remove(at: 0)) // "Now serving Ewa!"
9. 枚举
基本枚举
enum CompassPoint {
case north
case south
case east
case west
}
var direction = CompassPoint.north
direction = .east // 类型已知时可以省略枚举名
// 匹配枚举值
switch direction {
case .north:
print("North")
case .south:
print("South")
case .east:
print("East")
case .west:
print("West")
}
关联值
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode):
print("QR code: \(productCode)")
}
原始值
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
let earthsOrder = Planet.earth.rawValue // 3
let possiblePlanet = Planet(rawValue: 7) // uranus?
递归枚举
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product)) // 18
10. 类和结构体
定义
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
实例创建
let someResolution = Resolution()
let someVideoMode = VideoMode()
// 访问属性
print("Width: \(someResolution.width)")
someVideoMode.resolution.width = 1280
// 结构体成员逐一构造器
let vga = Resolution(width: 640, height: 480)
值类型 vs 引用类型
// 结构体是值类型
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print(hd.width) // 1920
print(cinema.width) // 2048
// 类是引用类型
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print(tenEighty.frameRate) // 30.0
print(alsoTenEighty.frameRate) // 30.0
// 恒等运算符
if tenEighty === alsoTenEighty {
print("Same instance")
}
属性
存储属性
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var range = FixedLengthRange(firstValue: 0, length: 3)
range.firstValue = 6 // 可以修改
// range.length = 4 // 错误,常量不能修改
// 延迟存储属性
class DataImporter {
var filename = "data.txt"
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// importer 属性尚未创建
print(manager.importer.filename) // 此时才创建
计算属性
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
属性观察器
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
stepCounter.totalSteps = 360
stepCounter.totalSteps = 896
类型属性
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
print(SomeStructure.storedTypeProperty) // "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty) // "Another value."
print(SomeEnumeration.computedTypeProperty) // 6
print(SomeClass.computedTypeProperty) // 27
11. 方法
实例方法
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
let counter = Counter()
counter.increment()
counter.increment(by: 5)
counter.reset()
self 属性
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
修改值类型的实例方法
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
类型方法
class SomeClass {
class func someTypeMethod() {
print("Type method")
}
}
SomeClass.someTypeMethod()
12. 继承
基类
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做
}
}
let someVehicle = Vehicle()
print("Vehicle: \(someVehicle.description)")
子类化
class Bicycle: Vehicle {
var hasBasket = false
}
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
重写
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
let train = Train()
train.makeNoise() // "Choo Choo"
// 重写属性
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
防止重写
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
// 使用 final 防止重写
final class FinalClass {
// 类定义
}
13. 初始化
存储属性的初始赋值
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
自定义初始化
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPoint = Celsius(fromFahrenheit: 212.0)
let freezingPoint = Celsius(fromKelvin: 273.15)
类的继承和初始化
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
可失败初始化器
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
let anonymousCreature = Animal(species: "")
if anonymousCreature == nil {
print("The anonymous creature couldn't be initialized")
}
必要初始化器
class SomeClass {
required init() {
// 初始化代码
}
}
class SomeSubclass: SomeClass {
required init() {
// 子类实现
}
}
14. 析构
析构过程
class Bank {
static var coinsInBank = 10_000
static func distribute(coins numberOfCoinsRequested: Int) -> Int {
let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receive(coins: Int) {
coinsInBank += coins
}
}
class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.distribute(coins: coins)
}
func win(coins: Int) {
coinsInPurse += Bank.distribute(coins: coins)
}
deinit {
Bank.receive(coins: coinsInPurse)
}
}
var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")
playerOne!.win(coins: 2_000)
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
print("The bank now only has \(Bank.coinsInBank) coins left")
playerOne = nil
print("PlayerOne has left the game")
print("The bank now has \(Bank.coinsInBank) coins")
15. 可选链
可选链调用
class Person {
var residence: Residence?
}
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
class Room {
let name: String
init(name: String) { self.name = name }
}
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber = buildingNumber, let street = street {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
john.residence?.address = Address()
if john.residence?.address?.street != nil {
print("Street was set.")
} else {
print("Unable to set street.")
}
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
16. 错误处理
表示和抛出错误
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
处理错误
struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
let machine = VendingMachine()
machine.coinsDeposited = 8
do {
try machine.vend(itemNamed: "Chips")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}
将错误转换为可选值
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
禁用错误传播
let photo = try! loadImage(atPath: "./Resources/JohnAppleseed.jpg")
17. 类型转换
类型检查
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
向下转型
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
Any 和 AnyObject
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
18. 扩展
扩展语法
extension SomeType {
// 新功能
}
extension SomeType: SomeProtocol, AnotherProtocol {
// 协议实现
}
计算属性
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
方法
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
3.repetitions {
print("Hello!")
}
下标
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
746381295[0] // 5
746381295[1] // 9
746381295[2] // 2
嵌套类型
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
19. 协议
协议语法
protocol SomeProtocol {
// 协议定义
}
struct SomeStructure: SomeProtocol {
// 结构体实现
}
class SomeClass: SomeSuperclass, SomeProtocol, AnotherProtocol {
// 类实现
}
属性要求
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
print(ncc1701.fullName)
方法要求
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((a * lastRandom + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
print("And another one: \(generator.random())")
异变方法要求
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
协议作为类型
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
委托
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = Array(repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
weak var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(_ game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
print("Started a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: DiceGame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
扩展协议
protocol TextRepresentable {
var textualDescription: String { get }
}
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
协议继承
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
extension SnakesAndLadders: PrettyTextRepresentable {
var textualDescription: String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
var prettyTextualDescription: String {
var output = textualDescription + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "▲ "
case let snake where snake < 0:
output += "▼ "
default:
output += "○ "
}
}
return output
}
}
print(game.prettyTextualDescription)
类专属协议
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// 类专属协议定义
}
协议组合
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
检查协议一致性
protocol HasArea {
var area: Double { get }
}
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
可选协议要求
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
协议扩展
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
print("And here's a random Boolean: \(generator.randomBool())")
20. 泛型
泛型函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
print("someString is now \(someString), and anotherString is now \(anotherString)")
泛型类型
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
let fromTheTop = stackOfStrings.pop()
扩展泛型类型
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
类型约束
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
关联类型
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
// IntStack 的原始实现
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// Container 协议的实现
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
struct Stack<Element>: Container {
// Stack<Element> 的原始实现
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 协议的实现
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
泛型 Where 语句
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 检查每一对元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
具有泛型 Where 子句的扩展
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return topItem == item
}
}
if stackOfStrings.isTop("tres") {
print("Top element is tres.")
} else {
print("Top element is something else.")
}
21. 访问控制
模块和源文件
- 模块:独立的代码分发单元(如框架或应用)
- 源文件:模块中的单个 Swift 源代码文件
访问级别
- open 和 public:可以被模块外访问
- internal:默认级别,模块内可访问
- fileprivate:文件内可访问
- private:封闭声明内可访问
访问控制语法
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}
public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
自定义类型
public class SomePublicClass { // 显式 public 类
public var somePublicProperty = 0 // 显式 public 类成员
var someInternalProperty = 0 // 隐式 internal 类成员
fileprivate func someFilePrivateMethod() {} // 显式 fileprivate 类成员
private func somePrivateMethod() {} // 显式 private 类成员
}
class SomeInternalClass { // 隐式 internal 类
var someInternalProperty = 0 // 隐式 internal 类成员
fileprivate func someFilePrivateMethod() {} // 显式 fileprivate 类成员
private func somePrivateMethod() {} // 显式 private 类成员
}
fileprivate class SomeFilePrivateClass { // 显式 fileprivate 类
func someFilePrivateMethod() {} // 隐式 fileprivate 类成员
private func somePrivateMethod() {} // 显式 private 类成员
}
private class SomePrivateClass { // 显式 private 类
func somePrivateMethod() {} // 隐式 private 类成员
}
子类
public class A {
fileprivate func someMethod() {}
}
internal class B: A {
override internal func someMethod() {
super.someMethod()
}
}
常量、变量、属性和下标
private var privateInstance = SomePrivateClass()
public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits += 1
}
}
public init() {}
}
var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked."
stringToEdit.value += " This edit will increment numberOfEdits."
stringToEdit.value += " So will this one."
print("The number of edits is \(stringToEdit.numberOfEdits)")
22. 高级运算符
位运算符
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 0b00111100
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 0b11111110
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 0b00010001
let shiftBits: UInt8 = 4 // 00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits >> 2 // 00000001
溢出运算符
var unsignedOverflow = UInt8.max
unsignedOverflow = unsignedOverflow &+ 1 // 0
var unsignedOverflow = UInt8.min
unsignedOverflow = unsignedOverflow &- 1 // 255
var signedOverflow = Int8.min
signedOverflow = signedOverflow &- 1 // 127
运算符函数
struct Vector2D {
var x = 0.0, y = 0.0
}
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
前缀和后缀运算符
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
let alsoPositive = -negative
复合赋值运算符
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
等价运算符
extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
}
let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
自定义运算符
prefix operator +++
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
优先级和结合性
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
23. Swift 与 Cocoa 和 Objective-C
与 Objective-C API 交互
import UIKit
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
label.text = "Hello, Swift!"
label.backgroundColor = UIColor.red
可空性
let url = URL(string: "https://www.apple.com")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error: \(error)")
return
}
guard let data = data else {
return
}
print("Received \(data.count) bytes")
}
task.resume()
类型转换
let subviews = view.subviews
for view in subviews {
if let button = view as? UIButton {
print("Found a button with title: \(button.title(for: .normal) ?? "")")
}
}
闭包与块
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let sortedNames = names.sorted { (s1: String, s2: String) -> Bool in
return s1 > s2
}
对象比较
let object1 = NSObject()
let object2 = NSObject()
let object3 = object1
print(object1 == object2) // false
print(object1 == object3) // true
print(object1 === object2) // false
print(object1 === object3) // true
24. 内存管理
自动引用计数
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John Appleseed")
reference2 = reference1
reference3 = reference1
reference1 = nil
reference2 = nil
reference3 = nil
强引用循环
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
弱引用
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // "John Appleseed is being deinitialized"
unit4A = 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: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
闭包的强引用循环
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil
25. SwiftUI 简介
基本 SwiftUI 视图
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, SwiftUI!")
.font(.title)
.foregroundColor(.blue)
Image(systemName: "star.fill")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.yellow)
Button(action: {
print("Button tapped")
}) {
Text("Tap Me")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
}
状态管理
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
.font(.largeTitle)
HStack {
Button(action: {
self.count -= 1
}) {
Text("-")
.padding()
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(5)
}
Button(action: {
self.count += 1
}) {
Text("+")
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(5)
}
}
}
}
}
列表和导航
struct ListView: View {
let names = ["Alice", "Bob", "Charlie", "David", "Eve"]
var body: some View {
NavigationView {
List(names, id: \.self) { name in
NavigationLink(destination: DetailView(name: name)) {
Text(name)
}
}
.navigationBarTitle("Names")
}
}
}
struct DetailView: View {
let name: String
var body: some View {
Text("Detail for \(name)")
.font(.largeTitle)
.navigationBarTitle(name)
}
}
网络请求
struct Post: Codable, Identifiable {
let id: Int
let title: String
let body: String
}
class PostsViewModel: ObservableObject {
@Published var posts = [Post]()
func fetchPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode([Post].self, from: data) {
DispatchQueue.main.async {
self.posts = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct PostsView: View {
@ObservedObject var viewModel = PostsViewModel()
var body: some View {
List(viewModel.posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.onAppear {
self.viewModel.fetchPosts()
}
}
}
26. 资源与进阶学习
官方资源
学习资源
开源项目
社区
Swift 是一门强大而现代的编程语言,本教程涵盖了 Swift 的主要特性和概念。要精通 Swift 需要不断实践,建议通过实际项目来加深理解。随着 Swift 的不断发展,保持学习最新特性和最佳实践非常重要。