乐闻世界logo
搜索文章和话题

面试题手册

Swift 中的可选类型是什么?如何正确使用可选类型?

Swift 中的可选类型是什么?如何正确使用可选类型?什么是可选绑定、强制解包和隐式解包可选类型?Swift 中的可选类型是一种处理值可能缺失的安全机制,它表示一个变量要么有值,要么为 nil。可选类型定义:使用 ? 声明可选类型,如 var name: String?可选类型实际上是一个枚举:enum Optional<Wrapped> { case none; case some(Wrapped) }nil 表示没有值,只能用于可选类型可选绑定:使用 if let 或 guard let 安全地解包可选值if let:在条件作用域内使用解包后的值guard let:在函数或方法中提前退出,解包后的值在后续代码中可用示例: if let unwrappedName = optionalName { print(unwrappedName) } func processName(_ name: String?) { guard let unwrappedName = name else { return } print(unwrappedName) }强制解包:使用 ! 强制解包可选值如果可选值为 nil,会触发运行时错误只在确定可选值不为 nil 时使用示例:let name = optionalName!隐式解包可选类型:使用 ! 声明,如 var name: String!声明后可以像非可选类型一样使用但本质仍是可选类型,为 nil 时会崩溃主要用于初始化后不会为 nil 的情况,如 IBOutlet最佳实践:优先使用可选绑定而非强制解包使用 ?? 运算符提供默认值使用可选链 ?. 安全调用方法和属性避免过度使用隐式解包可选类型使用 guard let 提前处理 nil 情况
阅读 0·2月21日 15:07

Swift 中的属性观察器是什么?如何使用 willSet 和 didSet?

Swift 中的属性观察器是什么?如何使用 willSet 和 didSet?Swift 中的属性观察器用于监控和响应属性值的变化。当属性值被设置时,属性观察器会被触发,允许你在值改变前后执行自定义代码。属性观察器的类型:willSet:在新值存储之前调用可以访问新值(通过默认参数名 newValue)可以在设置新值之前执行验证或准备工作示例: swift class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("About to set totalSteps to \(newTotalSteps)") } } }didSet:在新值存储之后调用可以访问旧值(通过默认参数名 oldValue)可以在值改变后执行更新或通知操作示例: swift class StepCounter { var totalSteps: Int = 0 { didSet { print("Added \(totalSteps - oldValue) steps") } } }完整示例:class TemperatureMonitor { var temperature: Double { willSet { print("Temperature will change from \(temperature) to \(newValue)") if newValue > 100 { print("Warning: High temperature!") } } didSet { print("Temperature changed from \(oldValue) to \(temperature)") if temperature != oldValue { notifyTemperatureChange() } } } func notifyTemperatureChange() { print("Notifying temperature change...") }}let monitor = TemperatureMonitor()monitor.temperature = 25monitor.temperature = 105属性观察器的使用场景:数据验证: class Person { var age: Int { didSet { if age < 0 { age = 0 } if age > 150 { age = 150 } } } }UI 更新: class ViewModel { var isLoading: Bool = false { didSet { updateLoadingIndicator() } } func updateLoadingIndicator() { // 更新 UI 指示器 } }缓存失效: class DataCache { var data: [String: Any] = [:] { didSet { invalidateCache() } } func invalidateCache() { // 清除缓存 } }日志记录: class Logger { var logLevel: LogLevel = .info { didSet { print("Log level changed from \(oldValue) to \(logLevel)") } } }注意事项:属性观察器不能用于延迟属性(lazy)属性观察器不能用于常量属性(let)在初始化器中设置属性值不会触发属性观察器在 willSet 中修改属性值不会再次触发 willSet在 didSet 中修改属性值会再次触发属性观察器如果属性有默认值,初始化时不会触发属性观察器最佳实践:使用 willSet 进行值验证和准备使用 didSet 执行副作用和更新避免在属性观察器中执行耗时操作注意避免无限循环(在 didSet 中修改属性)使用有意义的参数名提高代码可读性
阅读 0·2月21日 15:07

Swift 中的属性包装器是什么?如何使用 @propertyWrapper?

Swift 中的属性包装器是什么?如何使用 @propertyWrapper?Swift 中的属性包装器是一种在属性设置和获取时添加额外逻辑的机制。属性包装器可以复用属性管理逻辑,减少代码重复。基本属性包装器:@propertyWrapperstruct TwelveOrLess { private var number: Int init() { self.number = 0 } var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } }}struct SmallRectangle { @TwelveOrLess var height: Int @TwelveOrLess var width: Int}var rectangle = SmallRectangle()rectangle.height = 10rectangle.width = 20print(rectangle.width) // 12带参数的属性包装器:@propertyWrapperstruct SmallNumber { private var maximum: Int private var number: Int init(wrappedValue: Int, maximum: Int) { self.maximum = maximum self.number = wrappedValue } var wrappedValue: Int { get { return number } set { number = min(newValue, maximum) } }}struct ZeroRectangle { @SmallNumber(wrappedValue: 0, maximum: 5) var height: Int @SmallNumber(wrappedValue: 0, maximum: 10) var width: Int}投射值:@propertyWrapperstruct SmallNumber { private var number: Int init(wrappedValue: Int) { self.number = wrappedValue } var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } var projectedValue: Bool { return number > 12 }}struct SomeStructure { @SmallNumber var someNumber: Int}var someStructure = SomeStructure()someStructure.someNumber = 4print(someStructure.$someNumber) // falsesomeStructure.someNumber = 14print(someStructure.$someNumber) // true常见的属性包装器用例:线程安全: @propertyWrapper struct ThreadSafe { private var value: T private let lock = NSLock() init(wrappedValue: T) { self.value = wrappedValue } var wrappedValue: T { get { lock.lock() defer { lock.unlock() } return value } set { lock.lock() defer { lock.unlock() } value = newValue } } }延迟加载: @propertyWrapper struct Lazy { private var value: T? var wrappedValue: T { mutating get { if let value = value { return value } let initialValue = wrappedValueFactory() value = initialValue return initialValue } } let wrappedValueFactory: () -> T init(wrappedValue: @autoclosure @escaping () -> T) { self.wrappedValueFactory = wrappedValue } }用户默认值: @propertyWrapper struct UserDefault { let key: String let defaultValue: T var wrappedValue: T { get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } }属性包装器的最佳实践:使用属性包装器封装重复的属性逻辑使用投射值提供额外的功能为属性包装器提供合理的默认值注意属性包装器的性能影响在适当的地方使用属性包装器
阅读 0·2月21日 15:06

Swift 中的协议是什么?如何使用协议?

Swift 中的协议是什么?如何使用协议?什么是协议扩展和面向协议编程?Swift 中的协议定义了一组方法和属性的蓝图,类、结构体和枚举可以遵循协议来实现这些要求。协议是 Swift 中实现多态和代码复用的重要机制。协议的基本概念:协议定义了实现者必须提供的属性、方法和其他要求协议本身不实现功能,只是定义接口类型可以遵循多个协议协议可以继承其他协议协议可以作为类型使用定义协议:protocol FullyNamed { var fullName: String { get } func sayHello()}protocol Identifiable { var id: String { get set } static func generateID() -> String}遵循协议:struct Person: FullyNamed { var fullName: String func sayHello() { print("Hello, my name is \(fullName)") }}class Product: Identifiable { var id: String var name: String init(name: String) { self.id = Self.generateID() self.name = name } static func generateID() -> String { return UUID().uuidString }}协议作为类型:func greet(_ person: FullyNamed) { print("Hello, \(person.fullName)") person.sayHello()}let john = Person(fullName: "John Doe")greet(john)协议扩展:为协议提供默认实现可以添加协议未要求的方法和属性可以使用 where 子句进行条件扩展示例: extension FullyNamed { func sayHello() { print("Hello, \(fullName)") } func introduce() { print("I am \(fullName)") } } extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = first else { return true } return all { $0 == first } } }面向协议编程:使用协议定义抽象接口通过协议扩展提供默认行为使用协议组合实现灵活的功能避免继承的局限性示例: protocol Renderable { func render() } protocol Animatable { func animate() } extension Renderable { func render() { print("Rendering...") } } extension Animatable { func animate() { print("Animating...") } } struct Button: Renderable, Animatable { // 自动获得默认实现 }协议的继承:protocol TextRepresentable { var textualDescription: String { get }}protocol PrettyTextRepresentable: TextRepresentable { var prettyTextualDescription: String { get }}协议组合: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)!")}最佳实践:使用协议定义清晰的接口通过协议扩展提供默认实现使用协议组合替代多重继承优先使用协议而非继承合理使用关联类型提高灵活性
阅读 0·2月21日 15:06

Swift 中的下标是什么?如何自定义下标?

Swift 中的下标是什么?如何自定义下标?下标可以接受多个参数吗?Swift 中的下标是一种快捷方式,用于访问集合、列表或序列中元素的成员。通过下标,你可以使用方括号语法 [] 来访问和设置值,而不需要调用单独的方法。基本下标语法:struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index }}let threeTimesTable = TimesTable(multiplier: 3)print(threeTimesTable[6]) // 18下标的特点:使用 subscript 关键字定义可以接受一个或多个参数可以是读写、只读或只写可以有可变参数可以有默认参数值可以抛出错误只读下标:struct Matrix { let rows: Int let columns: Int var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns self.grid = Array(repeating: 0.0, count: rows * columns) } subscript(row: Int, column: Int) -> Double { get { return grid[(row * columns) + column] } }}读写下标:struct Matrix { let rows: Int let columns: Int var grid: [Double] subscript(row: Int, column: Int) -> Double { get { return grid[(row * columns) + column] } set { grid[(row * columns) + column] = newValue } }}var matrix = Matrix(rows: 2, columns: 2)matrix[0, 0] = 1.0matrix[0, 1] = 2.0print(matrix[0, 0]) // 1.0接受多个参数的下标:struct Chessboard { var board: [[String]] = Array(repeating: Array(repeating: "", count: 8), count: 8) subscript(row: Int, column: Int) -> String { get { return board[row][column] } set { board[row][column] = newValue } } subscript(position: String) -> String { get { let column = position[position.startIndex] let row = Int(position.dropFirst())! return board[row][columnToIndex(column)] } set { let column = position[position.startIndex] let row = Int(position.dropFirst())! board[row][columnToIndex(column)] = newValue } } private func columnToIndex(_ column: Character) -> Int { return Int(column.asciiValue! - Character("a").asciiValue!) }}var chessboard = Chessboard()chessboard["a1"] = "Rook"chessboard[0, 0] = "Queen"类型下标:enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune static subscript(n: Int) -> Planet? { return Planet(rawValue: n) }}let mars = Planet[4]带默认参数的下标:struct DefaultSubscript { var values: [Int] = [] subscript(index: Int, default defaultValue: Int = 0) -> Int { get { return index < values.count ? values[index] : defaultValue } set { if index < values.count { values[index] = newValue } else { values.append(newValue) } } }}最佳实践:使用下标提供简洁的访问语法合理设计下标参数,提高可读性为多维数据提供多参数下标注意下标的性能影响在复杂场景考虑使用方法替代下标
阅读 0·2月21日 15:06

Swift 中的访问控制有哪些级别?open、public、internal、fileprivate、private 有什么区别?

Swift 中的访问控制有哪些级别?open、public、internal、fileprivate、private 有什么区别?Swift 提供了五个访问控制级别,用于控制代码实体(类型、属性、方法等)的可见性和可访问性。这些访问级别帮助开发者实现封装和信息隐藏。访问控制级别:open:最高级别的访问权限可以在定义模块的任何地方访问可以在其他模块中访问和继承可以在其他模块中被重写主要用于类和类成员示例: swift open class OpenClass { open var openProperty: Int = 0 open func openMethod() {} }public:可以在定义模块的任何地方访问可以在其他模块中访问但不能在其他模块中继承或重写适用于不希望被继承或重写的公共 API示例: swift public class PublicClass { public var publicProperty: Int = 0 public func publicMethod() {} }internal(默认):可以在定义模块的任何地方访问不能在其他模块中访问这是默认的访问级别适用于模块内部的实现细节示例: swift class InternalClass { // 默认为 internal var internalProperty: Int = 0 func internalMethod() {} }fileprivate:只能在定义的文件中访问不能在同一个模块的其他文件中访问适用于文件内部的辅助类型和函数示例: swift class FilePrivateClass { fileprivate var filePrivateProperty: Int = 0 fileprivate func filePrivateMethod() {} }private:最低级别的访问权限只能在定义的作用域中访问不能在同一个文件的其他类型中访问适用于类型内部的实现细节示例: class PrivateClass { private var privateProperty: Int = 0 private func privateMethod() {} func publicMethod() { privateMethod() // 可以访问 } }访问控制规则:实体不能比其类型更公开: public class PublicClass { private var property: Int = 0 // 正确 // public var property: Int = 0 // 错误 }元组类型的访问级别是所有成员中最严格的: private var tuple: (Int, String) = (1, "hello") // private函数的访问级别是所有参数和返回值类型中最严格的: private func process(_ data: InternalType) -> PublicType { // 函数访问级别为 private }嵌套类型的访问级别: public class OuterClass { private class InnerClass { // InnerClass 的访问级别为 private } }最佳实践:默认使用 internal,只在需要时使用其他级别使用 private 限制实现细节使用 fileprivate 处理文件内部的辅助功能使用 public 定义公共 API使用 open 定义需要被继承和重写的公共 API遵循最小权限原则,只暴露必要的接口
阅读 0·2月21日 15:06

Swift 中的集合类型有哪些?Array、Set 和 Dictionary 有什么区别和适用场景?

Swift 中的集合类型有哪些?Array、Set 和 Dictionary 有什么区别和适用场景?Swift 提供了三种主要的集合类型:Array(数组)、Set(集合)和 Dictionary(字典)。每种集合类型都有其特定的用途和特点。Array(数组):有序集合可以包含重复元素可以通过索引访问元素示例: var numbers = [1, 2, 3, 4, 5] numbers.append(6) numbers[0] = 10 let first = numbers.first // Optional(10) let count = numbers.count // 6Array 的常用操作:var array = [1, 2, 3]// 添加元素array.append(4)array.insert(0, at: 0)// 删除元素array.remove(at: 0)array.removeLast()// 查找元素let index = array.firstIndex(of: 2)// 排序array.sort()let sorted = array.sorted()// 遍历for (index, element) in array.enumerated() { print("\(index): \(element)")}Set(集合):无序集合不能包含重复元素元素必须是可哈希的示例: var set: Set<Int> = [1, 2, 3, 4, 5] set.insert(6) set.contains(3) // true let count = set.count // 6Set 的常用操作:var set1: Set<Int> = [1, 2, 3, 4, 5]var set2: Set<Int> = [4, 5, 6, 7, 8]// 集合操作let union = set1.union(set2) // {1, 2, 3, 4, 5, 6, 7, 8}let intersection = set1.intersection(set2) // {4, 5}let difference = set1.subtracting(set2) // {1, 2, 3}let symmetricDifference = set1.symmetricDifference(set2) // {1, 2, 3, 6, 7, 8}// 检查关系set1.isSubset(of: set2) // falseset1.isSuperset(of: set2) // falseset1.isDisjoint(with: set2) // false// 添加和删除set1.insert(6)set1.remove(1)Dictionary(字典):无序键值对集合键必须是唯一的键必须是可哈希的示例: var dict = ["name": "John", "age": "30"] dict["email"] = "john@example.com" dict["name"] = "Jane" let name = dict["name"] // Optional("Jane") let count = dict.count // 3Dictionary 的常用操作:var dict = ["name": "John", "age": "30"]// 添加和更新dict["email"] = "john@example.com"dict.updateValue("Jane", forKey: "name")// 删除dict.removeValue(forKey: "age")// 访问if let name = dict["name"] { print(name)}// 遍历for (key, value) in dict { print("\(key): \(value)")}// 获取所有键和值let keys = Array(dict.keys)let values = Array(dict.values)Array、Set 和 Dictionary 的区别:有序性:Array:有序Set:无序Dictionary:无序(键值对)重复元素:Array:可以包含重复元素Set:不能包含重复元素Dictionary:键必须唯一,值可以重复访问方式:Array:通过索引访问Set:通过成员检查访问Dictionary:通过键访问性能:Array:插入和删除 O(n),访问 O(1)Set:插入、删除、查找 O(1)Dictionary:插入、删除、查找 O(1)适用场景:使用 Array 的场景:需要保持元素顺序允许重复元素需要通过索引访问元素示例:待办事项列表、历史记录使用 Set 的场景:需要确保元素唯一性需要快速查找元素是否存在需要集合操作(并集、交集等)示例:标签、用户 ID 集合使用 Dictionary 的场景:需要通过键快速查找值需要存储键值对数据键是唯一的示例:用户信息、配置选项最佳实践:根据需求选择合适的集合类型使用 Array 保持顺序使用 Set 确保唯一性使用 Dictionary 存储键值对注意集合的性能特性
阅读 0·2月21日 15:05

Swift 中的结构体和类有什么区别?何时选择结构体?

Swift 中的结构体和类有什么区别?在什么情况下应该选择结构体而不是类?Swift 中的结构体和类都是用于定义自定义数据类型的构造,但它们在内存管理、赋值行为和使用场景上有重要区别。主要区别:值类型 vs 引用类型:结构体是值类型,赋值时创建副本类是引用类型,赋值时传递引用示例: struct Point { var x: Int var y: Int } class Circle { var center: Point var radius: Int init(center: Point, radius: Int) { self.center = center self.radius = radius } } var point1 = Point(x: 0, y: 0) var point2 = point1 point2.x = 10 print(point1.x) // 0 (point1 不受影响) var circle1 = Circle(center: Point(x: 0, y: 0), radius: 5) var circle2 = circle1 circle2.radius = 10 print(circle1.radius) // 10 (circle1 受影响)继承:类支持继承,可以继承其他类的属性和方法结构体不支持继承示例: class Vehicle { var speed: Int = 0 func accelerate() { speed += 10 } } class Car: Vehicle { var brand: String init(brand: String) { self.brand = brand } }类型转换:类支持类型检查和转换(is、as)结构体不支持类型转换示例: swift let vehicle: Vehicle = Car(brand: "Toyota") if let car = vehicle as? Car { print(car.brand) }引用计数:类使用 ARC(自动引用计数)管理内存结构体不需要引用计数,直接在栈上分配类可能导致循环引用,需要使用 weak 或 unowned析构器:类可以定义析构器(deinit)结构体不能定义析构器示例: class FileHandler { let fileHandle: FileHandle init(path: String) { self.fileHandle = FileHandle(forReadingAtPath: path)! } deinit { fileHandle.closeFile() } }选择结构体的场景:数据模型主要是值类型数据需要独立存在,不希望被意外修改数据相对较小,复制成本较低不需要继承需要线程安全的操作需要比较值相等性选择类的场景:需要继承和多态需要共享状态数据较大,复制成本高需要控制生命周期和内存管理需要使用标识符比较需要使用析构器最佳实践:优先使用结构体,只有在需要引用类型特性时才使用类使用结构体表示简单的数据模型使用类表示具有身份和行为对象在需要继承时使用类注意类可能导致的循环引用问题
阅读 0·2月21日 15:05

Swift 中的高阶函数有哪些?如何使用 map、filter、reduce?

Swift 中的高阶函数有哪些?如何使用 map、filter、reduce 等函数式编程特性?Swift 提供了丰富的高阶函数,支持函数式编程范式。这些函数可以接受其他函数作为参数或返回函数,使代码更加简洁和声明式。主要的高阶函数:map:对集合中的每个元素应用转换函数返回包含转换后元素的新数组保持原始集合不变示例: let numbers = [1, 2, 3, 4, 5] let doubled = numbers.map { $0 * 2 } // [2, 4, 6, 8, 10] let names = ["alice", "bob", "charlie"] let capitalized = names.map { $0.capitalized } // ["Alice", "Bob", "Charlie"]filter:根据条件筛选集合中的元素返回包含满足条件元素的新数组示例: let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] let evenNumbers = numbers.filter { $0 % 2 == 0 } // [2, 4, 6, 8, 10] let words = ["apple", "banana", "pear", "grape"] let longWords = words.filter { $0.count > 4 } // ["apple", "banana"]reduce:将集合中的元素组合成单个值接受初始值和组合函数示例: let numbers = [1, 2, 3, 4, 5] let sum = numbers.reduce(0) { $0 + $1 } // 15 let product = numbers.reduce(1) { $0 * $1 } // 120 let names = ["Alice", "Bob", "Charlie"] let allNames = names.reduce("") { $0 + $1 + " " } // "Alice Bob Charlie "flatMap:对集合中的每个元素应用转换函数,并展平结果处理嵌套数组或可选值示例: let numbers = [[1, 2], [3, 4], [5, 6]] let flattened = numbers.flatMap { $0 } // [1, 2, 3, 4, 5, 6] let optionalNumbers: [Int?] = [1, nil, 3, nil, 5] let nonNilNumbers = optionalNumbers.flatMap { $0 } // [1, 3, 5]compactMap:类似于 map,但过滤掉 nil 值专门用于处理可选值示例: swift let strings = ["1", "2", "three", "4", "five"] let numbers = strings.compactMap { Int($0) } // [1, 2, 4]forEach:对集合中的每个元素执行操作不返回新值示例: swift let numbers = [1, 2, 3, 4, 5] numbers.forEach { print($0) }sorted:对集合进行排序可以自定义排序规则示例: let numbers = [5, 2, 8, 1, 9] let ascending = numbers.sorted { $0 < $1 } // [1, 2, 5, 8, 9] let descending = numbers.sorted { $0 > $1 } // [9, 8, 5, 2, 1]链式调用:let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]let result = numbers .filter { $0 % 2 == 0 } .map { $0 * $0 } .reduce(0) { $0 + $1 }// 220 (2² + 4² + 6² + 8² + 10²)最佳实践:优先使用高阶函数而非 for 循环合理使用链式调用提高代码可读性注意性能影响,避免过度嵌套使用有意义的变量名提高代码清晰度在复杂逻辑中使用传统循环提高可维护性
阅读 0·2月21日 15:04