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的用法
Measurement是swift中用于处理物理测量的强大类型,它结合了数值和单位,提供了类型安全、单位转换和国际化的支持
// 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 = .medium
let 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 = 1
let temperae = Measurement(value: 23.5, unit: UnitTemperature.celsius)
print(formatter.string(from: temperae)) // 23.5°C
5、CollectionOfOne和CollectionDifference的用法
ColletionOfOne是一个只包含单个元素的集合类型,实现了Collection协议
CollectionDifference表示两个集合之间的差异
在Swift中,applying是CollectionDifference的一个方法,它用于将一个差异(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
#error与 fatalError()这两个都是用于强制停止程序执行,但工作在不同的阶段,有着本质区别:
核心区别总结
| 特性 | #error | fatalError() |
|---|---|---|
| 执行阶段 | 编译时 | 运行时 |
| 位置 | 代码中任何地方 | 只能在函数/方法体内 |
| 可执行代码 | 不能包含 | 可以包含其他代码 |
| 条件检查 | 编译时条件(#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的实现。对于类,需要手动实现。
基本用法:
- 结构体自动合成
Hashable - 枚举自动合成
Hashable(如果所有关联值都遵循Hashable) - 手动实现
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 相同)