我想在Swift中存储一组弱引用.数组本身不应该是弱引用 - 它的元素应该是.我认为Cocoa NSPointerArray
提供了非类型安全版本.
派对有点迟,但试试我的.我实现为Set而不是数组.
class WeakObject<T: AnyObject>: Equatable, Hashable { weak var object: T? init(object: T) { self.object = object } var hashValue: Int { if let object = self.object { return unsafeAddressOf(object).hashValue } else { return 0 } } } func == <T> (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool { return lhs.object === rhs.object } class WeakObjectSet<T: AnyObject> { var objects: Set<WeakObject<T>> init() { self.objects = Set<WeakObject<T>>([]) } init(objects: [T]) { self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) }) } var allObjects: [T] { return objects.flatMap { $0.object } } func contains(object: T) -> Bool { return self.objects.contains(WeakObject(object: object)) } func addObject(object: T) { self.objects.unionInPlace([WeakObject(object: object)]) } func addObjects(objects: [T]) { self.objects.unionInPlace(objects.map { WeakObject(object: $0) }) } }
var alice: NSString? = "Alice" var bob: NSString? = "Bob" var cathline: NSString? = "Cathline" var persons = WeakObjectSet<NSString>() persons.addObject(bob!) print(persons.allObjects) // [Bob] persons.addObject(bob!) print(persons.allObjects) // [Bob] persons.addObjects([alice!, cathline!]) print(persons.allObjects) // [Alice, Cathline, Bob] alice = nil print(persons.allObjects) // [Cathline, Bob] bob = nil print(persons.allObjects) // [Cathline]
请注意WeakObjectSet不会采用String类型而是采用NSString.因为,String类型不是AnyType.我的快捷版本是Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
代码可以从Gist中获取. https://gist.github.com/codelynx/30d3c42a833321f17d39
**于2010年11月上市
我将代码更新为Swift 4
// Swift 4, Xcode Version 9.1 (9B55) class WeakObject<T: AnyObject>: Equatable, Hashable { weak var object: T? init(object: T) { self.object = object } var hashValue: Int { if var object = object { return UnsafeMutablePointer<T>(&object).hashValue } return 0 } static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool { return lhs.object === rhs.object } } class WeakObjectSet<T: AnyObject> { var objects: Set<WeakObject<T>> init() { self.objects = Set<WeakObject<T>>([]) } init(objects: [T]) { self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) }) } var allObjects: [T] { return objects.flatMap { $0.object } } func contains(_ object: T) -> Bool { return self.objects.contains(WeakObject(object: object)) } func addObject(_ object: T) { self.objects.formUnion([WeakObject(object: object)]) } func addObjects(_ objects: [T]) { self.objects.formUnion(objects.map { WeakObject(object: $0) }) } }
正如gokeji所提到的,我发现NSString不会根据使用中的代码取消分配.我摸不着头脑,按照以下方式写了MyString类.
// typealias MyString = NSString class MyString: CustomStringConvertible { var string: String init(string: String) { self.string = string } deinit { print("relasing: \(string)") } var description: String { return self.string } }
然后NSString
用MyString
这样替换.然后奇怪地说它有效.
var alice: MyString? = MyString(string: "Alice") var bob: MyString? = MyString(string: "Bob") var cathline: MyString? = MyString(string: "Cathline") var persons = WeakObjectSet<MyString>() persons.addObject(bob!) print(persons.allObjects) // [Bob] persons.addObject(bob!) print(persons.allObjects) // [Bob] persons.addObjects([alice!, cathline!]) print(persons.allObjects) // [Alice, Cathline, Bob] alice = nil print(persons.allObjects) // [Cathline, Bob] bob = nil print(persons.allObjects) // [Cathline]
然后我发现一个奇怪的页面可能与此问题有关.
弱引用保留解除分配的NSString(仅限XC9 + iOS Sim)
https://bugs.swift.org/browse/SR-5511
它说问题是,RESOLVED
但我想知道这是否仍然与这个问题有关.无论如何,MyString或NSString之间的行为差异超出了这个范围,但如果有人想出这个问题,我将不胜感激.
这不是我的解决方案.我在Apple Developer论坛上找到了它.
@GoZoner有一个很好的答案,但它崩溃了Swift编译器.
这是一个弱对象容器的版本不会崩溃当前发布的编译器.
struct WeakContainer<T where T: AnyObject> { weak var _value : T? init (value: T) { _value = value } func get() -> T? { return _value } }
然后,您可以创建这些容器的数组:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
您可以通过创建包装器对象来保存弱指针来完成此操作.
struct WeakThing<T: AnyObject> { weak var value: T? init (value: T) { self.value = value } }
然后在阵列中使用这些
var weakThings = WeakThing<Foo>[]()
您可以将NSHashTable与weakObjectsHashTable一起使用. NSHashTable.weakObjectsHashTable()
对于Swift 3: NSHashTable.weakObjects()
NSHashTable类参考
适用于OS X v10.5及更高版本.
适用于iOS 6.0及更高版本.
功能样式包装怎么样?
class Class1 {} func captureWeakly<T> (_ target:T) -> (() -> T?) where T: AnyObject { return { [weak target] in return target } } let obj1 = Class1() let obj2 = Class1() let obj3 = Class1() let captured1 = captureWeakly(obj1) let captured2 = captureWeakly(obj2) let captured3 = captureWeakly(obj3)
只需调用返回的闭包来检查目标是否仍然存活.
let isAlive = captured1() != nil let theValue = captured1()!
您可以将此闭包存储到数组中.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
并且您可以通过映射调用闭包来检索弱捕获的值.
let values = Array(array1.map({ $0() }))
创建一个通用包装器:
class Weak<T: AnyObject> { weak var value : T? init (value: T) { self.value = value } }
将此类的实例添加到您的数组.
class Stuff {} var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
定义时,Weak
您可以使用struct
或class
.
此外,为了帮助获取数组内容,您可以执行以下操作:
extension Array where Element:Weak<AnyObject> { mutating func reap () { self = self.filter { nil != $0.value } } }
AnyObject
上面的使用应该替换为T
- 但我不认为当前的Swift语言允许扩展定义为这样.
我有同样的想法用泛型创建弱容器.
结果我创建了包装器NSHashTable
:
class WeakSet<ObjectType>: SequenceType { var count: Int { return weakStorage.count } private let weakStorage = NSHashTable.weakObjectsHashTable() func addObject(object: ObjectType) { guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") } weakStorage.addObject(object as? AnyObject) } func removeObject(object: ObjectType) { guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") } weakStorage.removeObject(object as? AnyObject) } func removeAllObjects() { weakStorage.removeAllObjects() } func containsObject(object: ObjectType) -> Bool { guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") } return weakStorage.containsObject(object as? AnyObject) } func generate() -> AnyGenerator<ObjectType> { let enumerator = weakStorage.objectEnumerator() return anyGenerator { return enumerator.nextObject() as! ObjectType? } } }
用法:
protocol MyDelegate : AnyObject { func doWork() } class MyClass: AnyObject, MyDelegate { fun doWork() { // Do delegated work. } } var delegates = WeakSet<MyDelegate>() delegates.addObject(MyClass()) for delegate in delegates { delegate.doWork() }
它不是最好的解决方案,因为WeakSet
可以使用任何类型进行初始化,如果此类型不符合AnyObject
协议,则应用程序将因详细原因而崩溃.但我现在没有看到任何更好的解决方案.
原始解决方案是以WeakSet
这种方式定义:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
但在这种情况下WeakSet
无法使用协议初始化:
protocol MyDelegate : AnyObject { func doWork() } let weakSet = WeakSet<MyDelegate>()
目前上面的代码无法编译(Swift 2.1,Xcode 7.1).
这就是为什么我放弃了符合AnyObject
并添加额外的警卫fatalError()
断言.