Swift1.2 → 2.0での変化
Swift1.2 → 2.0でRawOptionSetTypeがOptionSetTypeに移り変わり、書き方が変わったのはご存知かと思います。
このOptionSetへの移行でよく記事として見かけるUIUserNotificationTypeを例に出すと、、
let notificationTypes: UIUserNotificationType = .Alert | .Badge | .Sound
let notificationTypes = UIUserNotificationType([.Alert, .Badge, .Sound])
// .Alert | .Badge | .Sound の書き方は×
こんな感じで、 | を使わずに表現する形に移りました。1.2→2.0の移行をした時は大変でしたね
書き方が変わった以外にも、OptionSetTypeを1.2の時よりも定義しやすくなったのも大きな変化かと思います。
人によっては、2.0移行での書き方 ([.Alert, .Badge, .Sound]
)、SomeType.A.union(.B)
のが好き!って人もいると思いますが......
& , | , ^ , ~ 使いたくないですか!?
基本的にUIUserNotificationTypeとかみたいにセットするだけ程度なら不要かもしれないですが、
自分でOptionSetType作って、フラグ管理する場合なんかは、
let type = SomeType.A
let other = SomeType.B
var newType = type.union(other) // ビット演算子の | と同等のことをする関数
newType.unionInPlace(.C)
newType.remove(.C)
ってやるよりは、
let type = SomeType.A
let other = SomeType.B
var newType = type | other
newType |= .C
newType &= ~.C
ってやった方が短くなるしスマートな気がします。
自分はC,C++
に慣れ親しんできたので、unionを使うよりこっちの書き方のが好きだしわかりやすいです。
ということで...使えるようにしましょう!
CustomOperatorを実装する
環境
- Xcode 7.1.1 (Swift 2.1.1)
おそらくSwift2.0移行なら大丈夫です。
以下のように、それぞれ必要な演算子を定義していきます。
func & <T: OptionSetType where T.RawValue: BitwiseOperationsType>(lhs: T, rhs: T) -> T {
return T(rawValue: lhs.rawValue & rhs.rawValue)
}
func | <T: OptionSetType where T.RawValue: BitwiseOperationsType>(lhs: T, rhs: T) -> T {
return T(rawValue: lhs.rawValue | rhs.rawValue)
}
func ^ <T: OptionSetType where T.RawValue: BitwiseOperationsType>(lhs: T, rhs: T) -> T {
return T(rawValue: lhs.rawValue ^ rhs.rawValue)
}
func &= <T: OptionSetType where T.RawValue: BitwiseOperationsType>(inout lhs: T, rhs: T) {
lhs = lhs & rhs
}
func |= <T: OptionSetType where T.RawValue: BitwiseOperationsType>(inout lhs: T, rhs: T) {
lhs = lhs | rhs
}
func ^= <T: OptionSetType where T.RawValue: BitwiseOperationsType>(inout lhs: T, rhs: T) {
lhs = lhs ^ rhs
}
prefix func ~ <T: OptionSetType where T.RawValue: BitwiseOperationsType>(type: T) -> T {
return T(rawValue: ~type.rawValue)
}
これで、 & , &= , | , |= , ^ , ^= , ~ , が使えるようになります
それぞれの演算子はもともとビット演算子として用意されているので、オーバーロードという形となります。
ポイントは、OptionSetType
のrawValueがBitwiseOperationsTypeに適合するという条件を加えることで、
lhs
とrhs
のrawValueでビット演算をおこなえるようになり、演算を行った結果をrawValueとして渡して新たなOptionSetType
を返しています。
ということで、冒頭で出してた例で再現すると、
let h = UIUserNotificationType([.Alert, .Badge, .Sound])
let g: UIUserNotificationType = .Alert | .Badge | .Sound // OK!!!!
// let g = UIUserNotificationType.Alert | .Badge | .Sound // これもOK。
//一番最初のTypeに型を書けばあとのTypeに対して型推論が働くらしい。
print(h == g) // true、2つとも同値であることが確認できた!!
~(チルダ) も実装したので、こんなこともできます
あくまでも例なので、実用的かどうかは一旦置いておきます。
import UIKit
import Foundation
import XCPlayground
let corners = UIRectCorner.AllCorners & ~.TopLeft // 下の行と同じ意味になる
//let corners = UIRectCorner.TopRight | .BottomLeft | .BottomRight
let view = UIView(frame: CGRectMake(0, 0, 100, 100))
view.backgroundColor = UIColor.redColor()
view.clipsToBounds = true
let path = UIBezierPath(
roundedRect: view.bounds,
byRoundingCorners: corners,
cornerRadii: CGSizeMake(50.0, 50.0)
)
let layer = CAShapeLayer()
layer.frame = view.bounds
layer.path = path.CGPath
view.layer.mask = layer
XCPlaygroundPage.currentPage.liveView = view // 左上以外が角丸のviewが描画される
A & ~B
で、AからBのビットを除いた結果を返すことができます。
ビット演算については、以下が参考になるかと思います。
あとはおこのみで、OptionSetTypeと、OptionSetTypeのRawValueと同じ型の数値と大小比較できる比較演算子を定義して、以下の処理ができるようにするといいかもしれないですね
var type = UIRectCorner.TopRight | .BottomLeft
if (type & .TopRight) > 0 { //typeがセットされているか調べる
//if (type & .TopRight).rawValue > 0 { ">"を実装しなければ、こうするのが正解
print("TopRight is Set!!")
}
これでだいぶ、CやC++の時のような扱い方ができるようになったかと思います!では!
あとがき(Swiftのソースを覗いてみる)
当初は、
func | <T: OptionSetType>(lhs: T, rhs: T) -> T {
return lhs.union(rhs) //ただのラッパー程度の実装
}
...
のように定義していたのですが、実際にunion ( | ), intersect ( & ), exclusiveOr ( ^ )とかをどうやって組んでいるだろうということで本家(swift/stdlib/public/core/OptionSet.swift)を 見にいったら、
extension OptionSetType {
/// Returns the set of elements contained in `self`, in `other`, or in
/// both `self` and `other`.
@warn_unused_result
public func union(other: Self) -> Self {
var r: Self = Self(rawValue: self.rawValue)
r.unionInPlace(other)
return r
}
...
}
extension OptionSetType where RawValue : BitwiseOperationsType {
/// Create an empty instance.
///
/// - Equivalent to `[] as Self`
public init() {
self.init(rawValue: .allZeros)
}
/// Insert all elements of `other` into `self`.
///
/// - Equivalent to replacing `self` with `self.union(other)`.
/// - Postcondition: `self.isSupersetOf(other)`
public mutating func unionInPlace(other: Self) {
self = Self(rawValue: self.rawValue | other.rawValue)
}
/// Remove all elements of `self` that are not also present in
/// `other`.
///
/// - Equivalent to replacing `self` with `self.intersect(other)`
/// - Postcondition: `self.isSubsetOf(other)`
public mutating func intersectInPlace(other: Self) {
self = Self(rawValue: self.rawValue & other.rawValue)
}
/// Replace `self` with a set containing all elements contained in
/// either `self` or `other`, but not both.
///
/// - Equivalent to replacing `self` with `self.exclusiveOr(other)`
public mutating func exclusiveOrInPlace(other: Self) {
self = Self(rawValue: self.rawValue ^ other.rawValue)
}
}
って書いてあるのを見つけて、intersectとかを書いてたら結果として処理が深くなっちゃうしなって思って、参考にしたのが今の形となります。
意外に、使える絵文字色々あるんですね