鉴于以下内容:
struct Weekdays: OptionSetType {
let rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let Monday = Weekdays(rawValue: 1)
static let Tuesday = Weekdays(rawValue: 2)
static let Wednesday = Weekdays(rawValue: 4)
static let Thursday = Weekdays(rawValue: 8)
static let allOptions: [Weekdays] = [.Monday, .Tuesday, .Wednesday, .Thursday]
}
我可以通过这样做将Ints
数组转换为Weekdays
对象:
let arr = [1, 4]
let weekdays = arr.reduce(Weekdays()) { $0.union(Weekdays(rawValue: $1)) }
我的问题是,如何使用Weekdays
对象并将其转换为Ints
数组?
答案 0 :(得分:7)
(不一定更好,但以不同的方式来看待它并稍微有点 更一般)。
OptionSetType
继承自RawRepresentable
,因此可以
从和转换为关联的原始类型,在您的情况下是
Int
。
所以"缺少链接"是原始值之间的转换(例如5
)
和按位分量的整数数组(例如[1, 4]
)。
可以使用Int
扩展方法完成此操作:
extension Int {
init(bitComponents : [Int]) {
self = bitComponents.reduce(0, combine: (+))
}
func bitComponents() -> [Int] {
return (0 ..< 8*sizeof(Int)).map( { 1 << $0 }).filter( { self & $0 != 0 } )
}
}
然后,您从数组转换为Weekdays
对象变为
let arr : [Int] = [1, 4]
let weekdays = Weekdays(rawValue: Int(bitComponents: arr))
print(weekdays)
// app.Weekdays(rawValue: 5)
和反向转换
let array = weekdays.rawValue.bitComponents()
print(array)
// [1, 4]
优点:
allOptions:
的明确定义。Int
作为原始价值)。还可以尝试将转换定义为协议扩展,
例如IntegerType
的{{1}},以便同样适用于其他整数原始类型。然而,这似乎有点复杂/丑陋
因为左移操作符<<
不属于
IntegerType
(或任何)协议。
Swift 3的更新:
extension Int {
init(bitComponents : [Int]) {
self = bitComponents.reduce(0, +)
}
func bitComponents() -> [Int] {
return (0 ..< 8*MemoryLayout<Int>.size).map( { 1 << $0 }).filter( { self & $0 != 0 } )
}
}
答案 1 :(得分:1)
当我写这个问题时,我想出来了:
let array = Weekdays.allOptions.filter { weekdays.contains($0) }.map { $0.rawValue }
有更好的方法吗?
答案 2 :(得分:1)
不能完全回答问题,但对其他人可能有用。根据马丁的回答,我提取出组件对象:
extension Int {
init(bitComponents : [Int]) {
self = bitComponents.reduce(0, +)
}
var bitComponents : [Int] {
return (0 ..< Int.bitWidth).map { 1 << $0 } .filter { self & $0 != 0 }
}
}
extension OptionSet where RawValue == Int, Self == Self.Element {
var components : [Self] {
return rawValue.bitComponents.map { return type(of: self).init(rawValue: $0) }
}
}
答案 3 :(得分:1)
您可以通过在OptionSet
中有条件地定义扩展名来改善其上下文。
extension OptionSet where RawValue: UnsignedInteger {
var individualCases: [Self] {
return (0..<(8 * MemoryLayout<RawValue>.size))
.map { bitsToShift in RawValue(1 << bitsToShift) } // Get every possible single-bit flag
.filter { (powerOfTwo: RawValue) -> Bool in rawValue & powerOfTwo != 0 } // filter out only the cases the receiver contains
.map { Self(rawValue: $0) } // create the `OptionSet` (single bit) type
}
}
let weekdays = Weekdays(rawValue: 0b11111)
weekdays.individualCases.map { $0.rawValue } // [1, 2, 4, 8, 16]
警告:在我的13英寸2019 MacBookPro上,我必须提供上述所有显式类型,才能在Swift 5.0中将方法类型检查保持在1500ms以内。
感谢MartinR启发了我们重新设计内存布局大小的方法。
为完成示例,我将以下工作日类型更新为下面的Swift 5,并明确使用UInt8
类型来提高individualCases
的效率。使用UInt
时,它将分别循环前64个循环map
和filter
,使用UInt8
则仅循环8次。
struct Weekdays: OptionSet {
let rawValue: UInt8
static let Monday = Weekdays(rawValue: 1)
static let Tuesday = Weekdays(rawValue: 2)
static let Wednesday = Weekdays(rawValue: 4)
static let Thursday = Weekdays(rawValue: 8)
static let Friday = Weekdays(rawValue: 16)
}