当值是引用类型时,我无法理解NSUndoManager如何捕获值。每当尝试使用撤消管理器时,值都不会撤消。
注意:我需要支持 macOS 10.10,并且使用的是 Swift 3 和 XCode 8
。在这里,我保存ID号的状态,将它们全部重置为-1,然后尝试撤消。结果是它们仍然是 -1。
import Cocoa
class Person: NSObject {
var id: ID
init(withIDNumber idNumber: Int) {
self.id = ID(idNumber)
}
}
class ID: NSObject {
var number: Int
init(_ number: Int) {
self.number = number
}
}
ViewController... {
@IBAction func setIDsToNegative1(_ sender: NSButton) {
var savedIDs: [ID] = []
for person in people {
savedIDs.append(person.id)
}
undoManager?.registerUndo(withTarget: self, selector:#selector(undo(savedIDs:)), object: savedIDs)
for person in people {
person.id.number = -1
}
}
func undo(savedIDs: [ID]) {
for id in savedIDs {
print(id.number)
}
//Prints -1, -1, -1
}
}
为了向自己证明捕获值是一个问题,我没有保存 ID 对象本身,而是存储了它们包含的 Int 值。它完美地工作,因为 Ints 是值类型。
然后我阅读了更多关于闭包捕获的信息并尝试了这个。
undoManager?.registerUndo(withTarget: self, handler: { [saved = savedIDs]
(self) -> () in
self.undo(savedIDs: saved)
})
同样的问题,都还是-1。
>NSUndoManager
并不是真正捕获旧值,而是捕获还原原始值的操作。
您只捕获对ID
的引用。因此,savedIDs
中的元素指向与people
元素上的id
属性相同的对象,即当您更改一个时,您也会更改另一个。
您需要做的是手动保存ID
和要重置它们的值,如下所示:
let savedPairs = people.map { (id: $0.id, originalValue: $0.id.number) }
这可确保id
的number
值本身保存在某个位置,而不仅仅是指向id
的指针。
然后,您可以注册一个操作,该操作使用手动捕获的值执行撤消,如下所示:
undoManager.registerUndo(withTarget: self) {
savedPairs.forEach { $0.id.number = $0.originalValue }
}