Swift NSUndoManager:是否可以捕获引用类型?
当值是引用类型时,我很难理解NSUndoManager如何捕获值。无论何时尝试使用undoManager,值都不会撤消 注意:我需要支持macOS 10.10,我使用的是Swift 3和XCode 8 在这里,我保存ID号的状态,将它们全部重置为-1,然后尝试撤消。结果是它们仍然是-1Swift NSUndoManager:是否可以捕获引用类型?,swift,cocoa,nsundomanager,Swift,Cocoa,Nsundomanager,当值是引用类型时,我很难理解NSUndoManager如何捕获值。无论何时尝试使用undoManager,值都不会撤消 注意:我需要支持macOS 10.10,我使用的是Swift 3和XCode 8 在这里,我保存ID号的状态,将它们全部重置为-1,然后尝试撤消。结果是它们仍然是-1 import Cocoa class Person: NSObject { var id: ID init(withIDNumber idNumber: Int) { self
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值。它工作得很好,因为int是值类型
然后我读了更多关于闭包捕获的内容,并尝试了这个方法
undoManager?.registerUndo(withTarget: self, handler: { [saved = savedIDs]
(self) -> () in
self.undo(savedIDs: saved)
})
同样的问题,仍然是-1。
NSUndoManager
并不是捕获旧值,而是捕获恢复原始值的操作
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
s的引用。因此,savedIDs
中的元素指向与people
元素上的id
属性相同的对象,即当您更改一个时,您也会更改另一个
您需要做的是手动保存ID
s以及要将其重置为的值,如下所示:
let savedPairs = people.map { (id: $0.id, originalValue: $0.id.number) }
undoManager.registerUndo(withTarget: self) {
savedPairs.forEach { $0.id.number = $0.originalValue }
}
这样可以确保id
的number
值本身保存在某个位置,而不仅仅是指向id
的指针
然后,您可以注册一个操作,该操作使用手动捕获的值执行撤消操作,如下所示:
let savedPairs = people.map { (id: $0.id, originalValue: $0.id.number) }
undoManager.registerUndo(withTarget: self) {
savedPairs.forEach { $0.id.number = $0.originalValue }
}
谢谢,这证实了我的猜测,“冷冻干燥”参考状态是不可能的。在我提供的示例中,数据结构非常简单,因此保存元组很容易。但是,如果我想撤销由其他引用类型组成的更复杂数据结构的类呢?我必须深入研究对象结构并提取所有的值类型,不是吗?我认为像我在这里建议的那样做要容易得多:你可以这样做,但我认为你必须管理每个对象的撤消状态,特别是当你处理对象层次结构时,你会受到很大的伤害。请记住,每个属性都需要一个撤消堆栈。另外,在这种情况下,这甚至没有帮助,因为您需要对每个ID对象调用undo()。考虑尽可能多地为模型使用值类型,这些值会像预期的那样捕获值。我们所能做的就是按照NSCopying协议获取处于先前状态的对象,并执行深度复制以恢复该状态,并且在向撤消堆栈添加数据时,只需对原始状态进行深度复制并添加到撤消堆栈