15 ответов:
создать общую оболочку как:
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 != .value } } }использование
AnyObjectвыше следует заменитьT- но я не думаю, что текущий язык Swift позволяет расширение, определенное как таковое.
Вы можете использовать NSHashTable с weakObjectsHashTable.
NSHashTable.weakObjectsHashTable()Для Swift 3:
NSHashTable.weakObjects()доступно в OS X v10.5 и более поздних версиях.
доступно в iOS 6.0 и более поздних версиях.
Это не мое решение. Я нашел его на форумах разработчиков Apple.
@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]
поздновато, но попробую. Я реализовал как набор не массив.
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: ) }) } var allObjects: [T] { return objects.flatMap { .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: ) }) } }использование
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 не будет принимать строковый тип, но NSString. Потому что строковый тип не является AnyType. Моя быстрая версия -
Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29).код может быть схвачен из Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39
** ДОБАВЛЕНО В НОЯБРЬ.2017
я обновил код до 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: ) }) } var allObjects: [T] { return objects.flatMap { .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: ) }) } }как упоминал гекеджи, я понял, что NSString не будет освобожден на основе кода в использовании. Я почесал в затылке и написал свой класс String следующим образом.
// 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 } }затем заменить
NSStringСMyStringтакой. Тогда странно говорить, что это работает.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 выходят за рамки этого контекста, но я был бы признателен, если бы кто-то понял эту проблему.
вы можете сделать это, создав объект-оболочку для хранения слабого указателя.
struct WeakThing<T: AnyObject> { weak var value: T? init (value: T) { self.value = value } }а затем использовать их в массиве
var weakThings = WeakThing<Foo>[]()
у меня была такая же идея создать слабый контейнер с дженериками.
В результате я создал обертку для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()утверждения.
как насчет функционального стиля обертки?
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({ () }))
существующий пример WeakContainer полезен, но на самом деле он не помогает использовать слабые ссылки в существующих контейнерах swift, таких как списки и словари.
Если вы хотите использовать методы списка, такие как contains, то WeakContainer должен будет реализовать Equatable. Поэтому я добавил код, чтобы позволить WeakContainer быть равным.
в случае, если вы хотели использовать WeakContainer в словарях, я также сделал это hashable поэтому он может быть использован в качестве словаря ключи.
Я также переименовал его в 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, поэтому он может быть проиндексирован в объекты контейнерного типа: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 }
на основе Каз Есикава ответ
подробности
xCode 9.1, Swift 4
решение
WeakObject
import Foundation protocol WeakObjectProtocol { associatedtype WeakObjectType var value: WeakObjectType? {get set} init(object: WeakObjectType) } class WeakObject<T: AnyObject>: WeakObjectProtocol { typealias WeakObjectType = T weak var value: WeakObjectType? required init(object: WeakObjectType) { self.value = object } var referenceCount: Int { return CFGetRetainCount(value) } } extension WeakObject: Equatable { static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool { return lhs.value === rhs.value } } extension WeakObject: Hashable { var hashValue: Int { if var value = value { return UnsafeMutablePointer<T>(&value).hashValue } return 0 } } extension WeakObject: CustomStringConvertible { var description: String { if let value = value { let className = String(describing: type(of: value.self)) return "{class: \(className); referenceCount: \(referenceCount)}" } return "nil" } }расширение массива
import Foundation extension Array where Element: AnyObject { var weak: Array<WeakObject<Element>> { var weakArray = [WeakObject<Element>]() for item in self { let obj = WeakObject(object: item) weakArray.append(obj) } return weakArray } } extension Array where Element: WeakObjectProtocol { typealias EnumeratedWeakObjectClosure = (_ index: Int, _ value: Element.WeakObjectType?)->() typealias WeakObjectClosure = (_ value: Element.WeakObjectType?)->() mutating func removeNils() { self = self.flatMap{ (element) -> Element? in if element.value == nil { return nil } return element } } mutating func append(weakValue: Element.WeakObjectType) { append(Element(object: weakValue)) } subscript(index: Int) -> Element.WeakObjectType? { get { return self[index].value } } func `for` (closure: WeakObjectClosure){ for item in self { closure(item.value) } } func forEnumerated (closure: EnumeratedWeakObjectClosure) { for (index,item) in self.enumerated() { closure(index, item.value) } } mutating func remove(index: Int, closure: EnumeratedWeakObjectClosure) { closure(index, self[index].value) remove(at: index) } mutating func remove(index: Int, closure: WeakObjectClosure) { closure(self[index].value) remove(at: index) } }использование
// Array of week objects var weakArray = [WeakObject<UIView>]() // Get array of week objects (transfom from [AnyObject]) // way 1 weakArray = view.subviews.weak // way 2 weakArray = [view.subviews[0], view.subviews[1]].weak // Add single element to the end of the array weakArray.append(weakValue: UIView()) // For loop weakArray.for { (element) in print("\(String(describing: element))") } // For loop with index (position number) weakArray.forEnumerated { (index, element) in print("\(index) \(String(describing: element))") }Полная Выборка
не забудьте добавить код решения здесь
ViewController
import UIKit class ViewController: UIViewController { var weakArray = [WeakObject<UIView>]() override func viewDidLoad() { super.viewDidLoad() addSubviews() weakArray = view.subviews.weak weakArray.append(weakValue: generateView()) weakArray.remove(index: 0) { item in item?.removeFromSuperview() } weakArray.for { (element) in print("\(String(describing: element))") } } func printArray(title: String) { print("=============================\n\(title)\ncount: \(weakArray.count)") weakArray.forEnumerated { (index,element) in print("\(index) \(String(describing: element))") } } } // Creating views extension ViewController { func generateView() -> UIView { let randomValue: ()->(CGFloat) = { return CGFloat(rand[50, 300]) } let view = UIView(frame: CGRect(x: randomValue(), y: randomValue(), width: randomValue(), height: randomValue())) view.backgroundColor = .blue let randomColorComponent: ()->(CGFloat) = { return CGFloat(rand[0, 255])/CGFloat(255) } let color = UIColor(red: randomColorComponent(), green: randomColorComponent(), blue: randomColorComponent(), alpha: 1) view.backgroundColor = color self.view.addSubview(view) return view } func addSubviews() { _ = generateView() _ = generateView() addButtons() } } // Buttons extension ViewController { func addButtons() { var button = UIButton(frame: CGRect(x: 10, y: 20, width: 40, height: 40)) button.setTitle("Add", for: .normal) button.addTarget(self, action: #selector(addView), for: .touchUpInside) button.setTitleColor(.blue, for: .normal) view.addSubview(button) button = UIButton(frame: CGRect(x: 60, y: 20, width: 60, height: 40)) button.setTitle("Delete", for: .normal) button.addTarget(self, action: #selector(deleteView), for: .touchUpInside) button.setTitleColor(.blue, for: .normal) view.addSubview(button) button = UIButton(frame: CGRect(x: 120, y: 20, width: 100, height: 40)) button.setTitle("Remove nil", for: .normal) button.addTarget(self, action: #selector(removeNils), for: .touchUpInside) button.setTitleColor(.blue, for: .normal) view.addSubview(button) } @objc func deleteView() { view.subviews.filter { view -> Bool in return !(view is UIButton) }.first?.removeFromSuperview() DispatchQueue.main.async { self.view.layoutIfNeeded() self.printArray(title: "First view deleted") } } @objc func addView() { weakArray.append(weakValue: generateView()) printArray(title: "View addded") } @objc func removeNils() { weakArray.removeNils() printArray(title: "Remove all nil elements in weakArray") } }class Random { subscript<T>(_ min: T, _ max: T) -> T where T : BinaryInteger { get { return rand(min-1, max+1) } } } let rand = Random() func rand<T>(_ min: T, _ max: T) -> T where T : BinaryInteger { let _min = min + 1 let difference = max - _min return T(arc4random_uniform(UInt32(difference))) + _min }результат
другие ответы охватили угол дженериков. Думал, что я поделюсь каким-то простым кодом, охватывающим
nilугол.Я хотел статический массив (читать Иногда) всех
Labels, которые в настоящее время существуют в приложении, но не хотят видеть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{.label != nil} return _allLabels.filter{.label != nil}.map{.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
Вы можете создать обертку вокруг
Array. Или используйте эту библиотеку https://github.com/NickRybalko/WeakPointerArraylet 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
С
NSPointerArrayуже обрабатывает большую часть этого автоматически, я решил проблему, сделав для нее типобезопасную обертку, которая позволяет избежать многих шаблонных ответов в других ответах:class WeakArray<T: AnyObject> { private let pointers = NSPointerArray.weakObjects() init (_ elements: T...) { elements.forEach{self.pointers.addPointer(Unmanaged.passUnretained().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так что я нет точного кода на руках).

Comments