LoginSignup
4
4

More than 5 years have passed since last update.

複数のBool値をひとつのInt値にして保存するとエコ (ビット演算)

Last updated at Posted at 2014-12-12

要旨

 ビット演算で頑張って True, True, True, True, True, True, True, True, True0b111_111_111 に変換して保存する。
 .storeddataのファイルサイズが1/5になったので、きっとエコ。

実例

 定休日を管理するクラスです。中身は2進数ですが、インスタンスを使うときにはEnumとBoolだけ触れば済みます。
 2進数ひと桁ごとに「trueなら1, falseなら0」というルールでゴリゴリ書いていきます。月曜休みなら9桁目がゼロ、火曜休みなら8桁目がゼロ、水曜休みなら… となります。

class BusinessDay {
    var binary: Int
    enum Day: Int {
        case Irregular, Holiday, Sun, Sat, Fri, Thu, Wed, Tue, Mon
    }
    private init(binary: Int) {
        self.binary = binary
    }
    convenience init() {
        self.init(binary: 0b11111_11_11)
    }
    func openOn(day: Day) -> Bool {
        // n = day.rawValue + 1
        // 右からn桁目を1にする
        let x = 0b1 << day.rawValue

        // 右からn桁目以外を0にする
        let y = (binary & x)

        // 0ならfalse
        return y != 0
    }
    func setDataOn(day: Day, open: Bool) {
        // 右からn桁目を1にする
        let x = 0b1 << day.rawValue

        // 右からn桁目以外を1にする
        let y = 0b11111_11_11 ^ x

        // 右からn桁目だけ0にする
        let z = binary & y

        if open {
            // 右からn桁目を1にする
            binary = x + y
        } else {
            // 右からn桁目は0のままにする
            binary = y
        }
    }
}

 ビット演算子が面白いので書きました。
 wikipediaを見ながら当てずっぽうで書いたものなので、もっと簡単に書けるのかも…

追記

 もっと簡単に書けたうえ、そもそもコメントが間違っていました。編集リクエスト多謝!
 言われてみれば確かに、「n桁目以外はゼロだが、n桁目はゼロか、ゼロでないか」というのは要するに「ゼロか、ゼロでないか」というのと同じことなのでした。勉強になります。

利点

 NSCodingを使い、NSDataにしたうえで保存する場合に比べて省スペースです。

2進数を使った場合.
<object type="ENTITY1" id="z102">
    <attribute name="day" type="int16">
        511
    </attribute>
</object>
NSCodingを使ってバイナリーデータを保存した場合.
<object type="ENTITY2" id="z102">
    <attribute name="day" type="binary">
        YnBsa(490文字略)AAAAAAAQk=
    </attribute>
</object>

 1000件ほど保存すると、前者は112kb, 後者は616kbになりました。それなりにエコです。

追記

 こちらのアイデアを使って書き直しました。



protocol StorableBools {
    init?(rawValue: Int)
    var rawValue: Int { get }
}

class InfoHolder <T: StorableBools> {

    private var binary: Int
    private var topicCount: Int {
        var n = 0
        while let x = topics(rawValue: ++n) {}
        return n
    }
    private var resetBinary: Int {
        return 1 << topicCount - 1
    }

    // target enum
    let topics: T.Type

    // init
    private init(topics: T.Type, binary: Int) {
        self.topics = topics
        self.binary = binary
    }
    convenience init (topics: T.Type) {
        self.init(topics: topics, binary: 0)
        binary = resetBinary
    }
    // method
    func trueAbout(topic: T) -> Bool {
        let x = 1 << topic.rawValue
        let y = (binary & x)
        return y != 0
    }
    func setDataOn(topic: T, open: Bool) {
        let x = 1 << topic.rawValue
        let y = resetBinary ^ x
        let z = binary & y
        if open {
            binary = x + z
        } else {
            binary = z
        }
    }
}

enum BusinessDay: Int, StorableBools {
    case Irregular, Holiday, Sun, Sat, Fri, Thu, Wed, Tue, Mon
}

// how to use
let days = InfoHolder(topics: BusinessDay.self)
days.setDataOn(.Mon, open: false)
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4