NSMapTable
の Key
の挙動に不信感を抱いたので少し調べてみました。ObjC時代にも調べたような気がしますが、記憶が定かではないので、自分へのメモ。
key
が NSString
の場合。"string" as NSString
とか書いている時点で、異なるオブジェクトをキーにしているような気もするのですが、なぜか一致します。まぁ、NSString
の場合はそうでなければ不便なのでいいとします。
var key: NSString
key = "east"
mapTable.setObject(1024 as NSNumber, forKey: key as NSString)
key = "west"
mapTable.setObject(512 as NSNumber, forKey: key as NSString)
mapTable.object(forKey: "east" as NSString) // "1024" as expected
mapTable.object(forKey: "west" as NSString) // "512" as expected
key
が NSMutableString
の場合で setString()
でその値を変えてみました。なんか不思議な挙動ですね。table内のキーは取り出せなくなってしまったのでしょうか。
let mapTable = NSMapTable<NSString, NSNumber>()
var key: NSMutableString
key = NSMutableString(string: "north")
mapTable.setObject(256 as NSNumber, forKey: key)
key.setString("south")
mapTable.object(forKey: "north" as NSString) // nil
mapTable.object(forKey: "south" as NSString) // nil
mapTable.object(forKey: key) // nil -- why??
mapTable.keyEnumerator().allObjects // ["south"] wow!!
key
が同じ値でも、別オブジェクトでアクセス。同一キーとして扱われています。
let mapTable = NSMapTable<NSString, NSNumber>()
var key1 = NSMutableString(string: "north")
var key2 = NSMutableString(string: "north")
mapTable.setObject(64 as NSNumber, forKey: key1)
mapTable.setObject(2048 as NSNumber, forKey: key2)
key1.setString("north")
mapTable.object(forKey: key1) // "2048" as expected
mapTable.keyEnumerator().allObjects // ["north"]
key
が NSDictionary
の場合。 key
とvalue
の定義する順番が異なっても、同じオブジェクトとして扱ってくれるようです。また、NSMutableDictionary
に setDictionary()
で同じKey
とValue
のセットを与えても、同じキーと判断してくれるようです。
let mapTable = NSMapTable<NSDictionary, NSString>()
let key1a = NSDictionary(dictionary: ["city": "Tokyo", "color": "blue"])
let key1b = NSDictionary(dictionary: ["color": "blue", "city": "Tokyo"])
let key1ax = NSMutableDictionary(dictionary: ["city": "Tokyo", "color": "blue"])
var key1c = NSMutableDictionary()
key1c.setDictionary(["city": "Tokyo", "color": "blue"])
mapTable.setObject("Japan" as NSString, forKey: key1a)
mapTable.object(forKey: key1b) // "Japan"
mapTable.object(forKey: key1ax) // "Japan"
mapTable.object(forKey: key1c) // "Japan"
key
を NSMutableDictionary
にして 値をセット、その後 key
の値を変更。すると、テーブル内のキーも変更され、キーはオブジェクトのように扱われているようです。
let mapTable = NSMapTable<NSDictionary, NSString>()
var key1 = NSMutableDictionary(dictionary: ["city": "Tokyo", "color": "blue"])
mapTable.setObject("Apple" as NSString, forKey: key1)
key1.setDictionary(["color": "red", "city": "Tokyo"]) // "red" !?
mapTable.object(forKey: key1) // "Apple"
mapTable.object(forKey: ["city": "Tokyo", "color": "blue"]) // nil
mapTable.keyEnumerator().allObjects // [["color": "red", "city": "Tokyo"]]
自分のクラスの場合です。Hashable
(Equatable
)を実装しても、実質的等価なオブジェクトも同じキーとは扱えないようで、これはこれで期待通りの挙動だったりします。
class MyObject: Hashable {
var name: String
init(name: String) {
self.name = name
}
var hashValue: Int { return name.hashValue }
}
func == (lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.name == rhs.name
}
let mapTable = NSMapTable<MyObject, NSString>()
let obj1a = MyObject(name: "red")
let obj1b = MyObject(name: "red")
let obj2 = MyObject(name: "blue")
obj1a == obj1b // true
mapTable.setObject("Orange", forKey: obj1a)
mapTable.object(forKey: obj1a) // "Orange"
mapTable.object(forKey: obj1b) // nil
mapTable.object(forKey: obj2) // nil