Hi~
Hi~
文章目录
  1. iOS 面试基础题
    1. 一、Objective-C 基础
    2. 二、Swift 基础
    3. 三、iOS 开发基础
    4. 四、内存管理
    5. 五、多线程
    6. 六、设计模式
    7. 七、网络
    8. 八、数据库
    9. 九、框架
    10. 十、综合问题

iOS Objective-C Swift 基础

iOS 面试基础题

一、Objective-C 基础

  1. 问题: Objective-C 的动态特性有哪些?与 Swift 的区别是什么?
    答案:

    • 动态特性:
      • 运行时(Runtime):支持方法动态调用(objc_msgSend)、动态添加方法、动态交换方法(method swizzling)。
      • KVC/KVO:键值编码和键值观察,动态访问和监听属性。
      • Category:运行时扩展类功能。
    • 与 Swift 的区别:
      • Swift 是静态类型语言,编译时优化更好,性能更高。
      • Objective-C 依赖运行时,灵活但性能稍逊。
      • Swift 的类型安全更强,Optional 避免空指针问题。
  2. 问题: 解释 Objective-C 的消息传递机制。
    答案:

    • Objective-C 使用消息传递(message passing)而非直接方法调用。
    • 核心函数 objc_msgSend 将消息发送给对象,运行时查找实现。
    • 示例:
      [self performSelector:@selector(doSomething) withObject:nil];
    • 注意:
      • 若方法不存在,可能导致崩溃(unrecognized selector)。
      • 使用 respondsToSelector: 检查方法存在性。
  3. 问题: Objective-C 中的属性(@property)有哪些常见修饰符?
    答案:

    • 原子性atomic(默认,线程安全但性能低)、nonatomic(非线程安全,性能高)。
    • 内存管理strong(持有引用)、weak(弱引用)、copy(复制对象)、assign(基本类型或弱引用)。
    • 读写readonlyreadwrite(默认)。
    • 示例:
      @property (nonatomic, strong) NSString *name;
      @property (nonatomic, weak) id<Delegate> delegate;
  4. 问题: Objective-C 中 block 的定义和使用?
    答案:

    • 定义:类似 Swift 闭包,捕获上下文变量。
    • 语法
      void (^myBlock)(int) = ^(int value) {
      NSLog(@"Value: %d", value);
      };
      myBlock(42);
    • 注意:
      • block 捕获变量默认是值捕获,需用 __block 修饰变量以支持修改。
      • 避免循环引用,使用 __weak typeof(self) weakSelf = self

二、Swift 基础

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

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

    • 属性初始化:
      • 非可选属性必须在初始化时赋值,或提供默认值。
      • 常量(let)属性只能在初始化时赋值。
      • struct 自动生成成员初始化器(若无自定义 init),class 需显式定义。
    • init 函数:
      • class 的 init 需确保所有非可选属性初始化完成。
      • class 支持指定初始化器(designated)和便利初始化器(convenience),convenience 必须调用 designated。
      • struct 的 init 不需要考虑继承,但若自定义 init,自动生成的成员初始化器失效。
      • deinit 仅适用于 class,用于释放资源。
    • 示例:
      class Animal {
      var name: String
      init(name: String) { self.name = name }
      }
      struct Point {
      var x: Int, y: Int
      // 自动生成 init(x:y:)
      }
  3. 问题: class 是否可以服从 Codable 协议?如何实现?
    答案:

    • 是的,class 可以服从 Codable 协议(包括 Encodable 和 Decodable)。
    • 实现方式:
      • 确保 class 的所有存储属性都符合 Codable。
      • 若属性是自定义类型,自定义类型也需符合 Codable。
      • 示例:
        class User: Codable {
        var name: String
        var age: Int
        }
    • 注意:class 的继承需要额外处理,父类和子类都需符合 Codable,可能需自定义编码/解码逻辑。
  4. 问题: 如何利用 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
        }
        }
        }
  5. 问题: Optional 的本质是什么?
    答案:

    • Optional 是一个枚举类型,定义如下:
      enum Optional<Wrapped> {
      case none
      case some(Wrapped)
      }
    • 本质:
      • 表示一个值可能存在(some)或不存在(none)。
      • 提供安全的方式处理空值,避免运行时崩溃。
    • 使用场景:
      • 解包:if let, guard let, ??, map, flatMap
      • 链式调用:optional?.property?.method()
    • 注意:
      • 避免强制解包(!),除非确定值存在。
  6. 问题: 解释 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(服务质量)优先级。
  7. 问题: 解释 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 }
        }
  8. 问题: Swift 中的闭包、逃逸闭包和捕获列表?
    答案:

    • 闭包定义:
      • 自包含的代码块,可捕获上下文变量。
      • 语法:{ (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
        }
  9. 问题: 高阶函数(如 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
  10. 问题: Combine 框架的核心概念和使用场景?
    答案:

    • 核心概念:
      • 响应式编程框架,处理异步事件流。
      • 核心组件:Publisher、Subscriber、Operator。
    • 使用场景:
      • 网络请求、 Connor输入、状态变化。
    • 示例
      import Combine
      let publisher = [1, 2, 3].publisher
      let cancellable = publisher
      .map { $0 * 2 }
      .sink { print($0) } // 输出 2, 4, 6
    • 注意:
      • 使用 AnyCancellable 管理订阅生命周期。
  11. 问题: 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 社区更大,跨平台支持更好。
  12. 问题: ARC 内存管理的原理和常见问题?
    答案:

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

三、iOS 开发基础

  1. 问题: 什么是 UIApplication 和 UIApplicationDelegate?
    答案:

    • UIApplication:管理 iOS 应用的生命周期和全局状态(如通知、后台任务)。
    • UIApplicationDelegate:处理应用生命周期事件(如 didFinishLaunchingWithOptions、applicationDidEnterBackground)。
    • 示例:
      class AppDelegate: UIResponder, UIApplicationDelegate {
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
      return true
      }
      }
  2. 问题: 解释 UIViewController 的生命周期。
    答案:

    • viewDidLoad:视图加载完成,初始化 UI。
    • viewWillAppear:视图即将显示。
    • viewDidAppear:视图已显示。
    • viewWillDisappear:视图即将消失。
    • viewDidDisappear:视图已消失。
    • 注意:避免在 viewDidLoad 执行耗时任务,UI 更新放在 viewWillAppear 或 viewDidAppear。
  3. 问题: Auto Layout 的基本原理和使用方式?
    答案:

    • 原理:通过约束(constraints)定义视图的布局,适应不同屏幕大小。
    • 使用方式
      • Interface Builder:拖拽设置约束。
      • 代码:NSLayoutConstraint 或 Anchor API。
      • 示例(Anchor):
        view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
        view.topAnchor.constraint(equalTo: superview.topAnchor, constant: 20),
        view.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 20)
        ])

四、内存管理

  1. 问题: MRC 和 ARC 的区别?
    答案:

    • MRC(手动引用计数)
      • 开发者手动调用 retain、release、autorelease。
      • 繁琐,易出错。
    • ARC(自动引用计数)
      • 编译器自动插入内存管理代码。
      • 简化开发,但需注意循环引用。
    • 注意:MRC 已基本淘汰,iOS 5 后默认 ARC。
  2. 问题: 如何检测和解决内存泄漏?
    答案:

    • 检测
      • 使用 Instruments 的 Leaks 工具。
      • 分析对象引用计数。
    • 解决
      • 使用 weak/unowned 打破循环引用。
      • 确保 delegate 属性使用 weak。
      • 检查闭包/block 中的 self 捕获。

五、多线程

  1. 问题: iOS 中有哪些多线程技术?
    answers:

    • GCD:DispatchQueue 管理任务队列。
    • NSOperation/OperationQueue:高级封装,支持依赖和取消。
    • Thread:底层线程管理(不推荐)。
    • async/await(Swift):结构化并发,简化异步编程。
    • NSTimer/PThread:特定场景(如定时器、低级线程)。
  2. 问题: 主线程和全局线程的区别?如何确保 UI 更新在主线程?
    答案:

    • 主线程:处理 UI 和用户交互,阻塞会导致卡顿。
    • 全局线程:用于后台任务,如网络请求、数据处理。
    • 确保主线程
      • GCD:DispatchQueue.main.async { /* UI 更新 */ }
      • async/await:@MainActorTask { await MainActor.run { /* UI 更新 */ } }

六、设计模式

  1. 问题: iOS 开发中常用的设计模式有哪些?
    答案:

    • 单例:如 UIApplication.shared。
    • 委托(Delegate):如 UITableViewDelegate。
    • 观察者:如 KVO 或 NotificationCenter。
    • MVC:Model-View-Controller,iOS 默认架构。
    • MVVM:结合 Combine/RxSwift 实现响应式架构。
    • 工厂模式:创建对象(如 URLSessionConfiguration)。
    • 适配器:桥接不同接口(如第三方库适配)。
  2. 问题: 实现一个简单的单例模式(Swift 和 Objective-C)。
    答案:

    • Swift
      class Singleton {
      static let shared = Singleton()
      private init() {}
      }
    • Objective-C
      @interface Singleton : NSObject
      + (instancetype)shared;
      @end

      @implementation Singleton
      + (instancetype)shared {
      static Singleton *instance = nil;
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      instance = [[Singleton alloc] init];
      });
      return instance;
      }
      @end

七、网络

  1. 问题: iOS 中如何进行网络请求?
    答案:

    • URLSession
      • 默认方式,支持 GET、POST、上传、下载。
      • 示例(Swift):
        let url = URL(string: "https://api.example.com")!
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data, error == nil else { return }
        // 处理数据
        }
        task.resume()
    • Alamofire(第三方库):
      • 简化请求和响应处理。
      • 示例:
        import Alamofire
        AF.request("https://api.example.com").responseJSON { response in
        // 处理响应
        }
  2. 问题: 如何处理 HTTPS 和证书验证?
    答案:

    • 默认 HTTPS:URLSession 默认支持 HTTPS。
    • 自定义证书验证
      • 实现 URLSessionDelegatedidReceiveChallenge 方法。
      • 示例:
        class NetworkManager: NSObject, URLSessionDelegate {
        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // 验证服务器证书
        completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
        }
        }

八、数据库

  1. 问题: iOS 中常见的数据库技术有哪些?
    答案:

    • Core Data
      • 苹果原生框架,对象-关系映射(ORM)。
      • 适合复杂数据模型。
    • Realm
      • 第三方数据库,性能高,易用。
      • 支持实时更新。
    • SQLite
      • 轻量级数据库,需手动管理 SQL。
      • 使用 FMDB 简化操作。
    • UserDefaults
      • 轻量级键值存储,适合简单数据。
  2. 问题: Core Data 的基本使用流程?
    答案:

    • 步骤
      1. 创建数据模型(.xcdatamodeld 文件)。
      2. 配置 NSPersistentContainer。
      3. 执行 CRUD 操作。
    • 示例
      let container = NSPersistentContainer(name: "Model")
      container.loadPersistentStores { _, error in
      guard error == nil else { return }
      let context = container.viewContext
      let entity = NSEntityDescription.insertNewObject(forEntityName: "User", into: context)
      entity.setValue("John", forKey: "name")
      try? context.save()
      }

九、框架

  1. 问题: UIKit 和 SwiftUI 的区别和适用场景?
    答案:

    • UIKit
      • 基于命令式编程,成熟稳定。
      • 适合复杂应用,支持 iOS 9+。
      • 使用 UIViewController、UIView 等。
    • SwiftUI
      • 基于声明式编程,简洁现代。
      • 适合新项目,iOS 13+。
      • 使用 View 协议和 Combine。
    • 选择
      • UIKit:兼容旧设备或需要复杂自定义。
      • SwiftUI:快速开发,跨平台(iOS、macOS)。
  2. 问题: 如何在 UIKit 中集成 SwiftUI?
    答案:

    • 使用 UIHostingController 嵌入 SwiftUI 视图。
    • 示例:
      import SwiftUI
      let swiftUIView = Text("Hello, SwiftUI!")
      let hostingController = UIHostingController(rootView: swiftUIView)
      navigationController?.pushViewController(hostingController, animated: true)

十、综合问题

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

    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. 问题: 如何优化 iOS 应用的性能?
    答案:

    • 内存:避免循环引用,使用 Instruments 检测泄漏。
    • CPU:减少复杂计算,使用 GCD 或 async/await 优化耗时任务。
    • UI:异步加载图片,优化 Auto Layout,减少离屏渲染。
    • 网络:缓存响应,压缩数据,使用分页加载。
    • 启动时间:延迟非必要初始化,主线程只处理 UI。
  3. 问题: 常见崩溃原因及解决方法?
    答案:

    • 原因
      • 空指针(Objective-C)/强制解包(Swift)。
      • 数组越界。
      • 主线程阻塞。
      • 循环引用导致内存不足。
    • 解决
      • 使用 Optional 和安全解包。
      • 检查数组索引。
      • 耗时任务移至后台线程。
      • 使用 weak/unowned 打破循环引用。
支持一下
扫一扫,支持forsigner
  • 微信扫一扫
  • 支付宝扫一扫