Swift 与 OC 面试题

24 阅读4分钟

Swift 实现单例的原理,和OC有什么区别?

在OC 中,我们常常这样写单例:

+ (instancetype)sharedInstance {
    static MyClass *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
原理分析:
  1. dispatch_once 保证只执行一次

    • dispatch_once 是 GCD 的 API,用底层的原子操作和内存屏障机制,保证线程安全地执行初始化代码一次。
    • 即使多个线程同时调用 sharedInstance,也只有一个线程能进入 block。
  2. 静态局部变量

    • static 修饰的 instanceonceToken 只初始化一次,生命周期贯穿整个程序运行期。
  3. 懒加载 + 保证线程安全

    • 第一次访问时创建对象,之后直接返回同一实例。
在Swift 中,单例更加简洁:
class MyManager {
    static let shared = MyManager()
    private init() {}
}
原理分析:
  1. static let 本身是线程安全的

    • Swift 的 static let 在底层由 lazy + dispatch_once 实现。
    • 即在第一次访问时初始化,且保证只初始化一次。
    • 编译器自动帮你处理线程同步逻辑,不需要显式使用 dispatch_once
  2. 编译器层级的单例保证

    • Swift 的 static 存储属性会在访问时由编译器插入类似 pthread_once 的一次性初始化逻辑。
    • 内部使用 _swift_once_dispatch_once 来确保线程安全。
  3. 类型安全

    • Swift 的单例是类型安全的,shared 明确为 MyManager 类型;
    • Objective-C 的 id 和动态派发机制,使得类型在运行时才检查。

Swift 单例利用编译器生成的静态属性初始化逻辑,天然支持线程安全的“懒加载”;而 Objective-C 依赖 GCD 的 dispatch_once 来保证初始化的唯一性和线程安全性,属于运行时层面的控制。

Swift 的懒加载和 OC 的区别

OC 懒加载
@interface Person : NSObject
@property (nonatomic, strong) NSMutableArray *friends;
@end

@implementation Person
- (NSMutableArray *)friends {
    if (!_friends) {
        _friends = [NSMutableArray array];
    }
    return _friends;
}
@end
swift 懒加载
class Person {
    lazy var friends: [String] = {
        return [String]()
    }()
}

区别
语言懒加载写法含义
Objective-C在 getter 方法中判断 _ivar 是否为空,再初始化手动控制懒加载逻辑
Swiftlazy var 修饰属性编译器自动生成延迟初始化逻辑

Objective-C 的懒加载 是“手动延迟初始化”,依靠 getter 判断 _ivar 是否为 nil;
Swift 的懒加载 是“语言级延迟初始化”,由编译器自动生成包装逻辑和初始化闭包,
前者是设计模式级特性,后者是语言语法级特性

Swift 的闭包和 OC 的闭包有什么区别?

特性Swift 闭包OC Block
层级语言级对象/Runtime 级
内存优化编译器处理,逃逸闭包堆,非逃逸闭包栈默认栈 Block,需要 copy 到堆
类型安全强类型,编译期检查弱类型,运行期检查(id)
捕获变量自动捕获,按值/按引用按值/按引用(__block)
生命周期管理ARC + 编译器控制ARC + runtime 控制
语法{ params in ... }^{ ... },语法糖生成对象

都是利用LLVM 开发的语言,Swift比 OC 利用了更多编译期特性,为什么呢?

语言设计的目标不同
  • Objective-C

    • 设计年代较早(1980s~1990s),语言特性以 动态运行时 为中心
    • 大量依赖 runtime(如 objc_msgSendisamethod swizzling
    • 编译器在编译期做的优化有限,大部分类型检查、方法调用都是 运行时解析
  • Swift

    • 现代语言设计(2014 年推出),目标是 安全、高性能、静态类型
    • 大量使用 编译期类型检查、泛型、值类型优化
    • Swift 的类型系统复杂且丰富,尽量在编译期就做类型检查、方法调用绑定、内存布局优化
类型系统差异
特性Objective-CSwift
类型检查运行时 (id, dynamic)编译期 + 泛型安全
可空性 (nullability)运行时 nil 检查编译期 Optional 类型
泛型通过 id + runtime 检查编译期泛型,零开销
协议 & 方法调度runtime 消息发送编译期可静态派发,支持动态派发

Swift 编译器可以在编译期生成更多优化代码,例如:

  • 内联函数
  • 泛型类型特化(specialization)
  • 协议 witness table 静态绑定
LLVM 的利用程度
  • Objective-C 编译器生成的 LLVM IR 中,很多方法调用是通过 objc_msgSend运行时才解析

  • Swift 编译器生成的 LLVM IR 中:

    • 值类型(struct、enum)可以 直接在栈上操作
    • 函数调用可以 静态绑定
    • 泛型函数可以 专门化生成,避免运行时开销

总结:Swift 利用更多编译期特性,是因为语言设计偏向 静态类型 + 安全 + 高性能,而 Objective-C 偏向 动态性 + 兼容性 + 灵活性

内存管理与 ARC
  • Objective-C 的 ARC 主要依赖 编译器插入 retain/release,仍有很多 runtime 支持
  • Swift 的 ARC 更紧密结合编译期类型,编译器能静态分析对象生命周期,更少依赖 runtime