NSUndoManager: capturing reference types possible?

时光毁灭记忆、已成空白 提交于 2019-12-25 09:26:05

问题


I am having trouble understanding how NSUndoManager captures values when the values are reference types. Whenever try to use the undoManager, the values don't undo.

Note: I need to support macOS 10.10, and I am using Swift 3 and XCode 8

Here I save the state of the ID numbers, reset them all to -1, and then try to undo. The result is that they are all still -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
    }

}

To prove to myself that it was a problem capturing values, instead of saving the ID objects themselves, I stored the Int values that they contain. It worked perfectly as the Ints are value types.

Then I read some more about closure capturing and tried this.

undoManager?.registerUndo(withTarget: self, handler: { [saved = savedIDs] 
    (self) -> () in 
         self.undo(savedIDs: saved)
 })

Same problem, all still -1.


回答1:


NSUndoManager isn't really about capturing about old values, but about capturing operations that restore the original values.

You are only capturing references to the IDs. So the elements in savedIDs are pointing to the same objects as the id properties on the elements of people, i.e. when you change one, you also change the other.

What you need to do is manually save the IDs and the values you want to reset them to, like so:

let savedPairs = people.map { (id: $0.id, originalValue: $0.id.number) }

This ensures that the number value itself of id is saved somewhere, not just the pointer to the id.

You can then register an action that performs the undoing with the values you captured manually, like so:

undoManager.registerUndo(withTarget: self) {
    savedPairs.forEach { $0.id.number = $0.originalValue }
}


来源:https://stackoverflow.com/questions/41575694/nsundomanager-capturing-reference-types-possible

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!