【问题标题】:NSUndoManager undo not working with Core Data on iOSNSUndoManager 撤消不适用于 iOS 上的核心数据
【发布时间】:2011-11-15 07:40:15
【问题描述】:

在处理复杂模型时,我遇到了 NSUndoManager 无法撤消的问题。

这是我的模型。

我有一个处理核心数据的单例,这是它的初始化:

        model =[NSManagedObjectModel mergedModelFromBundles:nil];

        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

        NSString *path = pathInDocumentDirectory(@"store.data");
        NSURL *storeURL = [NSURL fileURLWithPath:path];

        NSError *error = nil;

        if(![psc addPersistentStoreWithType:NSSQLiteStoreType
                              configuration:nil
                                        URL:storeURL 
                                    options:nil 
                                      error:&error]) {
            [NSException raise:@"Open failed" format:@"Reason: %@", [error localizedDescription]];
        }

        context = [[NSManagedObjectContext alloc] init];

        NSUndoManager *contextUndoManager = [[NSUndoManager alloc] init];
        [contextUndoManager setLevelsOfUndo:20];    
        [context setUndoManager:contextUndoManager];

        [context setPersistentStoreCoordinator:psc];

对于每个实体,我都有一个调用 [self initWithEntity...] 的 init 方法,然后初始化一些属性。这是 HalfEdge 实体的一个示例:

- (id) initWithVertex:(Vertex*) vert inManagedObjectContext: context {

    NSEntityDescription* tEntityDescription = [NSEntityDescription entityForName: @"HalfEdge"
                                                          inManagedObjectContext: context];

    self = [self initWithEntity: tEntityDescription insertIntoManagedObjectContext: context];       
    if(self) {
        self.lastVertex = vert;
        [self.lastVertex addHalfEdgeObject:self];
    }

    return self;
}

当用户添加新绘图时,我会创建一个新的绘图实体,然后让用户在屏幕上添加点。对于每个点,都会执行一个可能添加和/或删除三角形实体、半边和顶点的规则。这是电话:

[[[DrawingsStore sharedStore].managedObjectContext undoManager] beginUndoGrouping];

[delaunay addPoint:CGPointMake(localizacion.x-dummy.bounds.size.width/2, localizacion.y-dummy.bounds.size.height/2)];

[[DrawingsStore sharedStore].managedObjectContext processPendingChanges];
[[[DrawingsStore sharedStore].managedObjectContext undoManager] endUndoGrouping];

如您所见,我为该规则中发生的所有事情设置了一个撤消组。

然后当按下按钮时,我正在调用 [[context undoManager] undo];但它什么也没做。

我在撤消之前和之后打印一个提取,它是相同的。我可以看到 rutine 工作正常,将所有正确的实体添加到核心数据中,但它根本不会撤消任何操作。

根据 Aderstedt 的建议进行编辑

好的,我删除了 NSManagedObject 子类的自定义 init 方法,并创建了一个这样的类方法:

+ (HalfEdge*) addWithVertex:(Vertex*) vert inManagedObjectContext: context {

    HalfEdge* halfEdge = [NSEntityDescription insertNewObjectForEntityForName:@"HalfEdge" inManagedObjectContext:context];

    if(halfEdge) {
        halfEdge.lastVertex = vert;
        [halfEdge.lastVertex addHalfEdgeObject:self];
    }

    return halfEdge;
}

结果还是一样。对象被创建,撤消不起作用。 (canUndo 返回 1)

编辑

哇,我刚刚注册了 undoManager 的 NSUndoManagerCheckpointNotification,一旦我点击撤消,它就会像循环一样被永久发布。好的,现在我知道我一定在某个地方做错了,但是……在哪里?

【问题讨论】:

    标签: objective-c core-data


    【解决方案1】:

    好的,我发现了。原来我找错地方了。

    尝试调试 NSUndoManager 我注册了通知,发现 NSUndoManagerCheckpointNotification 被一遍又一遍地调用。

    [delaunay addPoint...] 对模型进行所有更改。但同时有一个渲染例程正在运行,将三角形渲染到屏幕上。在那个例程中,我设置了这些三角形的颜色。我需要在那里做,因为我不知道在渲染屏幕背景之前应该放置什么颜色。

    对 NSManagedObject 子类 Triangle 的颜色属性所做的更改导致 NSUndoManagerCheckpointNotification 被触发并且撤消不起作用。如果我删除它,撤消工作。

    所以我想我只需要添加这个,所以在渲染期间所做的更改不会进入撤消堆栈。

    [[[DibujosStore sharedStore] managedObjectContext] processPendingChanges];
    [[[[DibujosStore sharedStore] managedObjectContext] undoManager] disableUndoRegistration];
    [renderer render];
    [[[DibujosStore sharedStore] managedObjectContext] processPendingChanges];         
    [[[[DibujosStore sharedStore] managedObjectContext] undoManager] enableUndoRegistration];
    

    【讨论】:

      【解决方案2】:

      您正在创建 NSManagedObject 实例 The Wrong Way™。使用

      - [NSEntityDescription insertNewObjectForEntityForName:... inManagedObjectContext...]
      

      插入新对象。如果要在插入对象时对其进行自定义处理,请覆盖

      - (void)awakeFromInsert
      

      在您的 NSManagedObject 子类中。请查看 Core Data 文档,它明确指出不鼓励您覆盖 initWithEntity...。现在,至于您的撤消问题,请致电

      [delaunay addPoint:CGPointMake(localizacion.x-dummy.bounds.size.width/2, localizacion.y-dummy.bounds.size.height/2)];
      

      ... 这真的会改变 Core Data 对象的任何属性吗?其他实例变量,缓存数组等。不会自动注册撤消。如果您确实更改了 Core Data 对象的属性,请检查 [context undoManager] 是否为 nil。

      【讨论】:

      • 感谢您的回复。我会试试你说的。但是我确实阅读了有关子类化 NSManagedObject 的文档,正如您所看到的,我实际上并没有覆盖 initWithEntity ......我知道这样做似乎不太正确,但我在某处看到它,它实际上似乎工作,正如我所说的,实体正在被创建并添加到核心数据数据库中。至于 awakeFromInsert... 有没有办法以这种方式将一些参数传递给初始化程序?
      • -insertNewObjectForEntityForName 返回插入的对象,您可以使用它来设置一些初始参数。
      • [context undoManager] 是零吗? [delaunay addPoint:...] 是否真的在改变 Core Data 对象的属性?你能发布那个方法的代码吗?
      • 调用-initWithEntity:insertIntoManagedObjectContext: 不是错误的方法。 +insertNewObjectForEntityForName:inManagedObjectContext: 的文档清楚地表明它在内部执行的操作等同于调用 -initWithEntity:insertIntoManagedObjectContext:
      • 但是文档还声明不鼓励您覆盖 -initWithEntity:insertIntoManagedObjectContext。如果 Apple 积极阻止它,我肯定会称其为错误的方式。尤其是当有更简单的方法来完成任务时。
      猜你喜欢
      • 2011-05-31
      • 1970-01-01
      • 2012-02-12
      • 2010-11-17
      • 1970-01-01
      • 2014-12-21
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      相关资源
      最近更新 更多