如何在Swift中声明一个弱引用数组?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Swift中声明一个弱引用数组?相关的知识,希望对你有一定的参考价值。
我想在Swift中存储一组弱引用。数组本身不应该是弱引用 - 它的元素应该是。我认为Cocoa NSPointerArray
提供了非类型安全版本。
创建一个通用包装器:
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语言允许扩展定义为这样。
以下是如何使@ GoZoner的答案符合Hashable
,因此它可以在Container对象中编入索引,例如: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
}
其他答案涵盖了泛型角度。以为我会分享一些涵盖nil
角度的简单代码。
我想要一个当前存在于应用程序中的所有Label
s的静态数组(偶尔读取),但是不想看到旧的那些曾经是nil
的地方。
没什么好看的,这是我的代码......
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))
}
}
针对同一问题的又一个解决方案......这个问题的重点是存储对象的弱引用,但也允许存储结构。
[我不确定它有多有用,但确实需要一段时间才能使语法正确]
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
由于NSPointerArray
已经自动处理了大部分内容,因此我通过为其设置类型安全包装来解决问题,这避免了其他答案中的许多样板:
class WeakArray<T: AnyObject> {
private let pointers = NSPointerArray.weakObjects()
init (_ elements: T...) {
elements.forEach{self.pointers.addPointer(Unmanaged.passUnretained($0).toOpaque())}
}
func get (_ index: Int) -> T? {
if index < self.pointers.count, let pointer = self.pointers.pointer(at: 0) {
return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
} else {
return nil
}
}
func append (_ element: T) {
self.pointers.addPointer(Unmanaged.passUnretained(element).toOpaque())
}
func forEach (_ callback: (T) -> ()) {
for i in 0..<self.pointers.count {
if let element = self.get(i) {
callback(element)
}
}
}
// implement other functionality as needed
}
用法示例:
class Foo {}
var foo: Foo? = Foo()
let array = WeakArray(foo!)
print(array.get(0)) // Optional(Foo)
foo = nil
DispatchQueue.main.async{print(array.get(0))} // nil
它预先做的更多,但在其余代码中的使用更加清晰IMO。如果你想让它更像数组,你甚至可以实现下标,使它成为SequenceType
等(但我的项目只需要append
和forEach
所以我手头没有确切的代码)。
你可以在Array
周围创建包装器。或使用此库https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
它是类型安全的。
我基于@Eonil的工作,因为我喜欢闭包弱绑定策略,但我不想使用函数运算符来变量,因为它感觉非常反直觉
相反,我所做的如下:
class Weak<T> where T: AnyObject {
fileprivate var storedWeakReference: ()->T? = { return nil }
var value: T? {
get {
return storedWeakReference()
}
}
init(_ object: T) {
self.storedWeakReference = storeWeakReference(object)
}
fileprivate func storeWeakReference<T> (_ target:T) -> ()->T? where T: AnyObject {
return { [weak target] in
return target
}
}
}
通过这种方式,您可以执行以下操作:
var a: UIViewController? = UIViewController()
let b = Weak(a)
print(a) //prints Optional(<UIViewController: 0xSomeAddress>)
print(b.value) //prints Optional(<UIViewController: 0xSomeAddress>)
a = nil
print(a) //prints nil
print(b.value) //prints nil
我的解决方案:
- 解除分配时清理数组,因为WeakObjectSet正在存储而不是从WeakObject中删除
- 在Set中找到重复元素时解决致命错误
--
// MARK: - WeakObjectSet
public class WeakObject<T: AnyObject>: Equatable, Hashable {
// MARK: Public propreties
public weak var object: T?
public var hashValue: Int {
return self.identifier.hashValue
}
// MARK: Private propreties
private let identifier: ObjectIdentifier
// MARK: Initializer
public init(object: T) {
self.identifier = ObjectIdentifier(object)
self.object = object
}
public static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
return lhs.identifier == rhs.identifier
}
}
// MARK: - WeakObjectSet
public class WeakObjectSet<T: AnyObject> {
// MARK: Public propreties
public var allObjects: [T] {
return allSetObjects.compactMap { $0.object }
}
// MARK: Private propreties
private var objects: Set<WeakObject<T>>
private var allSetObjects: Set<WeakObject<T>> {
get {
objects = self.objects.filter { $0.object != nil }
return objects
}
set {
objects.formUnion(newValue.filter { $0.object != nil })
}
}
// MARK: Initializer
public init() {
self.objects = Set<WeakObject<T>>([])
}
public init(objects: [T]) {
self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
}
// MARK: Public function
public func contains(_ object: T) -> Bool {
return self.allSetObjects.contains(WeakObject(object: object))
}
public func addObject(_ object: T) {
self.allSetObjects.insert(WeakObjec以上是关于如何在Swift中声明一个弱引用数组?的主要内容,如果未能解决你的问题,请参考以下文章
翻译: Swift 中的委托保留周期 如何在“纯”Swift(没有@objc)中进行弱协议引用