Hi~
Hi~
文章目录
  1. Swift 面试题
    1. 一、Struct 和 Class 的区别
    2. 二、Struct 和 Class 初始化时的注意事项
    3. 三、Class 是否可以服从 Codable 协议
    4. 四、Optional 的本质
    5. 五、多线程编程
    6. 六、Swift 语法领域
    7. 七、综合问题

Swift Struct 和 Class 的区别

Swift 面试题

一、Struct 和 Class 的区别

  1. 问题: Swift 中 struct 和 class 的主要区别是什么?
    答案:

    • 值类型 vs 引用类型: struct 是值类型,复制时创建新副本;class 是引用类型,复制时共享同一实例。
    • 继承: class 支持继承,struct 不支持。
    • 内存管理: struct 通常在栈上分配,class 在堆上分配,需 ARC 管理。
    • 初始化: struct 自动生成成员初始化器,class 需手动定义。
    • 可变性: struct 的 mutating 方法需显式声明,class 方法默认可修改实例。
    • 使用场景: struct 适合简单数据模型(如 DTO),class 适合复杂对象(如视图控制器)。
  2. 问题: 在什么情况下选择 struct 而不是 class?
    答案:

    • 数据模型简单,主要是存储数据。
    • 需要值语义,避免共享状态。
    • 线程安全要求高,值类型更易管理。
    • 示例:表示点坐标的 struct Point { var x: Int, y: Int }

二、Struct 和 Class 初始化时的注意事项

  1. 问题: struct 和 class 在初始化时,属性和 init 函数有哪些注意事项?
    答案:

    • 属性初始化:
      • 非可选属性必须在初始化时赋值,或提供默认值。
      • 常量(let)属性只能在初始化时赋值。
      • struct 自动生成成员初始化器(若无自定义 init),class 需显式定义。
    • init 函数:
      • class 的 init 需确保所有非可选属性初始化完成。
      • class 支持指定初始化器(designated)和便利初始化器(convenience),convenience 必须调用 designated。
      • struct 的 init 不需要考虑继承,但若自定义 init,自动生成的成员初始化器失效。
      • deinit 仅适用于 class,用于释放资源。
    • 注意事项:
      • 确保初始化安全,避免属性未初始化就使用。
      • class 的子类必须调用 super.init。
      • struct 的 mutating 方法可能影响初始化后的状态。
  2. 问题: class 的初始化器如何处理继承?
    答案:

    • 子类必须调用父类的指定初始化器(super.init)。
    • 子类可以重写父类的指定初始化器,但必须满足父类的初始化要求。
    • 便利初始化器不能被子类直接调用,只能通过指定初始化器间接调用。
    • 示例:
      class Animal {
      var name: String
      init(name: String) { self.name = name }
      }
      class Dog: Animal {
      var breed: String
      init(name: String, breed: String) {
      self.breed = breed
      super.init(name: name)
      }
      }

三、Class 是否可以服从 Codable 协议

  1. 问题: class 是否可以服从 Codable 协议?如何实现?
    答案:

    • 是的,class 可以服从 Codable 协议(包括 Encodable 和 Decodable)。
    • 实现方式:
      • 确保 class 的所有存储属性都符合 Codable。
      • 若属性是自定义类型,自定义类型也需符合 Codable。
      • 示例:
        class User: Codable {
        var name: String
        var age: Int
        }
    • 注意:class 的继承需要额外处理,父类和子类都需符合 Codable,且可能需要自定义编码/解码逻辑。
  2. 问题: 如何利用 Codable 协议兼容接口下发的字段类型或字段名?
    答案:

    • 兼容字段名:
      • 使用 CodingKeys 枚举映射接口字段名和 Swift 属性名。
      • 示例:
        struct User: Codable {
        var userName: String
        var userAge: Int

        enum CodingKeys: String, CodingKey {
        case userName = "name"
        case userAge = "age"
        }
        }
    • 兼容字段类型:
      • 自定义 init(from:)encode(to:) 方法处理类型转换。
      • 示例(处理服务端返回的 age 可能是 String 或 Int):
        struct User: Codable {
        var name: String
        var age: Int

        enum CodingKeys: String, CodingKey {
        case name, age
        }

        init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        if let ageInt = try? container.decode(Int.self, forKey: .age) {
        age = ageInt
        } else if let ageString = try? container.decode(String.self, forKey: .age) {
        age = Int(ageString) ?? 0
        } else {
        age = 0
        }
        }
        }
    • 注意:
      • 使用 try? 优雅处理解码失败。
      • 可结合 PropertyWrapper 简化复杂类型转换。

四、Optional 的本质

  1. 问题: Optional 的本质是什么?
    答案:
    • Optional 是一个枚举类型,定义如下:
      enum Optional<Wrapped> {
      case none
      case some(Wrapped)
      }
    • 本质:
      • 表示一个值可能存在(some)或不存在(none)。
      • 提供安全的方式处理空值,避免运行时崩溃。
    • 使用场景:
      • 解包:if let, guard let, ??, map, flatMap
      • 链式调用:optional?.property?.method()
    • 注意:
      • 避免强制解包(!),除非确定值存在。
      • Optional 是 Swift 类型安全的基石。

五、多线程编程

  1. 问题: 解释 Swift 中的 GCD 多线程编程及其常见用法。
    答案:

    • GCD(Grand Central Dispatch):
      • 苹果提供的并发框架,用于管理任务队列和线程。
      • 核心概念:DispatchQueue(串行/并发)、DispatchGroup、DispatchSemaphore。
    • 常见用法:
      • 异步任务
        DispatchQueue.global().async {
        // 后台任务
        DispatchQueue.main.async {
        // 更新 UI
        }
        }
      • 串行队列
        let serialQueue = DispatchQueue(label: "com.example.serial")
        serialQueue.async { /* 任务 1 */ }
        serialQueue.async { /* 任务 2 */ } // 按顺序执行
      • DispatchGroup
        let group = DispatchGroup()
        group.enter()
        DispatchQueue.global().async {
        // 任务 1
        group.leave()
        }
        group.notify(queue: .main) {
        // 所有任务完成
        }
    • 注意:
      • 避免在主线程执行耗时任务。
      • 合理使用 QoS(服务质量)优先级。
  2. 问题: 解释 Swift 的 Task 和 async/await 多线程编程。
    答案:

    • async/await:
      • Swift 5.5 引入的结构化并发模型,简化异步编程。
      • 使用 async 标记异步函数,await 暂停等待结果。
    • Task:
      • 用于启动异步任务,运行于并发上下文。
      • 示例:
        func fetchData() async throws -> Data {
        let url = URL(string: "https://example.com")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
        }

        Task {
        do {
        let data = try await fetchData()
        print("Data fetched")
        } catch {
        print("Error: \(error)")
        }
        }
    • 并发模型:
      • Task Group:并行执行多个任务。
        let result = try await withTaskGroup(of: Int.self) { group in
        for i in 0..<5 {
        group.addTask { return i * 2 }
        }
        var results: [Int] = []
        for try await value in group {
        results.append(value)
        }
        return results
        }
      • Actor:线程安全的引用类型,避免数据竞争。
        actor Counter {
        var value = 0
        func increment() { value += 1 }
        }
    • 注意:
      • 使用 @MainActor 确保 UI 更新在主线程。
      • 避免在非异步上下文中直接调用 async 函数。
  3. 问题: GCD 和 async/await 的区别和适用场景?
    答案:

    • GCD:
      • 更底层,适合精细控制线程和队列。
      • 适用于简单后台任务或需要 DispatchGroup/Semaphore 的场景。
      • 代码复杂,容易出现回调地狱。
    • async/await:
      • 更现代化,代码简洁,适合结构化并发。
      • 适用于网络请求、文件操作等异步任务。
      • 提供更好的错误处理(try/catch)。
    • 选择:
      • 新项目优先使用 async/await。
      • 旧代码或特定场景(如复杂队列管理)使用 GCD。

六、Swift 语法领域

  1. 问题: Protocol 的作用和使用场景是什么?
    答案:

    • 作用:
      • 定义接口,规定类型必须实现的属性和方法。
      • 支持多态和解耦。
    • 使用场景:
      • 数据源/代理模式(如 UITableViewDataSource)。
      • 类型约束(如泛型)。
      • 扩展功能(如 Equatable)。
    • 示例:
      protocol Movable {
      func move(to point: CGPoint)
      }
      class Car: Movable {
      func move(to point: CGPoint) { print("Move to \(point)") }
      }
  2. 问题: 泛型的作用和常见用法?
    答案:

    • 作用:
      • 提高代码复用性和类型安全。
      • 允许函数/类型在不同类型间通用。
    • 用法:
      • 泛型函数:
        func swap<T>(_ a: inout T, _ b: inout T) {
        let temp = a
        a = b
        b = temp
        }
      • 泛型类型:
        struct Stack<Element> {
        var items: [Element] = []
        mutating func push(_ item: Element) { items.append(item) }
        }
      • 协议关联类型:
        protocol Container {
        associatedtype Item
        func add(_ item: Item)
        }
    • 注意:
      • 使用 where 约束泛型类型。
      • 泛型可提升性能(避免 Any 类型)。
  3. 问题: 闭包的定义、捕获列表和逃逸闭包?
    答案:

    • 闭包定义:
      • 自包含的代码块,可捕获上下文变量。
      • 语法:{ (parameters) -> ReturnType in statements }
    • 捕获列表:
      • 控制闭包如何捕获变量(值/引用)。
      • 示例:
        var x = 10
        let closure = { [x] in print(x) } // 捕获 x 的值
        x = 20
        closure() // 输出 10
    • 逃逸闭包:
      • 闭包在函数返回后仍可被调用,需标记 @escaping
      • 示例:
        var completion: (() -> Void)?
        func performTask(completion: @escaping () -> Void) {
        self.completion = completion
        }
  4. 问题: 高阶函数(如 map、filter、reduce)的用途和示例?
    答案:

    • 用途:
      • 函数式编程核心,用于处理集合数据。
      • 提高代码简洁性和可读性。
    • 示例:
      • map
        let numbers = [1, 2, 3]
        let doubled = numbers.map { $0 * 2 } // [2, 4, 6]
      • filter
        let evens = numbers.filter { $0 % 2 == 0 } // [2]
      • reduce
        let sum = numbers.reduce(0) { $0 + $1 } // 6
  5. 问题: Combine 框架的核心概念和使用场景?
    答案:

    • 核心概念:
      • 响应式编程框架,处理异步事件流。
      • 核心组件:Publisher、Subscriber、Operator。
    • 使用场景:
      • 网络请求、用户输入、状态变化。
    • 示例
      import Combine
      let publisher = [1, 2, 3].publisher
      let cancellable = publisher
      .map { $0 * 2 }
      .sink { print($0) } // 输出 2, 4, 6
    • 注意:
      • 使用 AnyCancellable 管理订阅生命周期。
      • Combine 适合复杂事件流处理。
  6. 问题: RxSwift 的核心概念和与 Combine 的区别?
    答案:

    • 核心概念:
      • 基于观察者模式的响应式框架。
      • 核心组件:Observable、Observer、Operator、Scheduler。
    • 示例
      import RxSwift
      let disposeBag = DisposeBag()
      Observable.just([1, 2, 3])
      .map { $0 * 2 }
      .subscribe(onNext: { print($0) })
      .disposed(by: disposeBag)
    • 与 Combine 的区别:
      • Combine 是苹果原生框架,RxSwift 是第三方库。
      • Combine 集成 Swift 语言特性(如 property wrapper)。
      • RxSwift 社区更大,跨平台支持更好。
    • 选择:
      • 新项目推荐 Combine,跨平台项目用 RxSwift。
  7. 问题: ARC 内存管理的原理和常见问题?
    答案:

    • 原理:
      • 自动引用计数(ARC)跟踪对象引用,引用计数为 0 时释放。
      • 强引用(strong)、弱引用(weak)、无主引用(unowned)。
    • 常见问题:
      • 循环引用
        • 两个对象互相强引用,导致内存泄漏。
        • 解决:使用 weak 或 unowned。
        • 示例:
          class Person {
          var dog: Dog?
          }
          class Dog {
          weak var owner: Person? // 避免循环引用
          }
      • 闭包捕获
        • 闭包捕获 self 可能导致循环引用。
        • 解决:使用捕获列表 [weak self]
    • 注意:
      • weak 引用可能为 nil,unowned 假设引用始终有效。
      • 使用 @MainActor 或 actor 避免并发内存问题。

七、综合问题

  1. 问题: 设计一个线程安全的单例类,支持 Codable,并处理网络请求。
    答案:

    import Foundation

    @MainActor
    final class NetworkManager: Codable {
    static let shared = NetworkManager()
    private let urlSession = URLSession.shared

    private init() {}

    enum CodingKeys: CodingKey {
    case dummy // 示例用,实际根据需求定义
    }

    func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    // 编码逻辑
    }

    init(from decoder: Decoder) throws {
    // 解码逻辑,单例无需恢复实例
    }

    func fetchData<T: Decodable>(from url: URL) async throws -> T {
    let (data, _) = try await urlSession.data(from: url)
    return try JSONDecoder().decode(T.self, from: data)
    }
    }
  2. 问题: 如何优化 Swift 代码性能?
    答案:

    • 使用 struct 减少堆分配。
    • 避免过度使用 Any 和 as? 转换。
    • 使用 final 关键字避免动态分发。
    • 优化集合操作,优先使用 map/filter 替代循环。
    • 使用 Instruments 分析内存和 CPU 使用。
  3. 问题: Swift 6 的主要更新有哪些?
    答案:

    • 数据竞争安全:引入严格的并发检查,强制使用 actor 或 Sendable 协议。
    • Typed throws:支持指定抛出错误类型。
    • 增强泛型:更灵活的泛型约束。
    • 注意:需调整现有代码以适配并发检查。
支持一下
扫一扫,支持forsigner
  • 微信扫一扫
  • 支付宝扫一扫