笔记
本篇文章记录一下100 Days of SwiftUI
第15天的笔记内容,因为第15天是对Swift 简介的总结,所以包含了前14天的笔记内容,具备Swift基础的同学可以跳过这部分笔记
创建常量和变量
// Swift可以创建常量和变量,但是常量通常更合适
// 变量
var name = "Ted"
name = "Rebecca"
// 常量
let user = "Daphne"
// 打印
print(user)
字符串
let actor = "Tom Cruise"
let actor = "Tom Cruise ??♂?"
// 特殊符号转转义前面加 \
let quote = "He tapped a sign saying \"Believe\" and walked away."
// 跨越多行的字符串
let movie = """
A day in
the life of an
Apple engineer
"""
// 获取字符数量.count
print(actor.count)
// 字符串是否以特定字母开头或结尾
print(quote.hasPrefix("He"))
print(quote.hasSuffix("Away.")) // Swift 中的字符串区分大小写,因此这里返回 false
整数
// Int
let score = 10
let higherScore = score + 10
let halvedScore = score / 2
// 支持复合赋值运算符
var counter = 10
counter += 5
// 是否能被某个数整除
let number = 120
print(number.isMultiple(of: 3))
// 特定范围内生成随机数
let id = Int.random(in: 1...1000)
浮点数
// Double
let score = 3.0
布尔类型
let goodDogs = true
let gameOver = false
// 反转值
var isSaved = false
isSaved.toggle()
拼接字符串
let name = "Taylor"
let age = 26
let message = "I'm \(name) and I'm \(age) years old."
print(message)
数组
var colors = ["Red", "Green", "Blue"]
let numbers = [4, 8, 15, 16]
var readings = [0.1, 0.5, 0.8]
print(colors[0])
print(readings[2])
// 添加元素
colors.append("Tartan")
// 删除指定元素
colors.remove(at: 0)
print(colors.count)
// 是否包含某个元素
print(colors.contains("Octarine"))
字典
let employee = [
"name": "Taylor",
"job": "Singer"
]
// 访问的元素不存在则会使用default的值
print(employee["name", default: "Unknown"])
print(employee["job", default: "Unknown"])
集合
// 集合与数组类似,但是是无序的,且忽略重复值
var numbers = Set([1, 1, 3, 5, 7])
print(numbers)
// 添加元素
numbers.insert(10)
枚举
enum Weekday {
case monday, tuesday, wednesday, thursday, friday
}
var day = Weekday.monday
day = .friday
类型注释
let player: String = "Roy"
var luckyNumber: Int = 13
let pi: Double = 3.141
var isEnabled: Bool = true
var albums: Array<String> = ["Red", "Fearless"]
var user: Dictionary<String, String> = ["id": "@twostraws"]
var books: Set<String> = Set(["The Bluest Eye", "Foundation"])
// 数组和字典常用写法
var albums: [String] = ["Red", "Fearless"]
var user: [String: String] = ["id": "@twostraws"]
// 创建空的字符串数据
var teams: [String] = [String]()
var clues = [String]()
// 枚举
enum UIStyle {
case light, dark, system
}
var style: UIStyle = .light
if判断语句
let age = 16
if age < 12 {
print("You can't vote")
} else if age < 18 {
print("You can vote soon.")
} else {
print("You can vote now.")
}
// && 组合条件是两个部分都为真时,整个条件才为真,|| 则是其中任意一个条件为真,整个条件就为真
let temp = 26
if temp > 20 && temp < 30 {
print("It's a nice day.")
}
Switch开关语句
enum Weather {
case sun, rain, wind
}
let forecast = Weather.sun
switch forecast {
case .sun:
print("A nice day.")
case .rain:
print("Pack an umbrella.")
default:
print("Should be okay.")
}
三元运算符
let age = 18
let canVote = age >= 18 ? "Yes" : "No"
循环
for循环
let platforms = ["iOS", "macOS", "tvOS", "watchOS"]
for os in platforms {
print("Swift works on \(os).")
}
// 1...12是1到12,包含12
for i in 1...12 {
print("5 x \(i) is \(5 * i)")
}
// 1..<13是1到12,不包含13
for i in 1..<13 {
print("5 x \(i) is \(5 * i)")
}
// 不需要循环变量可以使用_
var lyric = "Haters gonna"
for _ in 1...5 {
lyric += " hate"
}
print(lyric)
while循环
var count = 10
while count > 0 {
print("\(count)…")
count -= 1
}
print("Go!")
// continue跳过当前的循环迭代并继续执行以下循环
// break退出循环并跳过所有剩余迭代
let files = ["me.jpg", "work.txt", "sophie.jpg"]
for file in files {
if file.hasSuffix(".jpg") == false {
continue
}
print("Found picture: \(file)")
}
函数
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(number: 5)
// 返回值
func rollDice() -> Int {
return Int.random(in: 1...6)
}
let result = rollDice()
print(result)
// 如果函数仅包含一行代码可以省略return
func rollDice() -> Int {
Int.random(in: 1...6)
}
函数返回多个值
func getUser() -> (firstName: String, lastName: String) {
(firstName: "Taylor", lastName: "Swift")
}
let user = getUser()
print("Name: \(user.firstName) \(user.lastName)")
// 如果不需要元祖中的所有值,可以解构元组,jiang将其分解为单独的值,_可以会儿一些值
let (firstName, _) = getUser()
print("Name: \(firstName)")
自定义参数标签
// 如果调用函数时不想传递参数名可以在参数名前面加_
func isUppercase(_ string: String) -> Bool {
string == string.uppercased()
}
let string = "HELLO, WORLD"
let result = isUppercase(string)
// 设置函数外部、内部参数,for为外部参数,number为内部参数
func printTimesTables(for number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(for: 5)
提供参数的默认值
func greet(_ person: String, formal: Bool = false) {
if formal {
print("Welcome, \(person)!")
} else {
print("Hi, \(person)!")
}
}
greet("Tim", formal: true)
greet("Taylor")
处理函数中的错误
// 定义可能发生的错误
enum PasswordError: Error {
case short, obvious
}
// 编写可以抛出错误的函数
func checkPassword(_ password: String) throws -> String {
if password.count < 5 {
throw PasswordError.short
}
if password == "12345" {
throw PasswordError.obvious
}
if password.count < 10 {
return "OK"
} else {
return "Good"
}
}
let string = "12345"
// 通过do包含抛出异常函数,用try调用抛出异常函数,然后捕获发生的错误
// 捕获错误时,需要有一个catch可以处理各种错误
do {
let result = try checkPassword(string)
print("Rating: \(result)")
} catch PasswordError.obvious {
print("I have the same combination on my luggage!")
} catch {
print("There was an error.")
}
闭包
// 可以将功能直接分配给常量或变量
let sayHello = {
print("Hi there!")
}
sayHello()
// 接受参数,用in来标记参数和返回类型的结束,in后面是闭包本身的主体
let sayHello = { (name: String) -> String in
"Hi \(name)!"
}
let team = ["Gloria", "Suzanne", "Tiffany", "Tasha"]
let onlyT = team.filter({ (name: String) -> Bool in
return name.hasPrefix("T")
})
// 闭包在Swift中的使用,例如filter()方法通过测试运行数组的所有元素,测试为true的元素都会在新数组中返回
let team = ["Gloria", "Suzanne", "Tiffany", "Tasha"]
let onlyT = team.filter({ (name: String) -> Bool in
return name.hasPrefix("T")
})
print(onlyT) // ["Tiffany", "Tasha"]
// 对上述闭包进行改造,因为闭包主体只有一行代码,可以删除return
let onlyT = team.filter({ (name: String) -> Bool in
name.hasPrefix("T")
})
// 忽略闭包中的指定类型
let onlyT = team.filter({ name in
name.hasPrefix("T")
})
// 改造为尾随闭包的特殊语法
let onlyT = team.filter { name in
name.hasPrefix("T")
}
// Swift提供简短的参数名称,可以使用的$0
let onlyT = team.filter {
$0.hasPrefix("T")
}
结构体
// 结构体可以创建自定义数据类型,并且具有自己的属性和方法
struct Album {
let title: String
let artist: String
var isReleased = true
func printSummary() {
print("\(title) by \(artist)")
}
}
// 使用初始化器创建结构体实例
let red = Album(title: "Red", artist: "Taylor Swift")
print(red.title)
red.printSummary()
// 结构体方法更改其属性时需要标记为mutating
mutating func removeFromSale() {
isReleased = false
}
计算属性
struct Employee {
let name: String
var vacationAllocated = 14
var vacationTaken = 0
// 计算属性在每次访问时都会计算其值
var vacationRemaining: Int {
vacationAllocated - vacationTaken
}
}
// 计算属性提供了getter和setter方法
var vacationRemaining: Int {
get {
vacationAllocated - vacationTaken
}
set {
vacationAllocated = vacationTaken + newValue
}
}
属性观察器
// 属性观察器是在属性更改时运行的代码片段
// didSet在属性刚刚更改时运行,willSet在属性更改之前运行
struct Game {
var score = 0 {
didSet {
print("Score is now \(score)")
}
}
}
var game = Game()
game.score += 10
game.score -= 3
自定义初始化器
struct Player {
let name: String
let number: Int
init(name: String) {
self.name = name
number = Int.random(in: 1...99)
}
}
访问控制
// Swift 有多种用于结构内部访问控制的选项,但最常见的是四种:
// private:不要让结构体之外的任何东西使用它
// private(set):结构之外的任何内容都可以读取此内容,但不要让他们更改它
// fileprivate:不要让当前文件之外的任何内容使用它
// public:让任何人、任何地方都可以使用它
struct BankAccount {
private(set) var funds = 0
mutating func deposit(amount: Int) {
funds += amount
}
mutating func withdraw(amount: Int) -> Bool {
if funds > amount {
funds -= amount
return true
} else {
return false
}
}
}
let account = BankAccount(funds: 100)
print(account. funds) // ?
account.funds += 100 // ?
静态属性和方法
struct AppData {
static let version = "1.3 beta 2"
static let settings = "settings.json"
}
print(AppData.version)
类
// 类与结构体的区别一:可以通过继承其他类创建类
class Employee {
let hours: Int
init(hours: Int) {
self.hours = hours
}
func printSummary() {
print("I work \(hours) hours a day.")
}
}
class Developer: Employee {
func work() {
print("I'm coding for \(hours) hours.")
}
}
let novall = Developer(hours: 8)
novall.work()
novall.printSummary()
// 如果子类想更改从父类获得的方法,必须使用override
override func printSummary() {
print("I spend \(hours) hours a day searching Stack Overflow.")
}
// 类与结构体的区别二:初始化器不同
// 1.Swift 不会为类生成成员初始化器。(类的初始化比结构体复杂得多)
// 2.如果子类具有自定义初始值设定项,则它必须始终在完成设置自己的属性后调用父类的初始值设定项。
// 3.如果子类没有任何初始值设定项,它将自动继承其父类的初始值设定项。
class Vehicle {
let isElectric: Bool
init(isElectric: Bool) {
self.isElectric = isElectric
}
}
class Car: Vehicle {
let isConvertible: Bool
init(isElectric: Bool, isConvertible: Bool) {
self.isConvertible = isConvertible
// 即上述的第二点
super.init(isElectric: isElectric)
}
}
// 类与结构体的区别三:类实例的所有副本共享其数据(类型引用),而结构体不是(值引用)
class Singer {
var name = "Adele"
}
var singer1 = Singer()
var singer2 = singer1
singer2.name = "Justin"
print(singer1.name) // Justin,如果是Struct则是Adele
print(singer2.name) // Justin,如果是Struct则是Justin
// 类与结构体的区别四:类可以有一个析构器,当对对象的最后一个引用被销毁时,该析构器被调用
class User {
let id: Int
init(id: Int) {
self.id = id
print("User \(id): I'm alive!")
}
deinit {
print("User \(id): I'm dead!")
}
}
for i in 1...3 {
let user = User(id: i)
print("User \(user.id): I'm in control!")
}
// 类与结构体的区别五:即使类本身是常量,类也允许我们更改变量属性,类不需要mutating使用关键字来更改其数据
class User {
var name = "Paul"
}
let user = User()
user.name = "Taylor"
print(user.name)
协议
// 协议定义了我们期望数据类型支持的功能,而 Swift 确保我们的代码遵循这些规则
protocol Vehicle {
func estimateTime(for distance: Int) -> Int
func travel(distance: Int)
}
struct Car: Vehicle {
func estimateTime(for distance: Int) -> Int {
distance / 50
}
func travel(distance: Int) {
print("I'm driving \(distance)km.")
}
}
// 可以编写一个接受任何符合Vehicle类型的函数
func commute(distance: Int, using vehicle: Vehicle) {
if vehicle.estimateTime(for: distance) > 100 {
print("Too slow!")
} else {
vehicle.travel(distance: distance)
}
}
let car = Car()
commute(distance: 100, using: car)
// 协议可以添加属性
protocol Vehicle {
var name: String { get } // 标记为get,可能是常量或计算属性
var currentPassengers: Int { get set } // 标记为get set,可能是变量或带有 getter 和 setter 的计算属性
func estimateTime(for distance: Int) -> Int
func travel(distance: Int)
}
扩展
// 扩展允许我们给任何类型添加方法
extension String {
// Swift 的字符串有一个修剪空格和换行的方法,但它很长,所以我们可以将它变成一个扩展
func trimmed() -> String {
self.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
var quote = " The truth is rarely pure and never simple "
let trimmed = quote.trimmed()
// 如果您想直接更改值而不是返回新值,请将您的方法标记为mutating如下所示
extension String {
mutating func trim() {
self = self.trimmed()
}
}
quote.trim()
// 扩展还可以向类型添加计算属性
extension String {
var lines: [String] {
self.components(separatedBy: .newlines)
}
}
let lyrics = """
But I keep cruising
Can't stop, won't stop moving
print(lyrics.lines.count) // 2
协议扩展
// 对协议进行扩展
extension Collection { // Array、Dictionary和Set都符合Collection协议
var isNotEmpty: Bool {
isEmpty == false
}
}
let guests = ["Mario", "Luigi", "Peach"]
if guests.isNotEmpty {
print("Guest count: \(guests.count)")
}
可选型
// 可选值表示数据的缺失
// 它们区分值为 0 的整数和根本没有值的整数
let opposites = [
"Mario": "Wario",
"Luigi": "Waluigi"
]
let peachOpposite = opposites["Peach"] // 特殊值nil,意味着没有值
// Swift不允许直接使用可选型数据,因为可能是空的,所以我们需要解开可选性数据才能使用它
// 查看内部是否有值,如果有则取出并使用它
// 第一种解包方式(最常见的方式)
if let marioOpposite = opposites["Mario"] {
print("Mario's opposite is \(marioOpposite)")
}
用guard解包
// 第二种解包方式
// gurad let与if let非常相似,只是逻辑颠倒过来
func printSquare(of number: Int?) {
guard let number = number else {
print("Missing input")
return
}
print("\(number) x \(number) is \(number * number)")
}
// 可以在任何条件下使用gurad,包括不解开选项的条件,例如防护数组为空
nil合并
// 第三种解包方式
// 在可选值为空时提供默认值
let tvShows = ["Archer", "Babylon 5", "Ted Lasso"]
let favorite = tvShows.randomElement() ?? "None"
// nil 合并运算符在创建选项的许多地方都很有用
let input = ""
let number = Int(input) ?? 0
print(number)
可选链
let names = ["Arya", "Bran", "Robb", "Sansa"]
let chosen = names.randomElement()?.uppercased() // 如果可选型内部有值,则将其解开,并......
print("Next in line: \(chosen ?? "No one")")
可选型try?
enum UserError: Error {
case badID, networkFailed
}
func getUser(id: Int) throws -> String {
throw UserError.networkFailed
}
// 当不关心抛出了什么错误时可以使用 try?,只关心调用是否返回用户
// 如果确切想知道抛出了什么错误就不应该使用try?而是使用do try catch
if let user = try? getUser(id: 23) {
print("User: \(user)")
}