我如何在Swift中声明一个弱引用数组?
我想在Swift中存储一个弱引用数组。 数组本身不应该是一个弱引用 – 它的元素应该是。 我觉得cocoaNSPointerArray
提供了一个非types安全的版本。
创build一个通用的包装为:
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
的使用应该被replace为T
– 但是我不认为当前的Swift语言允许定义这样的扩展。
你可以使用带有weakObjectsHashTable的NSHashTable。 NSHashTable.weakObjectsHashTable()
对于Swift 3: NSHashTable.weakObjects()
NSHashTable类参考
在OS X v10.5及更高版本中可用。
在iOS 6.0及更高版本中可用。
这不是我的解决scheme。 我在苹果开发者论坛上find了它 。
@GoZoner有一个很好的答案,但它崩溃了Swift编译器。
这里有一个弱对象容器的版本不会使当前发布的编译器崩溃。
struct WeakContainer<T where T: AnyObject> { weak var _value : T? init (value: T) { _value = value } func get() -> T? { return _value } }
然后你可以创build这些容器的数组:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
你可以通过创build一个包装对象来容纳一个弱指针。
struct WeakThing<T: AnyObject> { weak var value: T? init (value: T) { self.value = value } }
然后在数组中使用这些
var weakThings = WeakThing<Foo>[]()
派对很晚了,但是试试我的。 我实现为一个不是一个数组。
WeakObjectSet
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不会采用Stringtypes,而是NSString。 因为,stringtypes不是AnyType。 我的swift版本是Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
。
代码可以从Gist中抓取。 https://gist.github.com/codelynx/30d3c42a833321f17d39
**于二零一七年十一月join
我更新了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 } }
然后像这样用MyString
replaceNSString
。 然后奇怪的说,它的作品。
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
它说这个问题是解决的,但我想知道这是否仍然与这个问题有关。 无论如何,MyString或NSString之间的行为差异超出了这个范围,但是如果有人能够解决这个问题,我将不胜感激。
我有同样的想法,用generics创build弱容器。
结果我创build了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() }
这不是最好的解决scheme,因为WeakSet
可以用任何types初始化,如果这种types不符合AnyObject
协议,那么应用程序将崩溃,具体原因。 但是现在我没有看到更好的解决scheme。
原始的解决scheme是这样定义WeakSet
:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
但在这种情况下, WeakSet
不能用协议初始化:
protocol MyDelegate : AnyObject { func doWork() } let weakSet = WeakSet<MyDelegate>()
目前上面的代码不能编译(Swift 2.1,Xcode 7.1)。
这就是为什么我放弃符合AnyObject
并添加与fatalError()
断言更多的警卫。
function样式包装如何?
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() }))
现有的WeakContainer示例很有帮助,但它并不能真正帮助在现有的Swift容器(如Lists和Dictionaries)中使用弱引用。
如果你想使用List方法,如包含,那么WeakContainer将需要实现Equatable。 所以我添加了代码以使WeakContainer可以相等。
如果你想在字典中使用WeakContainer,我也使它易于使用,所以它可以用作字典键。
我也将它重命名为WeakObject,强调这只适用于类types,并将其与WeakContainer示例区分开来:
struct WeakObject<TYPE where TYPE:AnyObject> : Equatable, Hashable { weak var _value : TYPE? let _originalHashValue : Int init (value: TYPE) { _value = value // We keep around the original hash value so that we can return it to represent this // object even if the value became Nil out from under us because the object went away. _originalHashValue = ObjectIdentifier(value).hashValue } var value : TYPE? { return _value } var hashValue: Int { return _originalHashValue } } func ==<T>(lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool { if lhs.value == nil && rhs.value == nil { return true } else if lhs.value == nil || rhs.value == nil { return false } // If the objects are the same, then we are good to go return lhs.value! === rhs.value! }
这可以让你做一些很酷的东西,比如使用弱引用字典:
private var m_observerDict : Dictionary<WeakObject<AnyObject>,FLObservationBlock> = Dictionary() func addObserver( observer:AnyObject, block:FLObservationBlock ) { let weakObserver = WeakObject(value:observer) m_observerDict[weakObserver] = block } func removeObserver( observer:AnyObject ) { let weakObserver = WeakObject(value:observer) m_observerDict.removeValueForKey(weakObserver) }
下面是如何使@ GoZoner的很好的答案符合Hashable
,所以它可以在容器对象索引,如: Set
, Dictionary
, Array
等。
private class Weak<T: AnyObject>: Hashable { weak var value : T! init (value: T) { self.value = value } var hashValue : Int { // ObjectIdentifier creates a unique hashvalue for objects. return ObjectIdentifier(self.value).hashValue } } // Need to override so we can conform to Equitable. private func == <T>(lhs: Weak<T>, rhs: Weak<T>) -> Bool { return lhs.hashValue == rhs.hashValue }
其他答案已经涵盖了generics的angular度。 以为我会分享一些覆盖nil
angular度的简单代码。
我想要一个静态数组(偶尔读取)当前存在于应用程序中的所有Label
,但不想看到旧的旧Label
在哪里。
没什么奇特的,这是我的代码…
public struct WeakLabel { public weak var label : Label? public init(_ label: Label?) { self.label = label } } public class Label : UILabel { static var _allLabels = [WeakLabel]() public static var allLabels:[WeakLabel] { get { _allLabels = _allLabels.filter{$0.label != nil} return _allLabels.filter{$0.label != nil}.map{$0.label!} } } public required init?(coder: NSCoder) { super.init(coder: coder) Label._allLabels.append(WeakLabel(self)) } public override init(frame: CGRect) { super.init(frame: frame) Label._allLabels.append(WeakLabel(self)) } }
对同样的问题的另一个解决scheme…这个的重点是存储一个对象的弱引用,但也允许你存储一个结构。
[我不确定它是多么有用,但它需要一段时间才能得到正确的语法]
class WeakWrapper : Equatable { var valueAny : Any? weak var value : AnyObject? init(value: Any) { if let valueObj = value as? AnyObject { self.value = valueObj } else { self.valueAny = value } } func recall() -> Any? { if let value = value { return value } else if let value = valueAny { return value } return nil } } func ==(lhs: WeakWrapper, rhs: WeakWrapper) -> Bool { return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) } class Stuff {} var weakArray : [WeakWrapper] = [WeakWrapper(value: Stuff()), WeakWrapper(value: CGRectZero)] extension Array where Element : WeakWrapper { mutating func removeObject(object: Element) { if let index = self.indexOf(object) { self.removeAtIndex(index) } } mutating func compress() { for obj in self { if obj.recall() == nil { self.removeObject(obj) } } } } weakArray[0].recall() weakArray[1].recall() == nil weakArray.compress() weakArray.count
你可以创buildArray
包装。 或者使用这个库https://github.com/NickRybalko/WeakPointerArray let array = WeakPointerArray<AnyObject>()
它是types安全的。