我想在 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 语言不允许这样定义扩展。
您可以将 NSHashTable 与 weakObjectsHashTable 一起使用。 NSHashTable<ObjectType>.weakObjectsHashTable()
对于 Swift 3:NSHashTable<ObjectType>.weakObjects()
在 OS X v10.5 及更高版本中可用。在 iOS 6.0 及更高版本中可用。
Any
而不是 AnyObject
的类型,例如协议。
MyProtocol: class
和 NSHashTable<MyProtocol>.weakObjects()
的编译器错误。 “'NSHashTable' 要求 'MyProtocol' 是一个类类型。
函数式编程方法
不需要额外的课程。
只需定义一组闭包 () -> Foo?
并使用 [weak foo]
将 foo 实例捕获为弱。
let foo = Foo()
var foos = [() -> Foo?]()
foos.append({ [weak foo] in return foo })
foos.forEach { $0()?.doSomething() }
filter { $0() != nil }
WeakArray<T> = [() -> T?]
派对有点晚了,但试试我的。我实现为 Set 而不是 Array。
弱对象集
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
** 添加于 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
它说问题是RESOLVED
,但我想知道这是否仍然与此问题有关。无论如何, MyString 或 NSString 之间的行为差异超出了此上下文,但如果有人解决了这个问题,我将不胜感激。
Set
中删除 nil
值。因此,我添加了顶部答案中提到的 reap()
函数,并确保每次访问 WeakObjectSet
时都调用 reap()
。
nil
时,弱引用不会立即被释放
NSString
不能。
UnsafeMutablePointer<T>(&object)
返回的指针可以随机变化(与 withUnsafePointer
相同)。我现在使用由 NSHashTable
支持的版本:gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d。
这不是我的解决方案。 I found it on the Apple Developer Forums。
@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]
EXC_BAD_ACCESS
。上课就好了
您可以通过创建一个包装对象来保存弱指针来做到这一点。
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
然后在数组中使用这些
var weakThings = WeakThing<Foo>[]()
class
才能使用 weak
变量
protocol Protocol : class { ... }
功能样式包装器怎么样?
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 }
var array: [(x: Int, y: () -> T?)]
。正是,我在寻找什么。
let values = Array(array1.map({ $0() })) part
。由于这不再是具有弱引用的闭包数组,因此值将被保留,直到该数组被释放。如果我是正确的,那么重要的是要注意你永远不应该像 self.items = Array(array1.map({ $0() }))
这样保留这个数组,因为这超出了目的。
细节
斯威夫特 5.1,Xcode 11.3.1
解决方案
struct WeakObject<Object: AnyObject> { weak var object: Object? }
选项1
@propertyWrapper
struct WeakElements<Collect, Element> where Collect: RangeReplaceableCollection, Collect.Element == Optional<Element>, Element: AnyObject {
private var weakObjects = [WeakObject<Element>]()
init(wrappedValue value: Collect) { save(collection: value) }
private mutating func save(collection: Collect) {
weakObjects = collection.map { WeakObject(object: $0) }
}
var wrappedValue: Collect {
get { Collect(weakObjects.map { $0.object }) }
set (newValues) { save(collection: newValues) }
}
}
选项 1 用法
class Class1 { // or struct
@WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
选项 2
struct WeakObjectsArray<Object> where Object: AnyObject {
private var weakObjects = [WeakObject<Object>]()
}
extension WeakObjectsArray {
typealias SubSequence = WeakObjectsArray<Object>
typealias Element = Optional<Object>
typealias Index = Int
var startIndex: Index { weakObjects.startIndex }
var endIndex: Index { weakObjects.endIndex }
func index(after i: Index) -> Index { weakObjects.index(after: i) }
subscript(position: Index) -> Element {
get { weakObjects[position].object }
set (newValue) { weakObjects[position] = WeakObject(object: newValue) }
}
var count: Int { return weakObjects.count }
var isEmpty: Bool { return weakObjects.isEmpty }
}
extension WeakObjectsArray: RangeReplaceableCollection {
mutating func replaceSubrange<C : Collection>( _ subrange: Range<Index>, with newElements: C) where Element == C.Element {
weakObjects.replaceSubrange(subrange, with: newElements.map { WeakObject(object: $0) })
}
}
选项 2 用法
class Class2 { // or struct
var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
完整样本
不要忘记粘贴解决方案代码
import UIKit
class ViewController: UIViewController {
@WeakElements var weakObjectsArray = [UIView?]()
//var weakObjectsArray = WeakObjectsArray<UIView>()
override func viewDidLoad() {
super.viewDidLoad()
addSubviews()
}
private func printArray(title: String) {
DispatchQueue.main.async {
print("=============================\n\(title)\ncount: \(self.weakObjectsArray.count)")
self.weakObjectsArray.enumerated().forEach { print("\($0) \(String(describing: $1))") }
}
}
}
extension ViewController {
private func createRandomRectangleAndAdd(to parentView: UIView) -> UIView {
let view = UIView(frame: CGRect(x: Int.random(in: 0...200),
y: Int.random(in: 60...200),
width: Int.random(in: 0...200),
height: Int.random(in: 0...200)))
let color = UIColor(red: CGFloat.random(in: 0...255)/255,
green: CGFloat.random(in: 0...255)/255,
blue: CGFloat.random(in: 0...255)/255,
alpha: 1)
view.backgroundColor = color
parentView.addSubview(view)
return view
}
private func addSubviews() {
(0...1).forEach { _ in addView() }
addButtons()
}
private func createButton(title: String, frame: CGRect, action: Selector) -> UIButton {
let button = UIButton(frame: frame)
button.setTitle(title, for: .normal)
button.addTarget(self, action: action, for: .touchUpInside)
button.setTitleColor(.blue, for: .normal)
return button
}
private func addButtons() {
view.addSubview(createButton(title: "Add",
frame: CGRect(x: 10, y: 20, width: 40, height: 40),
action: #selector(addView)))
view.addSubview(createButton(title: "Delete",
frame: CGRect(x: 60, y: 20, width: 60, height: 40),
action: #selector(deleteView)))
view.addSubview(createButton(title: "Remove nils",
frame: CGRect(x: 120, y: 20, width: 100, height: 40),
action: #selector(removeNils)))
}
@objc func deleteView() {
view.subviews.first { view -> Bool in return !(view is UIButton) }?
.removeFromSuperview()
printArray(title: "First view deleted")
}
@objc func addView() {
weakObjectsArray.append(createRandomRectangleAndAdd(to: view))
printArray(title: "View addded")
}
@objc func removeNils() {
weakObjectsArray = weakObjectsArray.filter { $0 != nil }
printArray(title: "Remove all nil elements in weakArray")
}
}
protocol TP: class { } class TC { var a = WeakArray<TP>() var b = WeakObjectsArray<TP>() }
protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
delegates
这样的数组的单例。然后你会有一些想要使用这个功能的视图控制器。您希望调用 MyManager.delegates.append(self)
。但是如果 MyManager
被锁定为某个泛型类型,那么这不是很有用。
protocol TP: class { } class MyManager { typealias Delegate = AnyObject & TP static var delegates = [Delegate?]() } class A: TP { } class B: TP { } //MyManager.delegates.append(A()) //MyManager.delegates.append(B())
我有同样的想法来创建使用泛型的弱容器。
结果我为 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()
断言的额外保护。
由于 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
前面的工作量更大,但其余代码中的用法更清晰 IMO。如果你想让它更像数组,你甚至可以实现下标,使它成为 SequenceType
等(但我的项目只需要 append
和 forEach
所以我手头没有确切的代码)。
get
函数中的索引
WeakContainer 的现有示例很有帮助,但它并不能真正帮助在现有的 swift 容器(如列表和字典)中使用弱引用。
如果你想使用 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)
}
以下是如何使 @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
}
同一问题的另一种解决方案......这个问题的重点是存储对对象的弱引用,但也允许您存储结构。
[我不确定它有多大用处,但确实需要一段时间才能使语法正确]
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
您可以围绕 Array
创建包装器。或者使用这个库 https://github.com/NickRybalko/WeakPointerArray let array = WeakPointerArray<AnyObject>()
它是类型安全的。
这是一个类型安全的集合,包含弱对象的容器。它还会在访问时自动删除容器/包装器。
例子:
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 }
}
}
在许多情况下,返回可取消的会更干净。这允许调用站点决定何时显式销毁值(以及通过 descoping 隐式销毁):
public protocol Cancellable {
func cancel()
}
private struct MyValue: Identifiable {
let id: String
// ...
}
private class CancellationHandler: Cancellable {
let handler: () -> ()
init(handler: @escaping () -> ()) { self.handler = handler }
func cancel() { handler() }
deinit { handler() }
}
public class Container {
private var array = [MyType]()
public func add() -> Cancellable {
let value = MyValue(...)
array.append(value)
return CancellationHandler {
array.removeFirst(where: { $0.id == value.id })
}
}
}
let cancellable = container.add()
// Both cancellable.cancel() and the cancellable descoping
// will call the `cancel` function, removing the value from array.
其他答案涵盖了泛型角度。以为我会分享一些涵盖 nil
角度的简单代码。
我想要一个包含应用程序中当前存在的所有 Label
的静态数组(偶尔读取),但不想看到旧的 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))
}
}
flatMap
代替 filter
怎么样? map
?
我基于@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(WeakObject(object: object))
}
public func addObjects(_ objects: [T]) {
objects.forEach { self.allSetObjects.insert(WeakObject(object: $0)) }
}
}
感谢这里提供的所有建议解决方案。由于强数组引用,我遇到了同样的内存泄漏问题,并且对象包装器解决方案运行良好。一个注意事项;我看到了通过将数组替换为过滤数组来压缩数组的示例。到目前为止,我更喜欢使用 removeAll 方法,例如,receiveLinks.removeAll( where: { $0.receiveLink == receiveLink } ) 我一直假设 Array 使用 NSMutableArray 并且替换 Array 对象与从中删除元素不同数组,尤其是当数组有多个引用时。在我看来, removeAll 会更有效率。
不定期副业成功案例分享
count
怎么办?Stuff
替换为协议则失败;见this related question