我想在Swift中存储一个弱引用数组。数组本身不应该是弱引用——它的元素应该是。我认为Cocoa NSPointerArray提供了一个非类型安全的版本。
当前回答
现在参加派对有点晚了,但试试我的吧。我实现为一个集合而不是数组。
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之间的行为差异超出了这个上下文,但如果有人能解决这个问题,我会很感激。
其他回答
其他的回答涵盖了泛型的角度。我想分享一些涵盖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))
}
}
现有的WeakContainer示例很有帮助,但它并不能真正帮助在现有的快速容器(如列表和字典)中使用弱引用。
如果您想使用List方法,如contains,那么WeakContainer将需要实现Equatable。因此,我添加了允许WeakContainer是相等的代码。
如果您想在字典中使用WeakContainer,我还将其设置为可哈希的,以便可以将其用作字典键。
我还将其重命名为WeakObject,以强调这仅适用于类类型,并将其与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)
}
我基于@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
由于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: index) {
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
它在前面做了更多的工作,但在你的代码的其余部分的使用是更干净的我。如果你想让它更像数组,你甚至可以实现下标,使它成为一个SequenceType等(但我的项目只需要append和forEach,所以我手头没有确切的代码)。
这是一个类型安全的集合,用于保存弱对象的容器。当它被访问时,它也会自动移除nil容器/包装器。
例子:
protocol SomeDelegate: class {
func doSomething()
}
class SomeViewController: UIViewController {
var delegates: WeakCollection<SomeDelegate> = []
func someFunction(delegate: SomeDelegate) {
delegates.append(delegate)
}
func runDelegates() {
delegates.forEach { $0.doSomething() }
}
}
自定义集合https://gist.github.com/djk12587/46d85017fb3fad6946046925f36cefdc
import Foundation
/**
Creates an array of weak reference objects.
- Important:
Because this is an array of weak objects, the objects in the array can be removed at any time.
The collection itself will handle removing nil objects (garbage collection) via the private function cleanUpNilContainers()
*/
class WeakCollection<T>: RangeReplaceableCollection, ExpressibleByArrayLiteral {
typealias Index = Int
typealias Element = T
typealias Iterator = IndexingIterator<[Element]>
private var weakContainers: [WeakReferenceContainer]
required convenience init(arrayLiteral: Element...) {
self.init()
self.weakContainers = WeakCollection.createWeakContainers(from: arrayLiteral)
}
required init() {
weakContainers = []
}
required init<S>(_ elements: S) where S: Sequence, WeakCollection.Element == S.Element {
self.weakContainers = WeakCollection.createWeakContainers(from: elements)
}
static private func createWeakContainers<S>(from weakCollection: S) -> [WeakReferenceContainer] where S: Sequence,
WeakCollection.Element == S.Element {
return weakCollection.compactMap { WeakReferenceContainer(value: $0 as AnyObject) }
}
func append<S>(contentsOf newElements: S) where S: Sequence, WeakCollection.Element == S.Element {
self.weakContainers.append(contentsOf: WeakCollection.createWeakContainers(from: newElements))
}
var startIndex: Index {
return references.startIndex
}
var endIndex: Index {
return references.endIndex
}
func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where
C: Collection, R: RangeExpression, WeakCollection.Element == C.Element, WeakCollection.Index == R.Bound {
weakContainers.replaceSubrange(subrange, with: WeakCollection.createWeakContainers(from: newElements))
}
func index(after i: Int) -> Int {
return references.index(after: i)
}
func makeIterator() -> IndexingIterator<[Element]> {
return references.makeIterator()
}
subscript(index: Int) -> Element {
get {
return references[index]
}
set {
weakContainers[index] = WeakReferenceContainer(value: newValue as AnyObject)
}
}
}
extension WeakCollection {
private class WeakReferenceContainer {
private(set) weak var value: AnyObject?
init(value: AnyObject?) {
self.value = value
}
}
private func cleanUpNilContainers() {
weakContainers = weakContainers.compactMap { $0.value == nil ? nil : $0 }
}
private var references: [Element] {
cleanUpNilContainers()
return weakContainers.compactMap { $0.value as? T }
}
}
推荐文章
- 如何测试等价的Swift枚举与相关的值
- 在实现API时,我如何避免在块中捕获自我?
- 如何创建一个Swift Date对象?
- 在Swift中转换Float为Int
- 快速提取正则表达式匹配
- 如何应用梯度的背景视图的iOS Swift应用程序
- 我如何在Swift中声明一个弱引用数组?
- 如何用SwiftUI调整图像大小?
- 如何分配一个行动UIImageView对象在Swift
- 在iOS8中使用Swift更改特定视图控制器的状态栏颜色
- 打印一个可变内存地址在swift
- 我如何使一个enum可解码在Swift?
- HTTP请求在Swift与POST方法
- 获取用户当前位置/坐标
- 在SwiftUI中创建一个VStack填充屏幕宽度