我想通过扩展将Swift事物(泛型,结构,元组,任何Objective-C不喜欢的东西)与NSObject实例相关联。
我们今天怎么办? objc_setassociatedobject
对于处理Swift功能毫无用处。
我的第一种方法是使用带有弱键的全局字典来存储关联。类似的东西:
struct WeakKey<T where T: NSObject> : Hashable {
weak var object : T!
init (object: T) {
self.object = object
}
var hashValue: Int { return self.object.hashValue }
}
func ==<T where T: NSObject>(lhs: WeakKey<T>, rhs: WeakKey<T>) -> Bool {
return lhs.object == rhs.object
}
typealias ThingObjcDoesntLike = (Int, String)
var _associations : [WeakKey<NSObject>: ThingObjcDoesntLike] = [:]
extension NSObject {
var associatedThing : ThingObjcDoesntLike! {
get {
let key = WeakKey(object: self)
return _associations[key]
}
set(thing) {
let key = WeakKey(object: self)
_associations[key] = thing
}
}
}
let o = NSObject()
let t = (1, "Hello World")
o.associatedThing = t
很遗憾,这会在EXC_BAD_ACCESS
声明中与_associations
崩溃。删除weak
可以解决崩溃问题,但会导致NSObject实例被保留。
为了使其可行,我们还应该在解除分配NSObject时将关联设置为nil
。我们可以使用相关的见证对象,并覆盖其dealloc来完成工作。为简单起见,我将其从上面的代码中删除。
答案 0 :(得分:1)
这样的事情怎么样:
class ObjectWrapper : NSObject {
let value: ThingObjcDoesntLike
init(value: ThingObjcDoesntLike) {
self.value = value
}
}
extension NSObject {
var associatedThing : ThingObjcDoesntLike! {
get {
let wrapper = objc_getAssociatedObject(self, someKey) as ObjectWrapper?
return wrapper?.value
}
set(value) {
let wrapper = ObjectWrapper(value: value)
objc_setAssociatedObject(self, someKey, wrapper, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
}
答案 1 :(得分:1)
@NickLockwood的回答
这是我在Playground表单中所做的。我发布它是因为它似乎有用,我发现代码很有趣,即使它最终不是最好的解决方案。
我使用全局Swift Dictionary
,指针为弱键。为了清除字典,我使用见证关联对象,使用deinit
上的关联回调对象。
import Foundation
import ObjectiveC
var _associations : [COpaquePointer: Any] = [:]
@objc protocol HasAssociatedSwift : class {
func clearSwiftAssociations()
}
var _DeinitWitnessKey: UInt8 = 0
class DeinitWitness : NSObject {
weak var object : HasAssociatedSwift!
init (object: HasAssociatedSwift) {
self.object = object
}
deinit {
object.clearSwiftAssociations()
}
class func addToObject(object : NSObject) {
var witness = objc_getAssociatedObject(object, &_DeinitWitnessKey) as DeinitWitness?
if (witness == nil) {
witness = DeinitWitness(object: object)
objc_setAssociatedObject(object, &_DeinitWitnessKey, witness, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
}
extension NSObject : HasAssociatedSwift {
var associatedThing : Any! {
get {
return _associations[self.opaquePointer]
}
set(thing) {
DeinitWitness.addToObject(self)
_associations[self.opaquePointer] = thing
}
}
var opaquePointer : COpaquePointer {
return Unmanaged<AnyObject>.passUnretained(self).toOpaque()
}
func clearSwiftAssociations() {
_associations[self.opaquePointer] = nil
}
}
let o = NSObject()
o.associatedThing = (1, "Hello")