You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从CoreData迁移至Realm:替代NSUndoManager的撤销机制策略咨询

Hey there! I’ve gone through this exact migration from Core Data to Realm, and figuring out undo/redo was one of the trickiest parts since Realm doesn’t have a direct equivalent to NSUndoManager. Let’s break down the best strategies you can use to implement a solid undo mechanism with Realm:

1. Manual Undo Stack with Object Snapshots

The simplest approach is to track changes manually by taking snapshots of objects before modifying them, then storing those snapshots in undo/redo stacks. Since Realm writes are atomic transactions, you can reverse changes by reverting objects to their snapshot state in a new transaction.

Here’s a quick example in Swift:

var undoStack: [User] = []
var redoStack: [User] = []
let realm = try! Realm()

// Helper to update a user and track the change
func updateUser(_ user: User, newName: String) {
    // Save a snapshot of the current state
    let snapshot = User(value: user)
    undoStack.append(snapshot)
    redoStack.removeAll() // Clear redo when a new change is made

    // Perform the write transaction
    try! realm.write {
        user.name = newName
    }
}

// Undo the last change
func undo() {
    guard let lastSnapshot = undoStack.popLast() else { return }
    
    // Save the current state for redo
    let currentUser = realm.objects(User.self).first(where: { $0.id == lastSnapshot.id })!
    redoStack.append(User(value: currentUser))
    
    // Revert to the snapshot
    try! realm.write {
        currentUser.name = lastSnapshot.name
        // Repeat this for all other properties you need to revert
    }
}

Pro tip: For large datasets or objects with many properties, avoid storing full object snapshots—instead, track only the specific properties that changed (e.g., a dictionary of old values) to save memory.

2. Automated Change Tracking with Realm Notifications

If you want a more hands-off approach, you can use Realm’s change notifications to automatically log every modification (inserts, updates, deletions). You’ll then reverse these logged changes when undo is triggered.

First, define a struct to track change records:

enum ChangeType {
    case insert(User)
    case update(oldValue: User, newValue: User)
    case delete(User)
}

struct ChangeRecord {
    let type: ChangeType
}

var changeHistory: [ChangeRecord] = []
var notificationToken: NotificationToken?

Then set up a listener to capture changes:

func setupChangeTracking() {
    notificationToken = realm.observe { [weak self] notification, realm in
        guard let self = self else { return }
        guard case .didChange(let changes) = notification else { return }
        
        changes.forEach { change in
            switch change {
            case .insert(let user):
                self.changeHistory.append(.init(type: .insert(User(value: user))))
            case .update(let user, _):
                // Important: Capture the old state *before* the write—wrap writes in helpers to ensure this
                let oldSnapshot = User(value: user)
                self.changeHistory.append(.init(type: .update(oldValue: oldSnapshot, newValue: User(value: user))))
            case .delete(let user):
                self.changeHistory.append(.init(type: .delete(User(value: user))))
            }
        }
    }
}

And finally, implement the undo logic:

func undoLastChange() {
    guard let lastChange = changeHistory.popLast() else { return }
    
    try! realm.write {
        switch lastChange.type {
        case .insert(let user):
            // Undo insert by deleting the object
            if let existingUser = realm.objects(User.self).first(where: { $0.id == user.id }) {
                realm.delete(existingUser)
            }
        case .update(let oldValue, _):
            // Undo update by reverting to old values
            if let existingUser = realm.objects(User.self).first(where: { $0.id == oldValue.id }) {
                existingUser.name = oldValue.name
                // Revert other properties here
            }
        case .delete(let user):
            // Undo delete by reinserting the object
            realm.create(User.self, value: user)
        }
    }
}

Note: You’ll need to wrap all write operations in helper functions to ensure you capture the old state before changes are applied—otherwise, the notification will only give you the new state.

3. Full Realm Snapshots with freeze()

If your app needs full-realm undo (like a document-based app where users might want to revert to an entire previous state), Realm’s freeze() method is perfect. It creates an immutable snapshot of the realm at a specific point in time, which you can store in your undo stack.

Example:

var undoStack: [Realm] = []
let realm = try! Realm()

// Helper to perform a write and save a snapshot
func performTrackedWrite(block: () throws -> Void) {
    // Save a frozen snapshot before writing
    let frozenSnapshot = realm.freeze()
    undoStack.append(frozenSnapshot)
    
    try! realm.write(block)
}

// Undo by restoring the last frozen snapshot
func undoToSnapshot() {
    guard let lastSnapshot = undoStack.popLast() else { return }
    
    try! realm.write {
        // Clear current data and restore from the snapshot
        realm.deleteAll()
        for user in lastSnapshot.objects(User.self) {
            realm.create(User.self, value: user)
        }
    }
}

Keep in mind: This works best for smaller realms—copying large datasets back can be slow, and frozen realms do take up memory, though they’re optimized to share data with the live realm where possible.

Key Things to Remember
  • Realm doesn’t have an out-of-the-box undo manager like Core Data, so you’ll need to implement tracking yourself.
  • Always perform undo/redo operations in a single write transaction to ensure atomicity and consistency.
  • Tailor your approach to your app’s needs: use manual snapshots for specific object changes, notifications for automated tracking, or frozen realms for full-state reverts.

内容的提问来源于stack exchange,提问作者thierryb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:12:30