我想在Swift中存储一个弱引用数组。数组本身不应该是弱引用——它的元素应该是。我认为Cocoa NSPointerArray提供了一个非类型安全的版本。
当前回答
下面是如何使@GoZoner的伟大的答案符合哈希,所以它可以在容器对象中索引:集,字典,数组等。
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
}
其他回答
可以通过创建包装器对象来保存弱指针来实现这一点。
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
然后在数组中使用这些
var weakThings = WeakThing<Foo>[]()
其他的回答涵盖了泛型的角度。我想分享一些涵盖nil角度的简单代码。
我想要一个静态数组(偶尔读)的所有标签,目前存在于应用程序,但不想看到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))
}
}
现在参加派对有点晚了,但试试我的吧。我实现为一个集合而不是数组。
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不会接受String类型,而是接受NSString类型。因为,String类型不是AnyType。我的swift版本是Apple swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)。
代码可以从Gist中获取。 https://gist.github.com/codelynx/30d3c42a833321f17d39
**于2017年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
}
}
然后像这样用MyString替换NSString。那么说它有效就很奇怪了。
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之间的行为差异超出了这个上下文,但如果有人能解决这个问题,我会很感激。
函数式样式包装器呢?
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() }))
实际上,你不需要一个函数来创建闭包。直接捕获一个对象。
let captured3 = { [weak obj3] in return obj3 }
函数式编程方法
不需要额外的课程。
简单地定义一个闭包数组()-> Foo?并使用[weak foo]将foo实例捕获为弱实例。
let foo = Foo()
var foos = [() -> Foo?]()
foos.append({ [weak foo] in return foo })
foos.forEach { $0()?.doSomething() }
推荐文章
- 如何测试等价的Swift枚举与相关的值
- 在实现API时,我如何避免在块中捕获自我?
- 如何创建一个Swift Date对象?
- 在Swift中转换Float为Int
- 快速提取正则表达式匹配
- 如何应用梯度的背景视图的iOS Swift应用程序
- 我如何在Swift中声明一个弱引用数组?
- 如何用SwiftUI调整图像大小?
- 如何分配一个行动UIImageView对象在Swift
- 在iOS8中使用Swift更改特定视图控制器的状态栏颜色
- 打印一个可变内存地址在swift
- 我如何使一个enum可解码在Swift?
- HTTP请求在Swift与POST方法
- 获取用户当前位置/坐标
- 在SwiftUI中创建一个VStack填充屏幕宽度