Tôi muốn lưu trữ một loạt các tài liệu tham khảo yếu trong Swift. Bản thân mảng không nên là một tham chiếu yếu - các phần tử của nó phải là. Tôi nghĩ rằng Cacao NSPointerArray
cung cấp một phiên bản không an toàn về điều này.
Tôi muốn lưu trữ một loạt các tài liệu tham khảo yếu trong Swift. Bản thân mảng không nên là một tham chiếu yếu - các phần tử của nó phải là. Tôi nghĩ rằng Cacao NSPointerArray
cung cấp một phiên bản không an toàn về điều này.
Câu trả lời:
Tạo một trình bao bọc chung như:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
Thêm các thể hiện của lớp này vào mảng của bạn.
class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
Khi xác định Weak
bạn có thể sử dụng struct
hoặc class
.
Ngoài ra, để giúp gặt hái nội dung mảng, bạn có thể làm gì đó dọc theo dòng:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
Việc sử dụng AnyObject
ở trên nên được thay thế bằng T
- nhưng tôi không nghĩ rằng ngôn ngữ Swift hiện tại cho phép một tiện ích mở rộng được xác định như vậy.
Stuff
bằng một giao thức; xem câu hỏi liên quan này
Bạn có thể sử dụng NSHashTable với yếuObjectsHashTable. NSHashTable<ObjectType>.weakObjectsHashTable()
Dành cho Swift 3: NSHashTable<ObjectType>.weakObjects()
Có sẵn trong OS X v10.5 trở lên.
Có sẵn trong iOS 6.0 trở lên.
Any
nhưng không AnyObject
, chẳng hạn như các giao thức.
MyProtocol: class
và NSHashTable<MyProtocol>.weakObjects()
. "'NSHashTable' yêu cầu 'MyProtocol' phải là loại lớp.
Đó là loại muộn của bữa tiệc, nhưng hãy thử của tôi. Tôi đã triển khai như một Set không phải là một mảng.
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]
Coi chừng WeakObjectSet sẽ không lấy loại Chuỗi nhưng NSString. Bởi vì, kiểu String không phải là AnyType. Phiên bản nhanh chóng của tôi là Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
Mã có thể được lấy từ Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39
** THÊM VÀO NOV.2017
Tôi đã cập nhật mã lên 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) })
}
}
Như gokeji đã đề cập, tôi đã tìm ra NSString sẽ không bị xử lý dựa trên mã sử dụng. Tôi gãi đầu và tôi đã viết lớp MyString như sau.
// 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
}
}
Sau đó thay thế NSString
bằng MyString
như thế này. Sau đó, lạ để nói rằng nó hoạt động.
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]
Sau đó, tôi tìm thấy một trang lạ có thể liên quan đến vấn đề này.
Tham chiếu yếu giữ lại NSString đã phân bổ (chỉ dành cho Sim XC9 + iOS)
https://bugs.swift.org/browse/SR-5511
Nó nói vấn đề là RESOLVED
nhưng tôi tự hỏi nếu điều này vẫn còn liên quan đến vấn đề này. Dù sao, sự khác biệt về hành vi giữa MyString hoặc NSString nằm ngoài bối cảnh này, nhưng tôi sẽ đánh giá cao nếu ai đó phát hiện ra vấn đề này.
nil
giá trị khỏi nội bộ Set
. Vì vậy, tôi đã thêm một reap()
chức năng được đề cập trong câu trả lời hàng đầu và đảm bảo gọi reap()
mỗi khi WeakObjectSet
truy cập.
nil
nữa
NSString
không.
UnsafeMutablePointer<T>(&object)
có thể thay đổi ngẫu nhiên (giống với withUnsafePointer
). Bây giờ tôi sử dụng một phiên bản được hỗ trợ bởiNSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Đây không phải là giải pháp của tôi. Tôi đã tìm thấy nó trên Diễn đàn nhà phát triển của Apple .
@GoZoner có một câu trả lời hay, nhưng nó làm sập trình biên dịch Swift.
Đây là phiên bản của bộ chứa đối tượng yếu không làm sập trình biên dịch được phát hành hiện tại.
struct WeakContainer<T where T: AnyObject> {
weak var _value : T?
init (value: T) {
_value = value
}
func get() -> T? {
return _value
}
}
Sau đó, bạn có thể tạo một mảng của các thùng chứa này:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
EXC_BAD_ACCESS
cho tôi. Với lớp học hoạt động tốt
Bạn có thể làm điều này bằng cách tạo một đối tượng trình bao bọc để giữ một con trỏ yếu.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
Và sau đó sử dụng chúng trong mảng
var weakThings = WeakThing<Foo>[]()
class
sử dụng weak
vars
protocol Protocol : class { ... }
Làm thế nào về phong cách chức năng bao bọc?
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)
Chỉ cần gọi đóng lại để kiểm tra mục tiêu vẫn còn sống.
let isAlive = captured1() != nil
let theValue = captured1()!
Và bạn có thể lưu trữ đóng cửa này thành một mảng.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
Và bạn có thể truy xuất các giá trị bị bắt yếu bằng cách ánh xạ gọi các bao đóng.
let values = Array(array1.map({ $0() }))
Trên thực tế, bạn không cần một chức năng để đóng cửa. Chỉ cần chụp một đối tượng trực tiếp.
let captured3 = { [weak obj3] in return obj3 }
var array: [(x: Int, y: () -> T?)]
. Chính xác những gì tôi đang tìm kiếm.
let values = Array(array1.map({ $0() })) part
. Vì đây không còn là một mảng các bao đóng với các tham chiếu yếu, các giá trị sẽ được giữ lại cho đến khi mảng này được giải phóng. Nếu tôi đúng thì điều quan trọng cần lưu ý là bạn không bao giờ nên giữ lại mảng self.items = Array(array1.map({ $0() }))
này như điều này đánh bại mục đích.
Tôi đã có cùng một ý tưởng để tạo ra container yếu với thuốc generic.
Kết quả là tôi đã tạo trình bao bọc cho 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?
}
}
}
Sử dụng:
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()
}
Đây không phải là giải pháp tốt nhất, bởi vì WeakSet
có thể được khởi tạo với bất kỳ loại nào và nếu loại này không tuân thủ AnyObject
giao thức thì ứng dụng sẽ bị sập với lý do chi tiết. Nhưng tôi không thấy bất kỳ giải pháp tốt hơn ngay bây giờ.
Giải pháp ban đầu là xác định WeakSet
theo cách này:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
Nhưng trong trường hợp này WeakSet
không thể khởi tạo với giao thức:
protocol MyDelegate : AnyObject {
func doWork()
}
let weakSet = WeakSet<MyDelegate>()
Hiện tại mã trên không thể được biên dịch (Swift 2.1, Xcode 7.1).
Đó là lý do tại sao tôi bỏ việc tuân thủ AnyObject
và thêm các vệ sĩ bổ sung với các fatalError()
xác nhận.
struct WeakObject<Object: AnyObject> { weak var object: Object? }
@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) }
}
}
class Class1 { // or struct
@WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
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) })
}
}
class Class2 { // or struct
var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
đừng quên dán mã giải pháp
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
. Sau đó, bạn sẽ có một số bộ điều khiển xem muốn sử dụng chức năng này. Bạn sẽ gọi MyManager.delegates.append(self)
. Nhưng nếu MyManager
bị khóa với một số loại chung chung thì điều này không thể sử dụng được.
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())
Ví dụ hiện có của WeakContainer rất hữu ích, nhưng nó không thực sự giúp người ta sử dụng các tài liệu tham khảo yếu trong các thùng chứa nhanh như Danh sách và Từ điển.
Nếu bạn muốn sử dụng các phương thức List như chứa, thì WeakContainer sẽ cần triển khai Equitable. Vì vậy, tôi đã thêm mã để cho phép WeakContainer tương đương.
Trong trường hợp bạn muốn sử dụng WeakContainer trong từ điển, tôi cũng đã làm cho nó có thể băm để nó có thể được sử dụng làm khóa từ điển.
Tôi cũng đã đổi tên nó thành WeakObject để nhấn mạnh rằng điều này chỉ dành cho các loại lớp và để phân biệt nó với các ví dụ của 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!
}
Điều này cho phép bạn thực hiện một số nội dung thú vị như sử dụng Từ điển tham chiếu yếu:
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)
}
Dưới đây là cách thực hiện @ câu trả lời tuyệt vời GoZoner của phù hợp với Hashable
, vì vậy nó có thể được lập chỉ mục trong các đối tượng container như: Set
, Dictionary
, Array
vv
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
}
Vì NSPointerArray
đã tự động xử lý hầu hết các vấn đề này, tôi đã giải quyết vấn đề bằng cách tạo một trình bao bọc an toàn cho nó, điều này giúp tránh được rất nhiều câu trả lời trong các câu trả lời khác:
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
}
Ví dụ sử dụng:
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
Nó hoạt động nhiều hơn trước, nhưng việc sử dụng trong phần còn lại của mã của bạn sạch hơn IMO. Nếu bạn muốn làm cho nó giống mảng hơn, bạn thậm chí có thể triển khai đăng ký, biến nó thành một SequenceType
v.v. (nhưng dự án của tôi chỉ cần append
và forEach
vì vậy tôi không có mã chính xác trong tay).
Tuy nhiên, một giải pháp khác cho cùng một vấn đề ... trọng tâm của vấn đề này là lưu trữ một tham chiếu yếu đến một đối tượng nhưng cũng cho phép bạn lưu trữ một cấu trúc.
[Tôi không chắc nó hữu ích như thế nào, nhưng phải mất một thời gian để có được cú pháp đúng]
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
Bạn có thể tạo trình bao bọc xung quanh Array
. Hoặc sử dụng thư viện này https://github.com/NickRybalko/WeakPulumArray
let array = WeakPointerArray<AnyObject>()
Đây là loại an toàn.
Các câu trả lời khác đã bao quát các góc chung. Tôi nghĩ rằng tôi sẽ chia sẻ một số mã đơn giản bao gồm các nil
góc.
Tôi muốn có một mảng tĩnh (thỉnh thoảng đọc) của tất cả các Label
s hiện đang tồn tại trong ứng dụng, nhưng không muốn thấy nil
nơi mà những cái cũ đã từng ở.
Không có gì lạ mắt, đây là mã của tôi ...
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
thay vì filter
& map
?
Tôi dựa trên điều này dựa trên công việc của @Eonil, vì tôi yêu thích chiến lược liên kết yếu đóng cửa, nhưng tôi không muốn sử dụng một toán tử hàm cho một biến, vì nó cảm thấy cực kỳ trực quan
Những gì tôi đã làm, thay vào đó, như sau:
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
}
}
}
Bằng cách này bạn có thể làm một cái gì đó như:
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
Đây là giải pháp của tôi:
-
// 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)) }
}
}
Đây là một bộ sưu tập an toàn chứa các vật chứa yếu. Nó cũng tự động loại bỏ nil container / Wrapper khi truy cập.
Thí dụ:
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() }
}
}
Bộ sưu tập tùy chỉnh 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 }
}
}
Điều gì về một phương pháp chức năng ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
Đây là ý tưởng chính, sau đó thêm bất kỳ logic tiện lợi nào để theo dõi những gì trong mảng. Ví dụ, người ta có thể xem xét cách tiếp cận tương tự với Từ điển bằng cách sử dụng khóa như một cách tìm thấy những gì trong đó.