Edited at

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

More than 3 years have passed since last update.


要旨

 ビット演算で頑張って 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)