Как мне объявить массив слабых ссылок в Swift?

152

Я хочу сохранить массив слабых ссылок в Swift. Сам массив не должен быть слабой ссылкой - его элементы должны быть. Я думаю, что Cocoa NSPointerArray предлагает нестандартную версию этого.

  • 1
    Как насчет создания объекта-контейнера, который слабо ссылается на другой объект, а затем создания массива из них? (Если вы не получите лучший ответ)
  • 1
    почему вы не используете NSPointerArray?
Показать ещё 6 комментариев
Теги:
weak-references
automatic-ref-counting
nspointerarray

16 ответов

128
Лучший ответ

Создайте общую оболочку как:

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 допускает расширение, определенное как таковое.

  • 9
    Как удалить объекты-оболочки из массива, когда их значение освобождено?
  • 0
    Я на самом деле просто наткнулся на эту же идею. К сожалению, это похоже на сбой компилятора. Я могу заставить его работать, только если исключу универсальный параметр. Я исследую больше, а затем, возможно, отправлю сообщение об ошибке.
Показать ещё 19 комментариев
40

Вы можете использовать NSHashTable со значением weakObjectsHashTable. NSHashTable.weakObjectsHashTable()

Для Swift 3: NSHashTable.weakObjects()

Справка класса NSHashTable

Доступно в OS X v10.5 и более поздних версиях.

Доступно в iOS 6.0 и более поздних версиях.

  • 0
    Лучший ответ и не трать время на фантики!
  • 1
    Это умно, но, как и ответ GoZoner, это не работает с типами Any но не AnyObject , такими как протоколы.
Показать ещё 3 комментария
10

Это вроде поздно для вечеринки, но попробуйте мой. Я реализовал как набор не массив.

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. Моя быстрая версия Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29).

Код можно взять с Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39

** ДОБАВЛЕН В NOV.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: $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
    }
}

Затем замените 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 выходят за рамки этого контекста, но я был бы признателен, если бы кто-то понял эту проблему.

  • 0
    Спасибо тебе за это. работает довольно хорошо
  • 0
    Хорошо, сэр, вы волшебник. благодарю вас.
Показать ещё 6 комментариев
10

Это не мое решение. Я нашел его на форумах разработчиков 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]
  • 1
    странно, но больше не работает со структурами. Говорит EXC_BAD_ACCESS для меня. С классом работает просто отлично
  • 5
    Структуры являются типами значений, они не должны работать с ними. Тот факт, что он завис во время выполнения, а не во время компиляции, является ошибкой компилятора.
9

Вы можете сделать это, создав объект-оболочку, чтобы удерживать слабый указатель.

struct WeakThing<T: AnyObject> {
  weak var value: T?
  init (value: T) {
    self.value = value
  }
}

И затем используя их в массиве

var weakThings = WeakThing<Foo>[]()
  • 0
    Должен быть class чтобы использовать weak переменные
  • 3
    Говорит кто? Код выше работает хорошо для меня. Единственное требование состоит в том, что объект, становящийся слабым, должен быть классом, а не объектом, содержащим слабую ссылку
Показать ещё 5 комментариев
7

У меня была та же идея создать слабый контейнер с дженериками.
В результате я создал обертку для 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().

7

Как насчет оболочки функционального стиля?

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() }))
  • 3
    Вопрос в том, как создать массив (или, скажем, Set) слабых объектов.
  • 0
    С помощью этого решения вы даже можете создать массив с несколькими значениями, такими как var array: [(x: Int, y: () -> T?)] . Именно то, что я искал.
Показать ещё 2 комментария
3

Существующий пример WeakContainer полезен, но на самом деле это не помогает использовать слабые ссылки в существующих быстрых контейнерах, таких как списки и словари.

Если вы хотите использовать методы List, такие как содержит, то 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)
}
2

На основе Kaz Yoshikawa ответьте

Подробнее

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")
    }
}

Rand func

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
}

Результат

Изображение 1813

2

Здесь, как сделать отличный ответ @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
}
1

Еще одно решение одной и той же проблемы... основное внимание в этом вопросе заключается в сохранении слабой ссылки на объект, но позволяющей также хранить структуру.

[Я не уверен, насколько это полезно, но для получения синтаксиса это заняло некоторое время]

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
1

Другие ответы охватывают угол генериков. Думаю, я бы разделил простой код, охватывающий угол 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))
    }
}
  • 0
    Как насчет использования flatMap вместо filter и map ?
0

Это мое решение:

  • Очистите массив при освобождении, потому что WeakObjectSet хранит и не выходит из WeakObject
  • Устраните фатальную ошибку при обнаружении дубликата элемента в наборе

-

// 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)) }
    }
}
0

Поскольку 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 поэтому у меня нет точного кода под рукой).

0

Я основал это на работе @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
0

Вы можете создать обертку вокруг Array. Или используйте эту библиотеку https://github.com/NickRybalko/WeakPointerArray let array = WeakPointerArray<AnyObject>() Это безопасный тип.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню