Swift 中的键值观察未显示数组中的插入和删除
Key-Value Observe in Swift not showing insertions and removals in arrays
我创建了一个包含数组的 class。我在视图控制器中向该数组添加了一个观察者,并对该数组进行了一些修改。
问题是,当我打印由 observeValueForKeyPath() 方法返回的更改字典时,我只能看到 NSKeyValueChangeSetting 类型的更改。换句话说,该方法告诉我数组已更改,为我提供了旧数组和新数组(包含所有元素),但我想接收有关添加或删除了哪些特定项目的信息。
这是一些示例代码。
这是class,其数组将被观察。
private let _observedClass = ObservedClass()
class ObservedClass: NSObject {
dynamic var animals = [String]()
dynamic var cars = [String]()
class var sharedInstance: ObservedClass {
return _observedClass
}
}
这是我的视图控制器中的代码。
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)
}
}
当我运行上面的代码时,我在控制台上得到了这个结果:
[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
)]
在此示例中,更改字典不应该使用更改类型 NSKeyValueChangeInsertion 显示添加到数组中的每个新项吗?
根据 Swift 指南:
For arrays, copying only takes place when you perform an action that has the potential to modify the length of the array. This includes appending, inserting, or removing items, or using a ranged subscript to replace a range of items in the array.
以append
操作为例。在引擎盖下,当您附加到数组时,Swift 在内存中创建一个 new 数组,将现有 animals
数组中的项目复制到这个新数组中 -加上新项目 - 然后将这个新数组分配给 animals
变量。这种技巧就是为什么你只能得到那种 1
(Setting
) 的原因,因为实际上每个 'edit' 实际上都会产生一个新的正在创建数组。
它与 NSMutableArray
不同,因为行为更加直观 - 编辑 是 对现有数组进行的(没有幕后复制到新数组),因此编辑后存在的数组与编辑前存在的数组相同 - 因此存储在 change
字典中且键为 NSKeyValueChangeKindKey
的值可以是 .Insertion
之一, .Removal
, 等等
但这还不是全部,因为您对 NSMutableArray
进行 KVO 兼容更改的方式因您使用的是 Swift 还是 Objective-C 而异.在 Objective-C 中,Apple 强烈建议实施他们所谓的可选 mutable indexed accessors。这些只是具有标准签名的方法,可以以非常有效的方式进行符合 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];
}
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
}
}
我创建了一个包含数组的 class。我在视图控制器中向该数组添加了一个观察者,并对该数组进行了一些修改。
问题是,当我打印由 observeValueForKeyPath() 方法返回的更改字典时,我只能看到 NSKeyValueChangeSetting 类型的更改。换句话说,该方法告诉我数组已更改,为我提供了旧数组和新数组(包含所有元素),但我想接收有关添加或删除了哪些特定项目的信息。
这是一些示例代码。
这是class,其数组将被观察。
private let _observedClass = ObservedClass()
class ObservedClass: NSObject {
dynamic var animals = [String]()
dynamic var cars = [String]()
class var sharedInstance: ObservedClass {
return _observedClass
}
}
这是我的视图控制器中的代码。
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)
}
}
当我运行上面的代码时,我在控制台上得到了这个结果:
[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
)]
在此示例中,更改字典不应该使用更改类型 NSKeyValueChangeInsertion 显示添加到数组中的每个新项吗?
根据 Swift 指南:
For arrays, copying only takes place when you perform an action that has the potential to modify the length of the array. This includes appending, inserting, or removing items, or using a ranged subscript to replace a range of items in the array.
以append
操作为例。在引擎盖下,当您附加到数组时,Swift 在内存中创建一个 new 数组,将现有 animals
数组中的项目复制到这个新数组中 -加上新项目 - 然后将这个新数组分配给 animals
变量。这种技巧就是为什么你只能得到那种 1
(Setting
) 的原因,因为实际上每个 'edit' 实际上都会产生一个新的正在创建数组。
它与 NSMutableArray
不同,因为行为更加直观 - 编辑 是 对现有数组进行的(没有幕后复制到新数组),因此编辑后存在的数组与编辑前存在的数组相同 - 因此存储在 change
字典中且键为 NSKeyValueChangeKindKey
的值可以是 .Insertion
之一, .Removal
, 等等
但这还不是全部,因为您对 NSMutableArray
进行 KVO 兼容更改的方式因您使用的是 Swift 还是 Objective-C 而异.在 Objective-C 中,Apple 强烈建议实施他们所谓的可选 mutable indexed accessors。这些只是具有标准签名的方法,可以以非常有效的方式进行符合 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];
}
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
}
}