Swift中的知识点总结

43 阅读8分钟

1、属性只想让外界访问,而不想让他们修改

// 属性只想让外界访问,而不想让他们修改,此时需要 public private(set) var name

public private(set) var name: String

2、智能排序localizedStandardCompare的用法

比较带有数字的字符串时,可以考虑使用localizedStandardCompare,避免挨个字符比较

/*--------------- 智能排序localizedStandardCompare的用法 -------------------*/
// 比较带有数字的字符串时,可以考虑使用localizedStandardCompare,避免挨个字符比较
let fileNames = [
    "File 100.txt",
    "File 5.txt",
    "File 20.txt"
]
​
// (1) 数组排序
let resultArr = fileNames.sorted {
    $0.localizedStandardCompare($1) == .orderedAscending
}
print(resultArr)
​
// (2) 排序
let string1 = "File2.txt"
let string2 = "File10.txt"
let res = string1.localizedStandardCompare(string2)
switch res {
case .orderedAscending:
    print("(string1) 在 (string2) 之前")
case .orderedDescending:
    print("(string1) 在 (string2) 之后")
case .orderedSame:
    print("(string1) 在 (string2) 相同")
}
​
//(3)自定义类型
let fileItem = [
    FileItem(name: "File2.txt", size: 100),
    FileItem(name: "File10.txt", size: 200),
    FileItem(name: "File1.txt", size: 50)
]
let sortedItem = fileItem.sorted()
print(sortedItem)
​
struct FileItem: Comparable {
    let name: String
    let size: Int
    
    static func < (lhs: FileItem, rhs: FileItem) -> Bool {
        return lhs.name.localizedStandardCompare(rhs.name) == .orderedAscending
    }
}
​
// 文件管理
class FileManagerViewModel: ObservableObject {
    @Published var files: [String] = []
    func loadFiles() {
        let fileManager = FileManager.default
        let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
        
        do {
            let allFiles = try fileManager.contentsOfDirectory(atPath: documentsURL.path)
            
            // 智能排序
            files = allFiles.sorted {
                $0.localizedStandardCompare($1) == .orderedAscending
            }
            
        } catch {
            
        }
    }
}

3、能用guard的地方尽量使用guard,不要使用if let绑定语法

过多的if let会造成代码缩进,形成类似地狱式回调的代码。同时也影响了可读性,会很难匹配哪个else对应哪个if 可以多使用guard语法,guard会将变量的作用域提升到top level,从而提升可读性,也能使逻辑处理更清晰

func guardDemo(name: String) {
    guard let captureDevice = AVCaptureDevice.default(for: .video) else {
        return
    }
    // 自动白平衡
    if captureDevice.isWhiteBalanceModeSupported(.continuousAutoWhiteBalance) {
        do {
            try captureDevice.lockForConfiguration()
            captureDevice.whiteBalanceMode = .continuousAutoWhiteBalance
            captureDevice.unlockForConfiguration()
        } catch {
​
        }
    }
​
    // 自动对焦
    if captureDevice.isFocusModeSupported(.continuousAutoFocus) {
        do {
            try captureDevice.lockForConfiguration()
            captureDevice.focusMode = .continuousAutoFocus
            captureDevice.unlockForConfiguration()
        } catch {
​
        }
    }
​
​
    // 自动曝光
    if captureDevice.isExposureModeSupported(.continuousAutoExposure) {
        do {
            try captureDevice.lockForConfiguration()
            captureDevice.exposureMode = .continuousAutoExposure
            captureDevice.unlockForConfiguration()
        } catch {}
    }
}

4、Measurement和MeasurementFormatter的用法

Measurementswift中用于处理物理测量的强大类型,它结合了数值和单位,提供了类型安全、单位转换和国际化的支持

 // 1、创建带有单位的测量值let distance = Measurement(value: 100, unit: UnitLength.meters)
​
let temperature = Measurement(value: 20, unit: UnitTemperature.celsius)
​
let speed = Measurement(value: 60, unit: UnitSpeed.kilometersPerHour)
​
// 2、长度单位let length = Measurement(value: 30, unit: UnitLength.kilometers)
let miles = Measurement(value: 10, unit: UnitLength.miles)
​
let weitht = Measurement(value: 10, unit: UnitMass.kilograms)
​
 // 3、不同单位进行换算(自动换算)let meters = Measurement(value: 1000, unit: UnitLength.meters)
​
let kilometers = Measurement(value: 1, unit: UnitLength.kilometers)
​
let totalLength = meters + kilometers // 2000.0m(kilometers自动转换为m)// 4、使用MeasurementFormatter格式化显示let formatter = MeasurementFormatter()
​
formatter.unitOptions = .providedUnit
​
formatter.unitStyle = .mediumlet dis = Measurement(value: 5.2, unit: UnitLength.kilometers)
print(formatter.string(from: dis)) // 5.2km// 本地化显示
​
formatter.locale = Locale(identifier: "zh_CN")
​
print(formatter.string(from: dis)) // 5.2公里// 温度格式化let tempFormatter = MeasurementFormatter()
​
tempFormatter.numberFormatter.maximumFractionDigits = 1let temperae = Measurement(value: 23.5, unit: UnitTemperature.celsius)
​
print(formatter.string(from: temperae)) // 23.5°C

5、CollectionOfOne和CollectionDifference的用法

ColletionOfOne是一个只包含单个元素的集合类型,实现了Collection协议 CollectionDifference表示两个集合之间的差异

Swift中,applyingCollectionDifference的一个方法,它用于将一个差异(difference)应用到集合上从而生成一个新的集合。这个方法通常用于更新一个数组(或其他集合)以反映另一个数组的状态,而不需要完全替换。

func applying(_ difference: CollectionDifference<Element>) -> [Element]?
​
let diffArr = newArray.difference(from: oldArray)
let allArr = oldArray.applying(diffArr)

/----- ColletionOfOne是一个只包含单个元素的集合类型,实现了Collection协议---------/
  let singleCollection = CollectionOfOne(88)
  print(singleCollection.first!) // 88
  print(singleCollection.count) // 1
​
  /---------CollectionDifference表示两个集合之间的差异-------------------------/
​
  let oldArray = ["A","B","C","D"]
  let newArray = ["A","C","D","E"];
​
  let diffArr = newArray.difference(from: oldArray)
  let allArr = oldArray.applying(diffArr)
  print("最终结果:(String(describing: allArr))") // 最终结果:Optional(["A", "C", "D", "E"])
  for change in diffArr {
    switch change {
    case .remove(let offset,let element,let associatedWith):
    // 移除:索引 1 处的元素 B associatedWith: -1
      print("移除:索引 (offset) 处的元素 (element) associatedWith: (associatedWith ?? -1)") 
    case .insert(let offset,let element,let associatedWith):
    // 插入:索引 3 处的元素 E associatedWith: -1
      print("插入:索引 (offset) 处的元素 (element) associatedWith: (associatedWith ?? -1)")
    }
  }
​
let oldItems = ["a", "b", "c", "d"]
let newItems = ["a", "c", "d", "e"]
let difference = newItems.difference(from: oldItems)
​
tableView.beginUpdates()
for change in difference {
    switch change {
    case .remove(let offset, _, _):
        tableView.deleteRows(at: [IndexPath(row: offset, section: 0)], with: .fade)
    case .insert(let offset, _, _):
        tableView.insertRows(at: [IndexPath(row: offset, section: 0)], with: .fade)
    }
}
tableView.endUpdates()

6、Swift 中的 #error 用法详解

#error是一个编译时指令,用于在编译过程中生成错误信息并停止编译

// 强制要求条件
struct API {
    #error("请设置 API Key")
    static let apiKey = ""
}
​
// 检查配置
enum Environment {
    case development, production
    
    static var current: Environment {
        #error("请设置环境变量")
        return .development
    }
}

// 框架开发中标记未实现功能
class MyFramework {
    func requiredMethod() {
        // 标记为必须实现的方法
        #error("子类必须重写此方法")
    }
}
​
class CustomImplementation: MyFramework {
    override func requiredMethod() {
        // 如果不实现,编译会报错
        print("实现自定义逻辑")
    }
}
// 版本检查
#if swift(<5.0)
    #error("此项目需要 Swift 5.0 或更高版本")
#endif

#errorfatalError()这两个都是用于强制停止程序执行,但工作在不同的阶段,有着本质区别:

核心区别总结

特性#errorfatalError()
执行阶段编译时运行时
位置代码中任何地方只能在函数/方法体内
可执行代码不能包含可以包含其他代码
条件检查编译时条件(#if运行时条件(if, guard
错误类型编译错误运行时错误
可捕获否(但可以设置错误处理)
信息显示编译错误信息运行时崩溃信息

7、dump和print的区别

  • print 用于输出一个或多个值到控制台,适合用于简单的调试和日志记录。print函数会添加换行符
  • dump 主要用于输出对象的内部结构,包含其属性(包括私有属性)以及嵌套对象的结构。它会递归地输出一个对象的镜像,显示所有子成员。通常用于调试复杂的对象,比如数组、字典、自定义实例灯

8、Equatable协议的用法

Equatable协议是用于定义两个对象是否相等。遵循Equatable协议的类型可以使用==运算符进行运算,也可以使用!=运算符进行不等的比较。

  • 自动合成:如果结构体或枚举的所有存储属性都遵循Equatable协议,那么Swift可以自动为结构体或枚举提供Equatable协议的实现。对于枚举,如果枚举没有关联值,或者有关联值但关联值都遵循Equatable,也可以自动合成。
  • 手动合成:如果自动合成的条件不满足,或者你想自定义相等性比较的逻辑,你可以手动实现==运算符。
// Equatable
​
struct Point: Equatable {
    var x: Int
    var y: Int
    // 编译器自动合成 == 操作
}
​
// 枚举自动合成
enum myStatus: Equatable {
    case idle
    case loading(Progress: Double) // 关联值也需要遵循Equatable
    case completed
}
​
class MyEquatable: NSObject {
    override init() {
        super.init()
        // 结构体
        let p1 = Point(x: 10, y: 20)
        let p2 = Point(x: 10, y: 20);
        print(p1 == p2) // true
        // 枚举
        let s1 = myStatus.loading(Progress: 0.5)
        let s2 = myStatus.loading(Progress: 0.5)
        print(s1 == s2) // true
        
        // 类
        let myP1 = MyPerson(name: "Alice", age: 25)
        let myP2 = MyPerson(name: "Alice", age: 25)
        print(myP1 == myP2) //true
        
        // 仅仅比较某个实例
        let book1 = Book(title: "A", author: "lily", isbn: "123")
        let book2 = Book(title: "B", author: "rose", isbn: "123")
        
        print(book1 == book2) // true
    }
}
​
class MyPerson: Equatable {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    // 必须手动实现
    static func == (lhs: MyPerson, rhs: MyPerson) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}
​
struct Book: Equatable {
    var title: String
    var author: String
    var isbn: String
    
    // 自定义比较规则:仅仅对比isbn
    static func == (lhs: Book, rhs: Book) -> Bool {
        return lhs.isbn == rhs.isbn
    }
}
​

9、Swift Hashable 用法详解

Hashable 协议允许类型实例用作集合类型(如 Set、Dictionary)的键。它继承自 Equatable,所以实现 Hashable 的类型也必须实现 ==

Hashable协议用于为类型提供哈希值,这对于将实例用作集合类型(如Set)的键或作为字典的键非常重要。遵循Hashable的类型必须实现hash(into:)方法,并且也必须遵循Equatable

从Swift 4.1开始,对于结构体和枚举,如果所有存储属性都遵循Hashable,编译器可以自动合成Hashable的实现。对于类,需要手动实现。

基本用法:

  1. 结构体自动合成Hashable
  2. 枚举自动合成Hashable(如果所有关联值都遵循Hashable
  3. 手动实现hash(into:)方法

注意:两个实例如果相等(通过==比较),那么它们的哈希值必须相同。反过来,哈希值相同的两个实例不一定相等。

// 结构体
struct hasPoint: Hashable {
    var x: Int
    var y: Int

    // 编译器自动合成hash(into:) 和 ==

}

// 枚举
enum Direction: Hashable {
    case north
    case south
    case east
    case west

    case custom(name: String)// 关联值需是Hashable
}

// 自定义hashable逻辑
struct User: Hashable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date

    // 自定义相等比较
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }


    // 自定义哈希计算
    func hash(into hasher: inout Hasher) {
        // 只使用id 进行哈希(因为 == 只比较id)
        hasher.combine(id)
    }

}

10、Swift CaseIterable的用法

swift中的caseIterable协议用于枚举,它允许我们以集合的方式访问枚举的所有case,使用caseIterable协议,我们可以通过allCases属性获取枚举的所有case组成的数组

注意:CaseIterable 协议只能用于枚举,并且枚举的每个 case 都不能有关联值。

// 1、结构体自动合成
struct Point: Hashable {
    var x: Int
    var y: Int
    // 编译器自动合成 hash(into:) 和 ==
}
​
let point = Point(x: 10, y: 20)
print(point.hashValue) // 自动生成哈希值// 用于 Set
let points: Set<Point> = [Point(x: 1, y: 2), Point(x: 3, y: 4)]
​
// 用于 Dictionary 键
var pointData: [Point: String] = [
    Point(x: 0, y: 0): "origin",
    Point(x: 1, y: 1): "unit"
]
// 2、枚举自动合成
enum Direction: Hashable {
    case north
    case south
    case east
    case west
    case custom(name: String) // 关联值需是 Hashable
}
​
let directions: Set<Direction> = [.north, .south, .north]
print(directions.count) // 2(去重后)// 3、自定义哈希
struct User: Hashable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date
    
    // 自定义相等比较
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }
    
    // 自定义哈希计算
    func hash(into hasher: inout Hasher) {
        // 只使用 id 进行哈希(因为 == 只比较 id)
        hasher.combine(id)
    }
}
​
let user1 = User(id: 1, name: "Alice", email: "a@test.com", createdAt: Date())
let user2 = User(id: 1, name: "Bob", email: "b@test.com", createdAt: Date())
print(user1 == user2) // true(id 相同)