Swift에서 약한 참조 배열을 어떻게 선언합니까?


179

Swift에 약한 참조 배열을 저장하고 싶습니다. 배열 자체는 약한 참조가 아니어야합니다. Cocoa NSPointerArray는 형식이 안전하지 않은 버전을 제공 한다고 생각 합니다.


1
다른 객체를 약하게 참조하는 컨테이너 객체를 만든 다음 배열을 만드는 것은 어떻습니까? (더 나은 답변을 얻지
못하면

1
왜 NSPointerArray를 사용하지 않습니까?
Bastian

@nielsbot 그것은 오래된 obj-c 솔루션입니다 :) Swifty로 만들려면 일반적인 객체 여야합니다! :) 그러나 실제 문제는 참조 된 객체가 할당 해제 될 때 배열에서 객체를 제거하는 방법입니다.
Sulthan

2
맞습니다. 매개 변수 유형이있는 것을 선호합니다. NSPointerArray 주위에 매개 변수가있는 래퍼를 만들 수는 있지만 대안이 있는지 확인하고 싶었습니다.
Bill

6
다른 옵션과 마찬가지로 NSHashTable이 존재합니다. 기본적으로 NSSet이며 포함 된 객체를 참조하는 방법을 지정할 수 있습니다.
Mick MacCallum

답변:


154

다음과 같이 일반 랩퍼를 작성하십시오.

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 언어에서 이와 같이 정의 된 확장을 허용하지 않는다고 생각합니다.


11
값이 할당 해제 될 때 배열에서 래퍼 객체를 어떻게 제거합니까?
Sulthan

9
예, 컴파일러가 다운되었습니다.
GoZoner 2016 년

5
새 질문에 문제 코드를 게시하십시오. 코드 될 때 내 대답을 딩 할 이유가 없습니다 !
GoZoner

2
@EdGamble 제공된 코드는 그대로 작동하지만 클래스 Stuff를 프로토콜 로 바꾸면 실패 합니다. 이 관련 질문을
Theo

2
힙 가져 오기가 필요하지 않고 스택에 유지되므로 구조체가 더 좋습니다.
KPM

60

weakObjectsHashTable과 함께 NSHashTable을 사용할 수 있습니다. NSHashTable<ObjectType>.weakObjectsHashTable()

스위프트 3의 경우 : NSHashTable<ObjectType>.weakObjects()

NSHashTable 클래스 참조

OS X v10.5 이상에서 사용 가능합니다.

iOS 6.0 이상에서 사용 가능합니다.


가장 좋은 답변이며 래퍼의 허리 시간이 아닙니다!
Ramis

1
이것은 영리하지만, GoZoner의 대답처럼,이는 유형이 작동하지 않습니다 Any하지만 AnyObject같은 프로토콜과 같은.
Aaron Brager

@SteveWilford 그러나 프로토콜은 클래스에 의해 구현 될 수 있으며,이를 참조 타입으로 만들 수 있습니다.
Aaron Brager

4
프로토콜은 클래스를 확장 한 다음 약한 것으로 사용할 수 있습니다 (예 : 프로토콜 MyProtocol : 클래스)
Yasmin Tiomkin

1
MyProtocol: classand 로 컴파일러 오류가 발생 NSHashTable<MyProtocol>.weakObjects()합니다. " 'NSHashTable'을 사용하려면 'MyProtocol'이 클래스 유형이어야합니다.
Greg

14

파티에 늦었지만 내 시도해보십시오. 배열이 아닌 세트로 구현했습니다.

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은 문자열 유형이 아닌 NSString을 사용합니다. 문자열 유형은 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
    }
}

그런 다음 교체 NSStringMyString같은. 그런 다음 작동한다고 말하면 이상합니다.

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의 동작 차이는이 컨텍스트를 벗어나지 만 누군가이 문제를 파악하면 감사하겠습니다.


내 프로젝트 에이 솔루션을 채택했습니다. 잘 했어! 단 하나의 제안,이 솔루션은 nilinternal에서 값 을 제거하지 않는 것 같습니다 Set. 그래서 나는 reap()최고 답변에 언급 된 기능을 추가하고 액세스 할 reap()때마다 호출해야했습니다 WeakObjectSet.
gokeji

흠 대기는,이 스위프트 4에서 작동하지 않는 몇 가지 이유 / 아이폰 OS (11)은 값이 될 때 약한 참조가 즉시 해제되지 않는 것 같아 nil더 이상
gokeji

1
Swift4로 코드를 업데이트했습니다. 답변의 후반부를 참조하십시오. NSString에 할당 해제 문제가있는 것 같지만 여전히 커스텀 클래스 객체에서 작동해야합니다.
Kaz Yoshikawa

@KazYoshikawa를 살펴보고 답변을 업데이트 해 주셔서 감사합니다! 또한 나중에 사용자 정의 클래스가 작동하지만 작동 NSString하지 않는다는 것을 깨달았습니다 .
gokeji

2
에 의해 반환 된 포인터가 UnsafeMutablePointer<T>(&object)무작위로 변경 될 수 있다는 경험을했습니다 (와 동일 withUnsafePointer). 지금은 지원을받는 버전 사용 NSHashTable: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d를 .
simonseyer

12

이것은 내 해결책이 아닙니다. 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나를 위해 말한다 . 와 클래스는 잘 작동
mente

6
Structs는 값 유형이므로 작동하지 않아야합니다. 컴파일 타임 오류가 아닌 런타임에 충돌했다는 사실은 컴파일러 버그입니다.
David Goodine

10

약한 포인터를 보유 할 랩퍼 오브젝트를 작성하여이를 수행 할 수 있습니다.

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

그런 다음 배열에서 이것을 사용하십시오.

var weakThings = WeakThing<Foo>[]()

되어야 class사용에이 weak바르

3
누가 그래? 위의 코드는 나를 위해 잘 작동합니다. 유일한 요구 사항은 약한 요구되는 객체는 객체가 아니라 약한 참조 들고 클래스 될 것입니다
여호수아 와인버그

죄송합니다. 맹세했을 수 있습니다. "struct에 약한 변수를 사용할 수 없습니다"라는 컴파일러 메시지가 나타납니다. 당신은 맞습니다-컴파일됩니다.
Bill

5
@JoshuaWeinberg Foo가 프로토콜이라면?
onmyway133

프로토콜이 클래스에 의해서만 구현되도록 선언 된 경우 @ onmyway133 AFAIK 작동합니다. protocol Protocol : class { ... }
olejnjak

8

기능적 스타일 래퍼는 어떻습니까?

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 }

3
문제는 약한 객체로 구성된 배열을 만드는 방법입니다.
David H

이 솔루션을 사용하면과 같은 여러 값으로 배열을 만들 수도 있습니다 var array: [(x: Int, y: () -> T?)]. 정확히 내가 찾던 것.
jboi

1
@DavidH 질문에 답변하기 위해 답변을 업데이트했습니다. 이게 도움이 되길 바란다.
eonil

나는 이 접근법을 좋아했고 , 그것이 매우 영리하다고 생각합니다. 이 전략을 사용하여 클래스를 구현했습니다. 감사합니다!
Ale Ravasio

에 대해 너무 확실하지 않습니다 let values = Array(array1.map({ $0() })) part. 더 이상 참조가 약한 클로저 배열이 아니므로이 배열이 할당 해제 될 때까지 값이 유지됩니다. 내가 맞다면 self.items = Array(array1.map({ $0() }))이것이 목적을 능가하는 것처럼이 배열을 유지해서는 안된다는 점에 유의하는 것이 중요합니다 .
Matic Oblak

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()보호자를 추가하고 추가했습니다 .


허 해시는 단지 hashtable.allObjects의 객체에 사용하십시오
malhal

6

세부

  • 스위프트 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>() }
Matic Oblak

@MaticOblak 제네릭을 사용하는 것은 어떻습니까? protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
Vasily Bodnarchuk

이 배열은 동일한 클래스 프로토콜을 구현하는 다른 유형의 객체를 보유 할 수 있습니다. 제네릭을 사용하면 단일 유형으로 잠급니다. 예를 들어와 같은 배열을 보유하는 싱글 톤을 갖는 것을 상상해보십시오 delegates. 그런 다음이 기능을 사용하려는 뷰 컨트롤러가 몇 개 있습니다. 전화 할 것으로 예상됩니다 MyManager.delegates.append(self). 그러나 MyManager일부 일반 유형에 잠겨 있으면 매우 유용하지 않습니다.
Matic Oblak

@MaticOblak 좋아. 이것을 시도하십시오 : 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())
Vasily Bodnarchuk

약간 중요한 배열로 일반적인 부분을 잃어 버렸습니다. :) 이것이 불가능하다는 느낌이 들었습니다. 지금 스위프트의 한계 ...
Matic Oblak

4

WeakContainer의 기존 예제는 도움이되지만 목록 및 사전과 같은 기존 빠른 컨테이너에서 약한 참조를 사용하는 것은 실제로 도움이되지 않습니다.

contains와 같은 List 메소드를 사용하려면 WeakContainer가 Equatable을 구현해야합니다. 그래서 WeakContainer가 동일 할 수 있도록 코드를 추가했습니다.

사전에서 WeakContainer를 사용하려는 경우 사전 키로 사용할 수 있도록 해시 가능하게 만들었습니다.

또한 클래스 유형에만 해당하고 WeakContainer 예제와 구별하기 위해 WeakObject로 이름을 변경했습니다.

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

3

여기 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
}

3

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내가 손에 정확한 코드를 필요가 없습니다).


2

같은 문제에 대한 또 다른 해결책은 이것에 대한 초점은 객체에 대한 약한 참조를 저장하지만 구조체도 저장할 수 있다는 것입니다.

[어떻게 유용한 지 잘 모르겠지만 구문을 제대로 얻으려면 시간이 걸렸습니다.]

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

& flatMap대신 에 사용 하는 것은 어떻습니까? filtermap
루카스 쿠바 네크

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

이 내 솔루션 :

  • 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

이것은 약한 객체의 컨테이너를 보유하는 형식이 안전한 컬렉션입니다. 또한 액세스 할 때 컨테이너 / 래퍼가없는 자동 제거합니다.

예:

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

0

무엇에 대한 기능적 접근 ?

let observers = [() -> Observer?]()

observers.append({ [weak anObserver] in return anObserver })

이것이 주요 아이디어입니다. 그런 다음 배열에 무엇이 있는지 추적하기 위해 편리한 논리를 추가하십시오. 예를 들어, 키를 사용하여 사전을 사용하는 것과 동일한 접근 방식을 고려할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.