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

面试题手册

Swift 中的闭包是什么?什么是逃逸闭包和非逃逸闭包?

Swift 中的闭包是什么?闭包的捕获列表是什么?什么是逃逸闭包和非逃逸闭包?Swift 中的闭包是自包含的函数代码块,可以在代码中被传递和使用。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。闭包的基本概念:闭包是引用类型可以作为参数传递给函数可以作为函数的返回值可以存储在变量或常量中三种形式:全局函数、嵌套函数、闭包表达式闭包的语法:// 完整形式let greeting = { (name: String) -> String in return "Hello, \(name)"}// 简化形式let greeting = { name in "Hello, \(name)" }// 最简形式(使用参数缩写)let greeting: (String) -> String = { "Hello, \($0)" }捕获列表:用于显式声明闭包要捕获的变量使用 [weak self] 或 [unowned self] 避免循环引用使用 [weak var = weakVar] 捕获弱引用使用 [unowned var = var] 捕获无主引用示例: let closure = { [weak self] in self?.doSomething() }逃逸闭包:在函数返回后仍会被调用的闭包使用 @escaping 标记必须显式引用 self常用于异步操作、回调、网络请求示例: func request(completion: @escaping (Result) -> Void) { DispatchQueue.global().async { let result = fetchData() completion(result) } }非逃逸闭包:在函数返回前会被调用的闭包Swift 3.0+ 默认为非逃逸可以隐式引用 self性能更好,编译器可以优化示例: func process(_ closure: (Int) -> Void) { closure(42) }最佳实践:使用捕获列表避免循环引用优先使用非逃逸闭包合理使用尾随闭包语法使用 weak 或 unowned 处理 self 引用考虑闭包的内存开销
阅读 0·2月21日 15:10

Swift 中的字符串处理有哪些常用方法?如何进行字符串的拼接、截取、替换和查找?

Swift 中的字符串处理有哪些常用方法?如何进行字符串的拼接、截取、替换和查找?Swift 提供了丰富的字符串处理方法,包括拼接、截取、替换、查找等操作。Swift 的字符串类型是值类型,支持 Unicode 字符。字符串拼接:// 使用 + 运算符let str1 = "Hello"let str2 = "World"let combined = str1 + ", " + str2 // "Hello, World"// 使用 += 运算符var greeting = "Hello"greeting += ", World" // "Hello, World"// 使用字符串插值let name = "John"let age = 30let message = "My name is \(name) and I'm \(age) years old"// 使用 append 方法var text = "Hello"text.append(", World") // "Hello, World"字符串截取:let str = "Hello, World"// 使用 prefix 和 suffixlet prefix = str.prefix(5) // "Hello"let suffix = str.suffix(6) // "World"// 使用 dropFirst 和 dropLastlet withoutFirst = str.dropFirst() // "ello, World"let withoutLast = str.dropLast() // "Hello, Worl"// 使用索引let startIndex = str.startIndexlet endIndex = str.index(str.startIndex, offsetBy: 5)let substring = str[startIndex..<endIndex] // "Hello"// 使用 rangelet range = str.range(of: "World")!let substring2 = str[range] // "World"字符串替换:let str = "Hello, World"// 替换单个字符let replaced = str.replacingOccurrences(of: "World", with: "Swift")// "Hello, Swift"// 使用闭包替换let replaced2 = str.replacingOccurrences(of: "o", with: "O")// "HellO, WOrld"// 使用闭包进行复杂替换let replaced3 = str.replacingOccurrences(of: "[aeiou]", with: "", options: .regularExpression)// "Hll, Wrld"字符串查找:let str = "Hello, World"// 检查是否包含let containsHello = str.contains("Hello") // truelet containsSwift = str.contains("Swift") // false// 查找子字符串if let range = str.range(of: "World") { print("Found at range: \(range)")}// 查找前缀和后缀let hasPrefix = str.hasPrefix("Hello") // truelet hasSuffix = str.hasSuffix("World") // true// 查找字符let firstIndex = str.firstIndex(of: ",") // Optional(String.Index)字符串分割:let str = "apple,banana,orange"// 按字符分割let components = str.components(separatedBy: ",")// ["apple", "banana", "orange"]// 按字符集分割let whitespace = "Hello World"let words = whitespace.components(separatedBy: .whitespaces)// ["Hello", "World"]字符串大小写转换:let str = "Hello, World"let uppercased = str.uppercased() // "HELLO, WORLD"let lowercased = str.lowercased() // "hello, world"let capitalized = str.capitalized // "Hello, World"字符串修剪:let str = " Hello, World "let trimmed = str.trimmingCharacters(in: .whitespaces)// "Hello, World"let trimmed2 = str.trimmingCharacters(in: .whitespacesAndNewlines)// "Hello, World"字符串长度和字符:let str = "Hello, World"let count = str.count // 13let isEmpty = str.isEmpty // falselet firstChar = str.first // Optional("H")let lastChar = str.last // Optional("d")let chars = Array(str) // ["H", "e", "l", "l", "o", ",", " ", "W", "o", "r", "l", "d"]最佳实践:使用字符串插值提高可读性注意字符串索引的使用使用 contains、hasPrefix、hasSuffix 进行快速检查使用 replacingOccurrences 进行字符串替换注意 Unicode 字符的处理
阅读 0·2月21日 15:10

Swift 中的并发编程有哪些特性?如何使用 async/await 和 Actor?

Swift 中的并发编程有哪些特性?如何使用 async/await 和 Actor?Swift 5.5 引入了现代并发编程模型,包括 async/await、结构化并发、Actor 等特性,使并发编程更加安全和易用。async/await 基本用法:func fetchImage(from urlString: String) async throws -> UIImage { guard let url = URL(string: urlString) else { throw URLError(.badURL) } let (data, _) = try await URLSession.shared.data(from: url) guard let image = UIImage(data: data) else { throw URLError(.cannotDecodeRawData) } return image}Task { do { let image = try await fetchImage(from: "https://example.com/image.jpg") print("Image loaded: \(image)") } catch { print("Error: \(error)") }}async let 并发执行:func fetchUserData() async throws -> User { async let profile = fetchProfile() async let posts = fetchPosts() async let friends = fetchFriends() let (userProfile, userPosts, userFriends) = try await (profile, posts, friends) return User(profile: userProfile, posts: userPosts, friends: userFriends)}TaskGroup:func downloadImages(urls: [URL]) async throws -> [UIImage] { try await withThrowingTaskGroup(of: UIImage.self) { group in var images: [UIImage] = [] for url in urls { group.addTask { try await downloadImage(from: url) } } for try await image in group { images.append(image) } return images }}Actor:确保数据访问的线程安全防止数据竞争示例: actor Counter { private var value = 0 func increment() { value += 1 } func getValue() -> Int { return value } } let counter = Counter() await counter.increment() let count = await counter.getValue()MainActor:确保代码在主线程执行用于 UI 更新示例: @MainActor class ViewModel: ObservableObject { @Published var isLoading = false func loadData() async { isLoading = true let data = try? await fetchData() isLoading = false } }Task:创建异步任务可以取消示例: let task = Task { for i in 1...10 { print(i) try? await Task.sleep(nanoseconds: 1_000_000_000) } } // 取消任务 task.cancel()Continuation:将基于回调的 API 转换为 async/await示例: func fetchImageContinuation(from urlString: String) async throws -> UIImage { try await withCheckedThrowingContinuation { continuation in fetchImageCallback(from: urlString) { result in switch result { case .success(let image): continuation.resume(returning: image) case .failure(let error): continuation.resume(throwing: error) } } } }并发编程的最佳实践:使用 async/await 替代闭包回调使用 Actor 保护共享状态使用 MainActor 更新 UI使用 TaskGroup 处理并发任务正确处理任务取消
阅读 0·2月21日 15:10

Swift 中的枚举是什么?如何使用关联值和原始值?

Swift 中的枚举是什么?如何使用关联值和原始值?枚举支持哪些高级特性?Swift 中的枚举是一种定义一组相关值的类型,比其他语言的枚举更强大。Swift 的枚举可以包含关联值、原始值,并且可以定义方法和计算属性。基本枚举定义:enum CompassPoint { case north case south case east case west}var direction = CompassPoint.northdirection = .south关联值:每个枚举成员可以存储不同类型的关联值类似于带有附加数据的枚举示例: enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) } var productBarcode = Barcode.upc(8, 85909, 51226, 3) productBarcode = .qrCode("ABCDEFGHIJKLMNOP") switch productBarcode { case .upc(let numberSystem, let manufacturer, let product, let check): print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)") case .qrCode(let productCode): print("QR code: \(productCode)") }原始值:枚举成员可以有相同类型的预填充值使用 rawValue 访问原始值必须是字符串、字符、整数或浮点数示例: enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune } let earth = Planet(rawValue: 3) print(earth?.rawValue) // Optional(3)枚举方法:enum TrafficLight { case red, yellow, green func description() -> String { switch self { case .red: return "Stop" case .yellow: return "Caution" case .green: return "Go" } } mutating func next() { switch self { case .red: self = .green case .yellow: self = .red case .green: self = .yellow } }}计算属性:enum Rectangle { case width(height: Double) case height(width: Double) var area: Double { switch self { case .width(let height): return height * 10 case .height(let width): return width * 5 } }}递归枚举:使用 indirect 关键字标记允许枚举成员引用枚举本身示例: enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))CaseIterable 协议:enum CompassPoint: CaseIterable { case north, south, east, west}let numberOfCases = CompassPoint.allCases.countfor direction in CompassPoint.allCases { print(direction)}Comparable 协议:enum Priority: Int, Comparable { case low = 1 case medium = 2 case high = 3 static func < (lhs: Priority, rhs: Priority) -> Bool { return lhs.rawValue < rhs.rawValue }}let priority1 = Priority.highlet priority2 = Priority.lowprint(priority1 > priority2) // true枚举的高级特性:支持泛型支持协议支持扩展支持初始化器支持下标最佳实践:使用枚举表示有限的、相关的值集合使用关联值传递额外的数据使用原始值表示固定的值为枚举添加方法提高可读性使用递归枚举处理树形结构
阅读 0·2月21日 15:10

Swift 中的扩展是什么?如何使用扩展?

Swift 中的扩展是什么?如何使用扩展?扩展可以添加哪些功能?Swift 中的扩展是一种为现有的类、结构体、枚举或协议类型添加新功能的机制。扩展可以添加新的功能,但不能重写现有的功能。扩展的基本用法:extension Int { var squared: Int { return self * self } func isEven() -> Bool { return self % 2 == 0 }}let number = 5print(number.squared) // 25print(number.isEven()) // false扩展可以添加的功能:计算属性: extension Double { var km: Double { return self * 1000 } var m: Double { return self } var cm: Double { return self / 100 } var mm: Double { return self / 1000 } } let oneInch = 25.4.mm print("One inch is \(oneInch) meters")实例方法: extension String { func reversed() -> String { return String(self.reversed()) } func trimmed() -> String { return self.trimmingCharacters(in: .whitespacesAndNewlines) } } let text = "Hello World" print(text.reversed()) // dlroW olleH类型方法: extension Int { static func random(in range: Range<Int>) -> Int { return Int.random(in: range) } } let randomNum = Int.random(in: 1..<100)初始化器: extension UIColor { convenience init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let a, r, g, b: UInt64 } }下标: extension Array { subscript(safe index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } } let array = [1, 2, 3] print(array[safe: 1]) // Optional(2) print(array[safe: 10]) // nil嵌套类型: extension Int { enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } } }协议扩展:protocol TextRepresentable { var textualDescription: String { get }}extension TextRepresentable { func describe() -> String { return "Description: \(textualDescription)" }}struct Person: TextRepresentable { var textualDescription: String}let person = Person(textualDescription: "John Doe")print(person.describe())条件扩展:extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = first else { return true } return all { $0 == first } }}extension Array where Element: Numeric { func sum() -> Element { return reduce(0, +) }}扩展的限制:不能添加存储属性不能添加属性观察器不能重写现有的方法不能添加指定的初始化器不能添加反初始化器最佳实践:使用扩展组织相关功能使用协议扩展提供默认实现使用条件扩展为特定类型添加功能避免在扩展中添加过多功能保持扩展的单一职责原则
阅读 0·2月21日 15:10

Swift 中的泛型是什么?如何使用泛型?

Swift 中的泛型是什么?如何使用泛型?什么是泛型约束和关联类型?Swift 中的泛型是一种强大的特性,允许你编写灵活、可重用的代码,同时保持类型安全。泛型使你能够编写适用于多种类型的函数和类型,而不需要为每种类型重复编写代码。泛型的基本概念:泛型允许你使用占位符类型名称(如 T)来表示类型编译器会在使用时推断具体的类型提供类型安全,避免运行时类型错误减少代码重复,提高代码复用性泛型函数:// 泛型函数示例func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp}// 使用泛型函数var x = 10var y = 20swapValues(&x, &y)泛型类型:// 泛型结构体struct Stack<Element> { private var items: [Element] = [] mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element? { return items.popLast() }}// 使用泛型类型var intStack = Stack<Int>()intStack.push(1)泛型约束:限制泛型类型必须遵循特定协议使用 where 子句添加更复杂的约束常见约束类型:T: Equatable - 类型必须遵循 Equatable 协议T: Comparable - 类型必须遵循 Comparable 协议T: SomeProtocol - 类型必须遵循指定协议T: AnyObject - 类型必须是类类型// 泛型约束示例func findFirstIndex<T: Equatable>(of value: T, in array: [T]) -> Int? { for (index, item) in array.enumerated() { if item == value { return index } } return nil}// 使用 where 子句func allItemsMatch<C1: Container, C2: Container>(_ container1: C1, _ container2: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { return true}关联类型:在协议中定义的占位符类型名称使用 associatedtype 关键字声明允许协议定义灵活的类型要求在遵循协议时指定具体类型// 带有关联类型的协议protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get }}// 遵循协议并指定关联类型struct IntStack: Container { typealias Item = Int private var items: [Int] = [] mutating func append(_ item: Int) { items.append(item) } var count: Int { return items.count } subscript(i: Int) -> Int { return items[i] }}最佳实践:合理使用泛型提高代码复用性使用泛型约束确保类型安全在协议中使用关联类型提高灵活性避免过度使用泛型导致代码复杂为泛型参数使用有意义的名称
阅读 0·2月21日 15:09

Swift 中的 guard 语句是什么?如何使用 guard 语句?

Swift 中的 guard 语句是什么?如何使用 guard 语句?guard 语句有什么优势?Swift 中的 guard 语句用于提前退出当前作用域,当条件不满足时执行 else 块中的代码。guard 语句使代码更加清晰,减少了嵌套层级。guard 语句的基本语法:func processAge(_ age: Int?) { guard let age = age else { print("Age is nil") return } guard age >= 18 else { print("Age must be 18 or older") return } print("Age is valid: \(age)")}guard 语句的特点:必须包含 else 块else 块中必须使用 return、break、continue 或 throw 等控制转移语句guard 条件为真时,继续执行后面的代码guard 条件为假时,执行 else 块可以使用可选绑定guard 语句的使用场景:可选绑定: func greet(name: String?) { guard let name = name else { print("Name is nil") return } print("Hello, \(name)") }条件检查: func divide(_ a: Int, by b: Int) throws -> Int { guard b != 0 else { throw DivisionError.divisionByZero } return a / b }多个条件检查: func registerUser(name: String?, age: Int?, email: String?) { guard let name = name, !name.isEmpty else { print("Invalid name") return } guard let age = age, age >= 18 else { print("Invalid age") return } guard let email = email, email.contains("@") else { print("Invalid email") return } print("User registered: \(name), \(age), \(email)") }类型转换: func processValue(_ value: Any) { guard let intValue = value as? Int else { print("Value is not an Int") return } print("Processing integer: \(intValue)") }guard 语句的优势:减少嵌套层级: // 不使用 guard func processWithoutGuard(_ value: Int?) { if let value = value { if value > 0 { if value < 100 { print("Valid value: \(value)") } else { print("Value too large") } } else { print("Value too small") } } else { print("Value is nil") } } // 使用 guard func processWithGuard(_ value: Int?) { guard let value = value else { print("Value is nil") return } guard value > 0 else { print("Value too small") return } guard value < 100 else { print("Value too large") return } print("Valid value: \(value)") }提高代码可读性:将错误处理放在前面主要逻辑在后面,更清晰减少缩进层级提前退出:在条件不满足时立即退出避免执行不必要的代码提高代码效率强制解包:guard let 确保后续代码可以使用解包后的值不需要重复检查可选值guard 语句在循环中的使用:func findFirstValidNumber(in numbers: [Int?]) -> Int? { for number in numbers { guard let validNumber = number else { continue } return validNumber } return nil}guard 语句与 if 语句的比较:guard:条件不满足时退出,满足时继续if:条件满足时执行,不满足时跳过guard 更适合提前退出场景if 更适合条件分支场景最佳实践:使用 guard 处理前置条件使用 guard 减少嵌套层级在 guard else 块中提供清晰的错误信息避免在 guard 中执行复杂逻辑合理使用 guard 提高代码可读性
阅读 0·2月21日 15:09

Swift 中的初始化器是什么?什么是指定初始化器、便利初始化器和可失败初始化器?

Swift 中的初始化器是什么?什么是指定初始化器、便利初始化器和可失败初始化器?Swift 中的初始化器用于创建类、结构体或枚举的新实例。初始化器确保所有存储属性在使用前都有合适的初始值,并执行其他必要的设置。初始化器的基本概念:使用 init 关键字定义不需要 func 关键字可以有参数可以重载确保实例完全初始化后才能使用指定初始化器:类的主要初始化器完全初始化所有引入的属性调用父类的指定初始化器每个类至少有一个指定初始化器示例: class Vehicle { var numberOfWheels: Int var maxSpeed: Int init(numberOfWheels: Int, maxSpeed: Int) { self.numberOfWheels = numberOfWheels self.maxSpeed = maxSpeed } }便利初始化器:辅助初始化器,提供便捷的初始化方式必须调用同一个类中的其他初始化器最终必须调用指定初始化器使用 convenience 关键字标记示例: class Car: Vehicle { var brand: String init(brand: String, numberOfWheels: Int, maxSpeed: Int) { self.brand = brand super.init(numberOfWheels: numberOfWheels, maxSpeed: maxSpeed) } convenience init(brand: String) { self.init(brand: brand, numberOfWheels: 4, maxSpeed: 200) } convenience init() { self.init(brand: "Unknown") } }可失败初始化器:可能返回 nil 的初始化器使用 init? 或 init! 定义在初始化失败时返回 nil示例: struct Temperature { let celsius: Double init?(celsius: Double) { if celsius < -273.15 { return nil } self.celsius = celsius } init?(fahrenheit: Double) { let celsius = (fahrenheit - 32) * 5 / 9 if celsius < -273.15 { return nil } self.celsius = celsius } } let validTemp = Temperature(celsius: 25) let invalidTemp = Temperature(celsius: -300)初始化器规则:指定初始化器必须调用父类的指定初始化器便利初始化器必须调用同一个类中的其他初始化器在初始化完成前不能访问实例方法或属性必须先初始化当前类的属性,再调用父类初始化器两段式初始化:class Base { var value: Int init(value: Int) { // 第一阶段:初始化当前类属性 self.value = value // 第二阶段:调用父类初始化器 // 对于基类,没有父类 }}class Derived: Base { var extra: Int init(value: Int, extra: Int) { // 第一阶段:初始化当前类属性 self.extra = extra // 第二阶段:调用父类初始化器 super.init(value: value) // 现在可以访问实例方法 }}必需初始化器:使用 required 关键字标记所有子类必须实现该初始化器示例: class SomeClass { required init() { // 初始化代码 } } class SubClass: SomeClass { required init() { // 必须实现 } }最佳实践:合理使用指定初始化器和便利初始化器使用可失败初始化器处理无效输入遵循两段式初始化规则避免在初始化器中调用实例方法使用必需初始化器确保子类一致性
阅读 0·2月21日 15:07

Swift 中的 inout 参数是什么?如何使用 inout 参数?

Swift 中的 inout 参数是什么?如何使用 inout 参数?inout 参数有什么限制?Swift 中的 inout 参数允许函数直接修改传递给它的变量的值。默认情况下,Swift 的参数是常量,函数内部不能修改。使用 inout 关键字可以改变这一行为。inout 参数的基本用法:func swapValues(_ a: inout Int, _ b: inout Int) { let temp = a a = b b = temp}var x = 10var y = 20swapValues(&x, &y)print("x: \(x), y: \(y)") // x: 20, y: 10inout 参数的工作原理:函数调用时,将变量的值复制到参数中函数内部修改参数的值函数返回时,将修改后的值复制回原变量类似于引用传递,但实际是值复制使用场景:交换值: func swap<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp }修改多个值: func incrementAndDecrement(_ a: inout Int, _ b: inout Int) { a += 1 b -= 1 }累积计算: func accumulate(_ value: Int, into total: inout Int) { total += value } var sum = 0 accumulate(5, into: &sum) accumulate(10, into: &sum)inout 参数的限制:不能使用字面量或常量: func modify(_ value: inout Int) { value += 1 } // 错误:不能使用字面量 // modify(&10) // 错误:不能使用常量 // let x = 5 // modify(&x) // 正确:必须使用变量 var x = 5 modify(&x)不能传递计算属性: struct Point { var x: Int var y: Int var sum: Int { return x + y } } var point = Point(x: 3, y: 4) // 错误:不能传递计算属性 // modify(&point.sum)不能传递属性观察器: var counter: Int = 0 { didSet { print("Counter changed to \(counter)") } } // 错误:不能传递有属性观察器的属性 // modify(&counter)同一变量不能多次传递: func doubleBoth(_ a: inout Int, _ b: inout Int) { a *= 2 b *= 2 } var x = 5 // 错误:同一变量不能多次传递 // doubleBoth(&x, &x)不能在闭包中捕获 inout 参数: func process(_ value: inout Int) { // 错误:不能在闭包中捕获 inout 参数 // let closure = { value += 1 } }inout 参数与引用类型的区别:inout 参数:值类型,通过复制实现引用类型:直接传递引用示例: class ReferenceType { var value: Int init(value: Int) { self.value = value } } func modifyReference(_ obj: ReferenceType) { obj.value += 1 } func modifyValue(_ value: inout Int) { value += 1 }最佳实践:只在需要修改多个值时使用 inout 参数使用有意义的参数名提高可读性避免过度使用 inout 参数注意 inout 参数的性能影响考虑使用返回值替代 inout 参数
阅读 0·2月21日 15:07

Swift 中的 lazy 属性是什么?如何使用 lazy 属性?

Swift 中的 lazy 属性是什么?如何使用 lazy 属性?lazy 属性的初始化时机是什么?Swift 中的 lazy 属性是一种延迟初始化机制,只有在第一次访问时才会计算其初始值。这对于初始化成本较高或依赖其他属性的场景非常有用。lazy 属性的特点:使用 lazy 关键字声明必须是变量(var),不能是常量(let)必须有初始值或闭包初始化第一次访问时才进行初始化初始化后保持值不变(除非被重新赋值)线程不安全,多线程环境下需要注意基本用法:class DataImporter { var filename = "data.txt"}class DataManager { lazy var importer = DataImporter() var data = [String]()}let manager = DataManager()manager.data.append("Some data")// 此时 importer 还没有被初始化print(manager.importer.filename)// 第一次访问 importer 时才进行初始化使用闭包初始化:class ImageLoader { lazy var image: UIImage = { let url = URL(string: "https://example.com/image.jpg")! let data = try! Data(contentsOf: url) return UIImage(data: data)! }() lazy var cachedImage: UIImage = { print("Loading image...") return UIImage(named: "cached")! }()}lazy 属性的初始化时机:第一次访问属性时不是在类实例化时即使访问失败,也会尝试初始化示例: class LazyExample { lazy var expensiveValue: Int = { print("Calculating expensive value...") return Int.random(in: 1...1000) }() } let example = LazyExample() print("Instance created") // 输出: Instance created print(example.expensiveValue) // 输出: Calculating expensive value... // 输出: (随机数) print(example.expensiveValue) // 输出: (相同的随机数,不会重新计算)lazy 属性的应用场景:初始化成本高的对象: class DatabaseManager { lazy var connection: DatabaseConnection = { let conn = DatabaseConnection() conn.connect() return conn }() }依赖其他属性的对象: class ViewController { var userID: String? lazy var userProfile: UserProfile = { guard let id = userID else { return UserProfile.guest } return UserProfile.load(id: id) }() }单例模式: class Singleton { static let shared = Singleton() lazy var expensiveResource: Resource = { return Resource() }() }注意事项:lazy 属性不是线程安全的不能在结构体中使用 lazy 属性(因为结构体是值类型)lazy 属性不能是常量如果初始化闭包抛出错误,需要处理错误在多线程环境下,可能需要添加同步机制最佳实践:用于初始化成本高的属性用于依赖其他属性的属性用于可能不会被使用的属性注意线程安全问题避免在 lazy 属性中使用 self 产生循环引用
阅读 0·2月21日 15:07