学习笔记 - Swfit 6.1 - 语法概览

获取版本号

swift 复制代码
swift -version

Hello world

swift 复制代码
print("Hello, world!")

末尾不需要分号

常量(let);变量(var)

swift 复制代码
var myVariable = 42
myVariable = 50
let myConstant = 42

可以显式声明变量类型,若没有则隐式推断,类似下面的Double

swift 复制代码
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70

赋值同一类型

swift 复制代码
let label = "The width is "
let width = 94
// 去掉String报错
// Binary operator '+' cannot be applied to operands of type 'String' and 'Int'
let widthLabel = label + String(width)

字符串中通过\(变量)的方法得到变量的字符串表示

swift 复制代码
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

多行文本的写法

swift 复制代码
// """ """ 包含的内容
let quotation = """
        Even though there's whitespace to the left,
        the actual lines aren't indented.
            Except for this line.
        Double quotes (") can appear without being escaped.

        I still have \(apples + oranges) pieces of fruit.
        """

数组/字典通过 [] 遍历

swift 复制代码
var fruits = ["strawberries", "limes", "tangerines"]
fruits[1] = "grapes"


var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
 ]
occupations["Jayne"] = "Public Relations"

自动扩容

swift 复制代码
fruits.append("blueberries")
print(fruits)

空数组/字典

swift 复制代码
fruits = []
occupations = [:]

// 指定类型
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]

控制流

循环: for-in,while,repeat-while 条件: if

swift 复制代码
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
print(teamScore)
// Prints "11"

if + 赋值

swift 复制代码
let scoreDecoration = if teamScore > 10 {
    "🎉"
} else {
    ""
}
print("Score:", teamScore, scoreDecoration)

属于语法糖,少写一个赋值

swift 复制代码
var n = 2
while n < 100 {
    n *= 2
}
print(n)
// Prints "128"


var m = 2
// 这个其它语言中一般是用do, 用repeat可能是为了强调循环?
repeat {
    m *= 2
} while m < 100
print(m)
// Prints "128"
swift 复制代码
var total = 0
for i in 0..<4 {
    total += i
}
print(total)
// Prints "6"

for i in 0..<4, i的遍历区间是0,1,2,3

可选型(optional)

  1. 类型后面跟问号
  2. if let name = optionalName 会解包,能确定namenil还是有具体的值
swift 复制代码
var optionalString: String? = "Hello"
print(optionalString == nil)
// Prints "false"


var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
    print(greeting)
}

还有种处理可选型的方法是通过??

swift 复制代码
let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"

nickname 有值则用nickname的值,没有值则用??后的值。这是种默认值的写法,更健壮的处理当数据可能为nil的情况。

switch 的写法

swift 复制代码
let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}
// Prints "Is it a spicy red pepper?"

case类型可以是String,这点比Objective-C方便很多,编程语言是给人读的。

去掉default语句会报Switch must be exhaustive编译错误,这是编程的实践,避免开发者遗漏

未使用的变量_

swift 复制代码
let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)

访问for循环时没有使用到字典的key,用_可以告诉编译器这件事方便优化

swift 复制代码
// 如果非要定义了不用,会给一个警告
Immutable value 'key' was never used; consider replacing with '_' or removing it

函数和闭包

  1. 函数关键字 func
  2. -> 后跟返回值
  3. 函数定义对参数的声明和objective-c类似
swift 复制代码
func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

通过元组(tuple)可以处理多个值

swift 复制代码
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0


    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }


    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
// Prints "120"
print(statistics.2)
// Prints "120"

嵌套函数

swift 复制代码
func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
returnFifteen()

这个见的少,只见过类似概念的Java里的内部类

函数作返回值,参数

Functions are a first-class type. This means that a function can return another function as its value.
类型:规定了变量可以取的值得范围,以及该类型的值可以进行的操作。根据类型的值的可赋值状况,可以把类型分为三类: 1、一级的(first class)。该等级类型的值可以传给子程序作为参数,可以从子程序里返回,可以赋给变量。大多数程序设计语言里,整型、字符类型等简单类型都是一级的。 2、二级的(second class)。该等级类型的值可以传给子程序作为参数,但是不能从子程序里返回,也不能赋给变量。 3、三级的(third class)。该等级类型的值连作为参数传递也不行。

函数当返回值

swift 复制代码
func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

函数当参数

swift 复制代码
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
// lessThanTen 函数作为参数
hasAnyMatches(list: numbers, condition: lessThanTen)

闭包

函数是一种特殊的闭包,有名字的闭包(closures)

swift 复制代码
// {} 内的是闭包
numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})

简化闭包写法

省略return

swift 复制代码
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

当闭包是函数的唯一参数时,可以完全省略括号

swift 复制代码
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)

对象和类

swift 复制代码
class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

创建对象

swift 复制代码
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

初始化函数

swift 复制代码
class NamedShape {
    var numberOfSides: Int = 0
    var name: String


    init(name: String) {
       self.name = name
    }


    func simpleDescription() -> String {
       return "A shape with \(numberOfSides) sides."
    }
}

子类重写父类方法

swift 复制代码
class Square: NamedShape {
    var sideLength: Double


    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }


    func area() -> Double {
        return sideLength * sideLength
    }


    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

属性访问器getter && setter

swift 复制代码
class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0


    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }


    var perimeter: Double {
        get {
             return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }


    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
// Prints "9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)
// Prints "3.3000000000000003"

计算属性

swift 复制代码
class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
// Prints "10.0"
print(triangleAndSquare.triangle.sideLength)
// Prints "10.0"
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
// Prints "50.0"

枚举和结构体

swift 复制代码
enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king


    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
let aceRawValue = ace.rawValue

rawValue 默认0开始,然后递增

枚举的 case 值是实际值,而不仅仅是编写其原始值的另一种方式。事实上,在没有有意义的原始值的情况下,可以不必提供原始值。

swift 复制代码
enum Suit {
    case spades, hearts, diamonds, clubs


    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()

并发

异步方法的关键字 async,调用异步方法前面加await

swift 复制代码
func fetchUserID(from server: String) async -> Int {
    print("fetchUserID")
    if server == "primary" {
        return 97
    }
    return 501
}

func fetchUsername(from server: String) async -> String {
    print("fetchUsername")
    let userID = await fetchUserID(from: server)
    if userID == 501 {
        return "John Appleseed"
    }
    return "Guest"
}

func connectUser(to server: String) async {
    // 异步调用
    async let userID = fetchUserID(from: server)
    async let username = fetchUsername(from: server)
    // 等待返回后执行下一句
    let greeting = await "Hello \(username), user ID \(userID)"
    print(greeting)
}

Task {
    await connectUser(to: "primary2")
}

等待任务组

swift 复制代码
let userIDs = await withTaskGroup(of: Int.self) { group in
    for server in ["primary", "secondary", "development"] {
        group.addTask {
            return await fetchUserID(from: server)
        }
    }


    var results: [Int] = []
    for await result in group {
        results.append(result)
    }
    return results
}

协议与扩展

使用关键字Protocol定义协议

swift 复制代码
protocol ExampleProtocol {
     var simpleDescription: String { get }
     mutating func adjust()
}

类,枚举和结构体都可以实现协议

swift 复制代码
class SimpleClass: ExampleProtocol {
     var simpleDescription: String = "A very simple class."
     var anotherProperty: Int = 69105
     func adjust() {
          simpleDescription += "  Now 100% adjusted."
     }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription


struct SimpleStructure: ExampleProtocol {
     var simpleDescription: String = "A simple structure"
     mutating func adjust() {
          simpleDescription += " (adjusted)"
     }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

结构体的adjust方法前有mutating修饰用于修改结构体的成员

可以使用扩展来为已有的类型添加方法

swift 复制代码
extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number \(self)"
    }
    mutating func adjust() {
        self += 42
    }
 }
print(7.simpleDescription)
// Prints "The number 7"

异常捕获

通过实现Error协议来表示错误类型

swift 复制代码
enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

throws 关键字来抛出异常

swift 复制代码
func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

可以使用 do-catch 来捕获异常,try 修饰可能会抛出异常的代码

swift 复制代码
do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
// Prints "Job sent"

多种异常的处理

swift 复制代码
do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
// Prints "Job sent"

defer关键词修饰的代码会在函数所有代码执行完成后,函数return返回前执行。 无论代码是否抛出异常都会执行。它一般用于建立或清理代码。

有点类似finally 部分,可以避免异常的时候没释放内存。这在某个函数有多个返回出口的时候特别有用。

swift 复制代码
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]


func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }


    let result = fridgeContent.contains(food)
    return result
}
if fridgeContains("banana") {
    print("Found a banana")
}
print(fridgeIsOpen)

泛型

泛型: <类型>

swift 复制代码
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result: [Item] = []
    for _ in 0..<numberOfTimes {
         result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)

可以对方法,函数,类,枚举,结构体应用泛型

swift 复制代码
// where T.Element: Equatable , T.Element == U.Element表示:只有元素遵循 Equatable 协议且内部的类型一致时才可以使用anyCommonElements方法
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
   return false
}
anyCommonElements([1, 2, 3], [3])

参考

  1. Swift-Doc
  2. 什么是 First-class function?
  3. Swift系列之关于Swift defer的正确使用
  4. Swift限定泛型、协议扩展或约束的where
相关推荐
大熊猫侯佩9 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩9 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩9 小时前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩9 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
season_zhu1 天前
iOS开发:关于日志框架
ios·架构·swift
大熊猫侯佩1 天前
SwiftUI 中如何花样玩转 SF Symbols 符号动画和过渡特效
swiftui·swift·apple
大熊猫侯佩1 天前
SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
swiftui·swift·apple
大熊猫侯佩1 天前
使用令牌(Token)进一步优化 SwiftData 2.0 中历史记录追踪(History Trace)的使用
数据库·swift·apple
大熊猫侯佩1 天前
SwiftUI 在 iOS 18 中的 ForEach 点击手势逻辑发生改变的解决
swiftui·swift·apple