键值观察Swift没有显示数组中的插入和删除

Fel*_*rri 9 arrays key-value-observing ios swift

我创建了一个包含数组的类.我在视图控制器中向该数组添加了一个观察者,并对该数组执行了一些修改.

问题是,当我打印observeValueForKeyPath()方法返回的更改字典时,我只能看到类型NSKeyValueChangeSetting的更改.换句话说,该方法告诉我数组已更改,向我提供旧的和新的数组(包含所有元素)但我想收到添加或删除的特定项的信息.

这是一些示例代码.

这是将观察其数组的类.

private let _observedClass = ObservedClass()

class ObservedClass: NSObject {
    dynamic var animals = [String]()
    dynamic var cars = [String]()

    class var sharedInstance: ObservedClass {
        return _observedClass
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的视图控制器的代码.

class ViewController: UIViewController {
    var observedClass = ObservedClass.sharedInstance

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        observedClass.addObserver(self, forKeyPath: "animals", options: .New | .Old, context: nil)
    }

    deinit {
        observedClass.removeObserver(self, forKeyPath: "animals")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        observedClass.animals.insert("monkey", atIndex: 0)
        observedClass.animals.append("tiger")
        observedClass.animals.append("lion")
        observedClass.animals.removeAtIndex(0)
    }

    override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
        println(change)
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码时,我在控制台上得到了这个结果:

[kind: 1, old: (
), new: (
    monkey
)]
[kind: 1, old: (
    monkey
), new: (
    monkey,
    tiger
)]
[kind: 1, old: (
    monkey,
    tiger
), new: (
    monkey,
    tiger,
    lion
)]
[kind: 1, old: (
    monkey,
    tiger,
    lion
), new: (
    tiger,
    lion
)]
Run Code Online (Sandbox Code Playgroud)

在这个例子中,不应该使用更改类型NSKeyValueChangeInsertion将更改字典显示为添加到数组中的每个新项目.

Pau*_*son 12

根据Swift指南:

对于数组,只有在执行可能会修改数组长度的操作时才会进行复制.这包括追加,插入或删除项目,或使用范围下标来替换数组中的一系列项目.

以一个append操作为例.在引擎盖下,当你附加到你的数组时,Swift 在内存中创建一个数组,将现有animals数组中的项复制到这个新数组 - 加上新项 - 然后将这个新数组分配给animals变量.这种愚蠢的手段就是为什么你只得到那种1(Setting),因为实际上每个' edit '实际上都会导致创建一个新的数组.

这是不同的NSMutableArray,因为行为更直观的一点-编辑现有阵列制成(有没有幕后的复制到一个新的数组),所以编辑后存在的阵列是之前存在的相同阵列-因此存储在该值change与密钥词典NSKeyValueChangeKindKey可以是一个.Insertion,.Removal等等.

然而,即使这不是整个故事,因为NSMutableArray根据您使用的是Swift还是Objective-C,您对符合KVO标准的更改的方式会有所不同.在Objective-C中,Apple强烈建议实现他们称之为可选的可变索引访问器.这些只是具有标准签名的方法,可以非常有效地进行符合KVO的更改.

// Mutable Accessors ///////////////////////////////////////////

// This class has a property called <children> of type NSMutableArray

// MUST have this (or other insert accessor)
-(void)insertObject:(NSNumber *)object inChildrenAtIndex:(NSUInteger)index {
    [self.children insertObject:object atIndex:index];
}

// MUST have this (or other remove accessor)
-(void)removeObjectFromChildrenAtIndex:(NSUInteger)index {
    [self.children removeObjectAtIndex:index];
}

// OPTIONAL - but a good idea.
-(void)replaceObjectInChildrenAtIndex:(NSUInteger)index withObject:(id)object {
    [self.children replaceObjectAtIndex:index withObject:object];
}
Run Code Online (Sandbox Code Playgroud)

Swift似乎没有这些,因此进行符合KVO的更改的唯一方法是通过可变代理对象 - 在NSKeyValueCoding页面中描述.这是一个非常快速的例子:

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    dynamic var children: NSMutableArray = NSMutableArray()


    ////////////////////////////////////

    func applicationDidFinishLaunching(aNotification: NSNotification) {

        addObserver(self,
            forKeyPath: "children",
            options: .New | .Old,
            context: &Update)

        // Get the KVO/KVC compatible array
        var childrenProxy = mutableArrayValueForKey("children")

        childrenProxy.addObject(NSNumber(integer: 20)) // .Insertion

        childrenProxy.addObject(NSNumber(integer: 30)) // .Insertion

        childrenProxy.removeObjectAtIndex(1)  // .Removal
    }
}
Run Code Online (Sandbox Code Playgroud)