12-7.【OC】【Swift+OC 混编】OC 能访问 Swift enum / struct 吗?

2 阅读2分钟

简单直接的回答是:不能直接访问,但可以通过“桥接”变通。

Objective-C 的底层架构(Runtime)是基于 C 语言指针和固定内存布局的,而 Swift 的 structenum(尤其是带关联值的)是现代值类型,其内存布局和管理方式与 OC 完全不兼容。


1. 为什么不能直接访问?(底层限制)

  • 内存布局不透明:Swift struct 的大小在编译期决定,且不具备 NSObject 的头部信息(isa 指针)。OC 无法通过指针偏移量来安全地读取 Swift 结构体。
  • 缺少 Runtime 元数据:OC 的方法调用依赖 objc_msgSend,它要求对象必须是类(Class)。structenum 并不存在于 OC 的类列表中。
  • 关联值的复杂性:Swift 的 enum 可以携带任意类型的关联值,这在底层是“带标签的联合体”(Tagged Union),OC 没有任何语法能够表达这种结构。

2. 情况 A:Swift Enum(枚举)

只有一种情况可以访问:

如果你的 Swift 枚举是整型原始值(Integer Raw Value) ,且标记了 @objc,它会映射为 OC 的 NS_ENUM

Swift

@objc enum UserRole: Int {
    case admin, member, guest
}
  • 映射结果:在 -Swift.h 中,它会被翻译为 typedef NS_ENUM(NSInteger, UserRole) { ... };

无法访问的情况:

  • 带关联值case success(String) 这种无法映射。
  • 字符串/浮点原始值enum Error: String 无法直接映射。

3. 情况 B:Swift Struct(结构体)

完全无法直接访问。 即使你加了 @objc,编译器也会报错。

变通方案:包装器(Wrapper)

如果你必须在 OC 中使用 Swift 的 struct,最标准的做法是将其包装在一个继承自 NSObject 的类中。

Swift

struct User {
    var name: String
    var age: Int
}

@objc class UserWrapper: NSObject {
    private var user: User
    
    @objc var name: String { return user.name }
    @objc var age: Int { return user.age }
    
    init(user: User) {
        self.user = user
    }
}

4. 特殊桥接:C 语言结构体

如果你定义的不是 Swift struct 而是 C 语言结构体(在 Bridging-Header 中定义或在 Swift 中按 C 语法定义),那么两端都可以访问。但这种结构体无法拥有 Swift struct 的方法、计算属性等高级特性。


5. 总结对照表

类型是否支持 OC 访问限制条件
Int 型 Enum支持必须加 @objc,必须是 Int 原始值。
String 型 Enum不支持OC 不支持字符串枚举,需用类常量替代。
带关联值 Enum不支持OC 无法理解内存布局。
Swift Struct不支持必须通过 Class 包装。
C Struct支持只能存储简单数据,无 Swift 逻辑。

💡 架构建议

在混编项目中,如果一个模型(Model)需要在两端频繁传递,建议将其定义为继承自 NSObjectClass。虽然会损失一些值类型的性能优势,但能极大地降低沟通成本和桥接代码的复杂性。