关联对象和Swift数组

问题描述 投票:0回答:1

我在包含一个数组(在这种情况下为可变数组)的视图扩展上有一个关联的对象。

var queue: NSMutableArray {
    get {
        if let queue = objc_getAssociatedObject(self, &Key.queue) as? NSMutableArray {
            return queue
        } else {
            let queue = NSMutableArray()
            objc_setAssociatedObject(self, &Key.queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            return queue
        }
    }
}

我想将NSMutableArray转换为Swift数组,但是我不知道该怎么做。这将阻止我执行非常丑陋的投射。有什么建议吗?

arrays objective-c swift nsmutablearray associated-object
1个回答
0
投票

NSMutableArray在这里是引用类型。这样的结果是,一旦您将某人交给您的视图对象的queue引用,您的视图对象就会失去对它的所有控制。参考的接收者可以重新排列项目,删除所有项目等,并且您的视图只有在下一次尝试阅读它时才知道。

通常来说,这种隐式数据共享被认为是一个坏主意,因为它破坏了封装并使系统复杂化,因为您无法再在本地就代码进行推理,因为始终存在另一个线程具有引用的威胁。到您的别名对象,并在您的脚下对其进行更改。

此模型与Swift的值类型不兼容,例如Array。如果queueArray<T>,则访问它的每个人都将获得自己的价值。从语义上讲,这些副本都是彼此完全隔离的,通过一种参考进行的突变不可能导致通过另一种参考可以观察到的效果。

如果您希望忠实地保留当前代码的引用语义,则需要一种机制来侦听NSMutableArray的更改,并更新从其派生的每个Array<T>。这不是很实际,也不是一个好主意。

这就是我要做的:

  1. 使接口更明确地具有事务性。您更有可能完全隐藏queue。将其设为私有,并让您的视图公开诸如pushpop之类的公共方法。

    import Foundation
    
    class C: NSObject {
        private enum AssociatedObjectKeys {
            static var queue: Int8 = 0
        }
    
        private var queue: Array<Int> {
            get {
                if let existingValue = objc_getAssociatedObject(self, &AssociatedObjectKeys.queue) {
                    if let existingArray = existingValue as? Array<Int> {
                        return existingArray
                    }
                    fatalError("Found an associated object that had the wrong type!")
                }
                else {
                    self.queue = []
                    return []
                }
            }
            set { 
                objc_setAssociatedObject(self, &AssociatedObjectKeys.queue, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    
        public func printQueueForDebugging() {
            print(self.queue)
        }
    
        public func push(_ newValue: Int) {
            self.queue.append(newValue)
        }
    
        public func pushAll<S: Sequence>(_ newValues: S) where S.Element == Int {
            self.queue.append(contentsOf: newValues)
        }
    
        public func pop() -> Int? {
            if self.queue.isEmpty { return nil }
            return self.queue.removeFirst()
        }
    }
    
    let c = C()
    c.printQueueForDebugging()
    c.pushAll(1...3)
    c.printQueueForDebugging()
    c.push(4)
    c.printQueueForDebugging()
    print(c.pop() as Any)
    print(c.pop() as Any)
    print(c.pop() as Any)
    print(c.pop() as Any)
    print(c.pop() as Any)
    
  2. 我将使用类型安全的Swift包装器来隐藏相关的对象集/获取,该对象集/获取可以在后台自动进行类型检查和强制转换。新的属性包装器功能非常适合此。

  3. 提取单独的Queue<T>数据结构。 Array<T>本身并不能构成一个好的队列,因为从一开始就删除具有O(n)的时间复杂性。有很多数据结构库,我将使用其中一个队列代替。

© www.soinside.com 2019 - 2024. All rights reserved.