混编中的枚举

Apple|Grouping Related Objective-C Constants 中,Apple 详细列举了 NS_ENUM、NS_CLOSED_ENUM、NS_OPTIONS、NS_TYPED_ENUM、NS_TYPED_EXTENSIBLE_ENUM 等宏的使用场景,用好它们以改善在混编时在 Swift 中的编程体验。另外,Apple 建议弃用 NS_STRING_ENUM/NS_EXTENSIBLE_STRING_ENUM 而改用 NS_TYPED_ENUM/NS_TYPED_EXTENSIBLE_ENUM。

各种宏

NS_ENUM

// Declare in Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

// In Swift, the UITableViewCellStyle enumeration is imported like this:
enum UITableViewCellStyle : Int {
    case `default` = 0
    case value1 = 1
    case value2 = 2
    case subtitle = 3
}

// Use it in Swift
let style = UITableViewCellStyle.default

这是基操, 但是如果是下面这种写法呢?

// Declare in Objective-C
typedef enum: NSUInteger {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
} UITableViewCellStyle;

// Generated Swift Interface
struct UITableViewCellStyle : Equatable, RawRepresentable {
    init(_ rawValue: UInt)
    init(rawValue: UInt)
    var rawValue: UInt
}

// Use it in Swift
let style = UITableViewCellStyleDefault

可见, 不用NS_ENUM

  1. Swift 也会自动生成一个枚举, 但是类型是 struct 而不是 enum
  2. 连名称也需要写全称, 一点也不Swift

NS_CLOSED_ENUM

NS_CLOSED_ENUM用于声明不会变更枚举成员的简单的枚举(简称 “冻结枚举” ),对应 Swift 中的 @frozen 关键字,将作为 @frozen enum 导入到 Swift 中。冻结枚举对于希望在 switch 语句中匹配有限状态集的时候非常有用,这个有限状态集是一个完整的集合,覆盖了所有情况,将来不会再有其他新的情况。

// Declare in Objective-C
typedef NS_CLOSED_ENUM(NSInteger, NSComparisonResult) {
    NSOrderedAscending = -1L,
    NSOrderedSame,
    NSOrderedDescending
};

// In Swift, the NSComparisonResult enumeration is imported like this:
@frozen enum NSComparisonResult : Int {
    case orderedAscending = -1
    case orderedSame = 0
    case orderedDescending = 1
}

使用 NS_ENUM 和 NS_CLOSED_ENUM 枚举宏在导入到 Swift 时生成的是 Enum 类型,而其它枚举宏都是生成 Struct 类型。

注:

冻结枚举降低了灵活性,但提升了性能。一旦枚举被标记为冻结枚举,那么在未来版本的库中就不能通过添加、删除或重新排序枚举的 case,否则会破坏 ABI 兼容性。

  • @unknown default不能用于冻结枚举,因为冻结枚举的 case 是固定的,不会发生改变。也就是说, 它用于非冻结枚举中的"未来加入的"枚举
  • 普通的"其它"是default

NS_OPTIONS

// Declare in Objective-C
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

// In Swift, the UIViewAutoresizing type is imported like this:
public struct UIViewAutoresizing: OptionSet {
    public init(rawValue: UInt)
    
    public static var flexibleLeftMargin: UIViewAutoresizing { get }
    public static var flexibleWidth: UIViewAutoresizing { get }
    public static var flexibleRightMargin: UIViewAutoresizing { get }
    public static var flexibleTopMargin: UIViewAutoresizing { get }
    public static var flexibleHeight: UIViewAutoresizing { get }
    public static var flexibleBottomMargin: UIViewAutoresizing { get }
}

// Use it in Swift
let style = UIViewAutoresizing([.flexibleWidth, .flexibleHeight])

可见, 要使用OPTIONS, 最好是在Objective-C中声明, 然后在Swift中使用。

NS_TYPED_ENUM

  • 它是NS_STRING_ENUM的替代品, 用来声明一个字符串类型的枚举
  • 在OC中并不是枚举, 是普通的String
  • 在Swift中则是一个Struct, 但可以把它当枚举来使用
// Store the three traffic light color options as 0, 1, and 2.
typedef long TrafficLightColor NS_TYPED_ENUM;
 
FOUNDATION_EXTERN TrafficLightColor const TrafficLightColorRed;
FOUNDATION_EXTERN TrafficLightColor const TrafficLightColorYellow;
FOUNDATION_EXTERN TrafficLightColor const TrafficLightColorGreen;

// In Swift, the TrafficLightColor type is imported like this:
struct TrafficLightColor : Hashable, Equatable, RawRepresentable {
    init(rawValue: Int)
}
extension TrafficLightColor {
    static let red: TrafficLightColor
    static let yellow: TrafficLightColor
    static let green: TrafficLightColor
}

// Use it in Swift
let color = TrafficLightColor.red
// 在能做类型推断的场景, 直接用点语法
let color: TrafficlightColor = .red

使用 NS_STRING_ENUM 宏,在逻辑上你不能在 Swift 中使用 extension 扩展新的常量集,虽然这是允许的。如果你需要做此支持,请使用 NS_TYPED_EXTENSIBLE_ENUM。

NS_TYPED_EXTENSIBLE_ENUM

用于声明可扩展的类型常量枚举。与 NS_TYPED_ENUM 的区别是生成的 struct 多了一个忽略参数标签的构造器。

// declared
typedef long FavoriteColor NS_TYPED_EXTENSIBLE_ENUM;
FOUNDATION_EXTERN FavoriteColor const FavoriteColorBlue;

// imported
struct FavoriteColor : Hashable, Equatable, RawRepresentable {
    // 多生成了个构造器
    init(_ rawValue: Int)
    init(rawValue: Int)
}
extension FavoriteColor {
    static let blue: FavoriteColor
}

// extended
extension FavoriteColor {
    static var green: FavoriteColor {
        return FavoriteColor(1) // blue is 0, green is 1, and new favorite colors could follow
    }
}

小结

仔细看下苹果的#define, 可以发现其实最终都落在NS_TYPED_ENUMNS_TYPED_EXTENSIBLE_ENUM上了.

#define _NS_TYPED_ENUM _CF_TYPED_ENUM
#define _NS_TYPED_EXTENSIBLE_ENUM _CF_TYPED_EXTENSIBLE_ENUM

// Note: NS_TYPED_ENUM is preferred to NS_STRING_ENUM
#define NS_STRING_ENUM _NS_TYPED_ENUM
// Note: NS_TYPED_EXTENSIBLE_ENUM is preferred to NS_EXTENSIBLE_STRING_ENUM
#define NS_EXTENSIBLE_STRING_ENUM _NS_TYPED_EXTENSIBLE_ENUM

#define NS_TYPED_ENUM _NS_TYPED_ENUM
#define NS_TYPED_EXTENSIBLE_ENUM _NS_TYPED_EXTENSIBLE_ENUM

via: source


Backlinks