objc_getAssociatedObject で必ずnil返ってくる件(解決済み)
こちらのエントリーでExtensionPropertyをExtensionとしてつけるだけでassociated objectにアクセスしやすくするというエントリーを書きました
しかしながら、この記事にはバグがありました。
それは getProperty
が必ずnilになってしまい、デフォルト値が必ず返ってきていました。
原因
該当するコード
public extension ExtensionProperty { public func getProperty<K: ExtensionPropertyKey, V>(key: K, initialValue: V) -> V { var keyString = key.keyString if let variable = objc_getAssociatedObject(self, &keyString) as? V { return variable } setProperty(key: key, newValue: initialValue) return initialValue } public func setProperty<K: ExtensionPropertyKey, V>(key: K, newValue: V) { objc_setAssociatedObject(self, key.keyString, newValue, .OBJC_ASSOCIATION_RETAIN) } }
var keyString = key.keyString
Stringをコピーしていたため、インスタンスのメモリの番地が違ってしまっていた。結果的にキーが違うのでnilが返ってくるという理屈でした。
対応
本当はExtensionPropertyKeyのkeyStringの参照を渡すことができれば容易なのですが、 inout
な引数にletな値を渡すことはできません。
そこで、UnsafeRawPointer
を取得して渡すことで解決しました。これならenumをキーとして使いたいという当初の目的を達成しています。
internal extension ExtensionPropertyKey { var keyPointer: UnsafeRawPointer { return unsafeBitCast(self.keyString, to: UnsafeRawPointer.self) } } // MARK: - ExtensionProperty public extension ExtensionProperty { public func getProperty<K: ExtensionPropertyKey, V>(key: K, defaultValue: V) -> V { if let variable = objc_getAssociatedObject(self, key.keyPointer) as? V { return variable } setProperty(key: key, newValue: defaultValue) return defaultValue } public func setProperty<K: ExtensionPropertyKey, V>(key: K, newValue: V, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN) { objc_setAssociatedObject(self, key.keyPointer, newValue, policy) } }
いや〜。ナマのポインタは最強ですね