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 언어에서 이와 같이 정의 된 확장을 허용하지 않는다고 생각합니다.
답변
weakObjectsHashTable과 함께 NSHashTable을 사용할 수 있습니다. NSHashTable<ObjectType>.weakObjectsHashTable()
스위프트 3의 경우 : NSHashTable<ObjectType>.weakObjects()
OS X v10.5 이상에서 사용 가능합니다.
iOS 6.0 이상에서 사용 가능합니다.
답변
파티에 늦었지만 내 시도해보십시오. 배열이 아닌 세트로 구현했습니다.
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
}
}
그런 다음 교체 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의 동작 차이는이 컨텍스트를 벗어나지 만 누군가이 문제를 파악하면 감사하겠습니다.
답변
이것은 내 해결책이 아닙니다. 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]
답변
약한 포인터를 보유 할 랩퍼 오브젝트를 작성하여이를 수행 할 수 있습니다.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
그런 다음 배열에서 이것을 사용하십시오.
var weakThings = WeakThing<Foo>[]()
답변
기능적 스타일 래퍼는 어떻습니까?
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 }
답변
제네릭으로 약한 컨테이너를 만드는 것과 동일한 아이디어가있었습니다.
결과적으로 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()
보호자를 추가하고 추가했습니다 .