在 Objective-C 中访问 Swift OptionSetType

Accessing Swift OptionSetType in Objective-C

在我的 Swift class 中,我为履行选项定义了 OptionSetType

struct FulfillmentOption : OptionSetType {
    let rawValue: Int
    
    static let Pickup = FulfillmentOption(rawValue: 1 << 0)
    static let Shipping = FulfillmentOption(rawValue: 1 << 1)
    static let UserShipping = FulfillmentOption(rawValue: 1 << 2)
}

然后我为 add/remove 创建一个变量并读取选项。这按预期工作。

 var options: FulfillmentOption = []
 options.insert(FulfillmentOption.Pickup)
 options.contains(FulfillmentOption.Pickup)

但是我需要从我的 Objective-C classes 之一访问 options 变量。由于 OptionSetType 未在 Objective-C 中定义,因此该变量对我的任何 Objective-C classes.

都不可见

我向 Objective-C 公开此内容的最佳方式是什么?我应该完全停止使用 OptionSetType 吗?

我考虑过像这样创建 publicprivate 变量以在两者之间进行转换。我不喜欢这个,但这是迄今为止我想出的最好的。

private var _options: FulfillmentOptions = []
private var options: UInt {
  get {
    // get raw value from _options
  }
  set {
    // set raw value to _options
  }
}

有没有更优雅的方法来完成这个?我想避免编写不必要的代码。

不是您问题的直接答案,但您可以作为替代方案 反过来工作。定义

typedef NS_OPTIONS(NSInteger, FulfillmentOption) {
    FulfillmentOptionPickup = 1 << 0,
    FulfillmentOptionShipping = 1 << 1,
    FulfillmentOptionUserShipping = 1 << 2,
};

在 Objective-C header 中,这将作为

导入到 Swift
public struct FulfillmentOption : OptionSetType {
    public init(rawValue: Int)

    public static var Pickup: FulfillmentOption { get }
    public static var Shipping: FulfillmentOption { get }
    public static var UserShipping: FulfillmentOption { get }
}

可以在 "Using Swift with Cocoa and Objective-C" 参考资料中找到更多信息:

  • "Interaction with C APIs":

Swift also imports C-style enumerations marked with the NS_OPTIONS macro as a Swift option set. Option sets behave similarly to imported enumerations by truncating their prefixes to option value names.

  • "Swift and Objective-C in the Same Project":

You’ll have access to anything within a class or protocol that’s marked with the @objc attribute as long as it’s compatible with Objective-C. This excludes Swift-only features such as those listed here:

  • ...
  • Structures defined in Swift
  • ...

Objective-C 看不到 struct,但幸运的是,您可以使用 @objc class 来实现 OptionSet

请注意,实现 -hash-isEqual: 非常重要,因为 OptionSet 继承的许多 SetAlgebra 默认实现都依赖它们才能工作。如果你不实现它们,print("\(Ability(.canRead) == Ability(.canRead))") 打印 false.

@objc
public final class Ability: NSObject, OptionSet {
    // Don't use
    //
    // public static let canRead = Ability(rawValue: 1 << 0)
    // public static let canWrite = Ability(rawValue: 1 << 1)
    //
    // because `Ability` is a `class`, not a `struct`, 
    // so `let` doesn't enforce immutability, and thus
    //
    // Ability.canRead.formUnion(Ability.canWrite)
    //
    // would break `Ability.canRead` for everyone!

    public static func canRead(): Ability { 
        return Ability(rawValue: 1 << 0)
    }

    public static func canWrite(): Ability {
        return Ability(rawValue: 1 << 1)
    }

    public var rawValue: Int

    public typealias RawValue = Int

    public override convenience init() {
        self.init(rawValue: 0)
    }

    public init(rawValue: Int) {
        self.rawValue = rawValue

        super.init()
    }

    /// Must expose this to Objective-C manually because
    /// OptionSet.init(_: Sequence) isn't visible to Objective-C
    /// Because Sequence isn't an Objective-C-visible type
    @objc
    @available(swift, obsoleted: 1.0)
    public convenience init(abilitiesToUnion: [Ability]) {
        self.init(abilitiesToUnion)
    }    

    // MARK: NSObject

    // Note that it is very important to implement -hash and
    // -isEqual: because lots of the `SetAlgebra`
    // default implementations that `OptionSet` inherits 
    // rely on them to work. If you don't implement them,
    // print("\(Ability(.canRead) == Ability(.canRead))")
    // prints `false`

    public override var hash: Int {
        return rawValue
    }

    public override func isEqual(_ object: Any?) -> Bool {
        guard let that = object as? Ability else {
            return false
        }

        return rawValue == that.rawValue
    }

    // MARK: OptionSet

    public func formUnion(_ other: Ability) {
        rawValue = rawValue | other.rawValue
    }

    public func formIntersection(_ other: Ability) {
        rawValue = rawValue & other.rawValue
    }

    public func formSymmetricDifference(_ other: Ability) {
        rawValue = rawValue ^ other.rawValue
    }  
}

然后,我可以从 Objective-C:

实例化它
Ability *emptyAbility = [Ability new];
Ability *readOnlyAbility = [[Ability alloc] initWithAbilitiesToUnion:@[Ability.canRead]];
Ability *readWriteAbility = [[Ability alloc] initWithAbilitiesToUnion:@[Ability.canRead, Ability.canWrite]];