NSUndoManager 撤消不使用核心数据

发布于 2024-08-21 22:52:17 字数 2044 浏览 11 评论 0原文

我正在尝试创建一个 iPhone 应用程序,用户可以在其中添加条目。当他按下新条目时,会弹出一个框,询问他一些信息。然后他可以按“取消”或“保存”来放弃数据或将其保存到磁盘。

为了保存,我使用了 Core Data 框架,它运行得很好。但是,我无法使用“取消”按钮。当弹出窗口询问信息时,我在托管对象上下文 (MOC) 中创建一个新对象。然后,当用户按下取消时,我尝试使用属于 MOC 的 NSUndoManager。

我还想使用嵌套撤消组来执行此操作,因为可能存在嵌套组。

为了测试这一点,我编写了一个简单的应用程序。该应用程序只是启用了核心数据的“基于窗口的应用程序”模板。对于核心数据模型,我创建了一个名为“Entity”且具有整数属性“x”的实体。然后在 applicationDidFinishLaunching 中,我添加以下代码:

- (void)applicationDidFinishLaunching:(UIApplication *)application {    

  // Override point for customization after app launch    

  unsigned int x=arc4random()%1000;
  [self.managedObjectContext processPendingChanges];
  [self.managedObjectContext.undoManager beginUndoGrouping];

  NSManagedObject *entity=[NSEntityDescription insertNewObjectForEntityForName:@"Entity" 
                                                        inManagedObjectContext:self.managedObjectContext];
  [entity setValue:[NSNumber numberWithInt:x] forKey:@"x"];
  NSLog(@"Insert Value %d",x);

  [self.managedObjectContext processPendingChanges];
  [self.managedObjectContext.undoManager endUndoGrouping];
  [self.managedObjectContext.undoManager undoNestedGroup];

  NSFetchRequest *fetchRequest=[[NSFetchRequest alloc] init];
  NSEntityDescription *entityEntity=[NSEntityDescription entityForName:@"Entity"
                                                inManagedObjectContext:self.managedObjectContext];
  [fetchRequest setEntity:entityEntity];
  NSArray *result=[self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
  for(entity in result) {
    NSLog(@"FETCHED ENTITY %d",[[entity valueForKey:@"x"] intValue]);
  }

    [window makeKeyAndVisible];
}

这个想法很简单。尝试插入一个新的实体对象,撤消它,获取 MOC 中的所有实体对象并将它们打印出来。如果一切正常,最后应该不会有任何物体。

但是,我得到以下输出:

[Session started at 2010-02-20 13:41:49 -0800.]
2010-02-20 13:41:51.695 Untitledundotes[7373:20b] Insert Value 136
2010-02-20 13:41:51.715 Untitledundotes[7373:20b] FETCHED ENTITY 136

如您所见,在我尝试撤消其创建后,该对象出现在 MOC 中。 关于我做错了什么有什么建议吗?

I am trying to create an iPhone application where the user can add entries. When he presses a new entry, a box will popup asking him for some information. Then he can either press "Cancel" or "Save" to discard the data or save it to disk.

For saving, I am using the Core Data framework, which works pretty well. However, I cannot get the "Cancel" button to work. When the window pops up, asking for information, I create a new object in the managed object context (MOC). Then when the user presses cancel, I try to use the NSUndoManager belonging to the MOC.

I would also like to do it using nested undo groups, because there might be nested groups.

To test this, I wrote a simple application. The application is just the "Window based application" template with Core Data enabled. For the Core Data model, I create a single entity called "Entity" with integer attribute "x". Then inside the applicationDidFinishLaunching, I add this code:

- (void)applicationDidFinishLaunching:(UIApplication *)application {    

  // Override point for customization after app launch    

  unsigned int x=arc4random()%1000;
  [self.managedObjectContext processPendingChanges];
  [self.managedObjectContext.undoManager beginUndoGrouping];

  NSManagedObject *entity=[NSEntityDescription insertNewObjectForEntityForName:@"Entity" 
                                                        inManagedObjectContext:self.managedObjectContext];
  [entity setValue:[NSNumber numberWithInt:x] forKey:@"x"];
  NSLog(@"Insert Value %d",x);

  [self.managedObjectContext processPendingChanges];
  [self.managedObjectContext.undoManager endUndoGrouping];
  [self.managedObjectContext.undoManager undoNestedGroup];

  NSFetchRequest *fetchRequest=[[NSFetchRequest alloc] init];
  NSEntityDescription *entityEntity=[NSEntityDescription entityForName:@"Entity"
                                                inManagedObjectContext:self.managedObjectContext];
  [fetchRequest setEntity:entityEntity];
  NSArray *result=[self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
  for(entity in result) {
    NSLog(@"FETCHED ENTITY %d",[[entity valueForKey:@"x"] intValue]);
  }

    [window makeKeyAndVisible];
}

The idea is simple. Try to insert a new Entity object, undo it, fetch all Entity objects in the MOC and print them out. If everything worked correctly, there should be no objects at the end.

However, I get this output:

[Session started at 2010-02-20 13:41:49 -0800.]
2010-02-20 13:41:51.695 Untitledundotes[7373:20b] Insert Value 136
2010-02-20 13:41:51.715 Untitledundotes[7373:20b] FETCHED ENTITY 136

As you can see, the object is present in the MOC after I try to undo its creation.
Any suggestions as to what I am doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

沫尐诺 2024-08-28 22:52:17

您的问题是由于以下事实引起的:与 OS X 不同,iPhone 托管对象上下文默认不包含撤消管理器。您需要明确添加一个。

将应用程序委托中的 ManagedObjectContext 属性生成的代码更改为如下所示:

- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        //add the following 3 lines of code
        NSUndoManager *undoManager = [[NSUndoManager alloc] init];
        [managedObjectContext setUndoManager:undoManager];
        [undoManager release];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }

    return managedObjectContext;

}

进行更改后,不再打印第二条日志消息。

希望有帮助...

戴夫

Your problem is caused by the fact that, unlike OS X, the iPhone managed object context does not contain an undo manager by default. You need to explicitly add one.

Change the generated code in the app delegate for the managedObjectContext property to look like this:

- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        //add the following 3 lines of code
        NSUndoManager *undoManager = [[NSUndoManager alloc] init];
        [managedObjectContext setUndoManager:undoManager];
        [undoManager release];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }

    return managedObjectContext;

}

After making that change, the 2nd log message is no longer printed.

Hope that helps...

Dave

夜血缘 2024-08-28 22:52:17

我尝试过戴夫的方法,但对我不起作用。
我终于在Apple的示例中找到了解决方案 CoreDataBooks

诀窍是创建一个与您应用程序上下文共享协调器的新上下文。要放弃更改,您不需要做任何事情,只需放弃新的上下文对象即可。由于您共享协调器,因此保存会更新您的主要上下文。

这是我的改编版本,其中我使用静态对象作为临时上下文来创建新的 ChannelMO 对象。

//Gets a new ChannelMO that is part of the addingManagedContext
+(ChannelMO*) getNewChannelMO{

    // Create a new managed object context for the new channel -- set its persistent store coordinator to the same as that from the fetched results controller's context.
    NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
    addingManagedObjectContext = addingContext;

    [addingManagedObjectContext setPersistentStoreCoordinator:[[self getContext] persistentStoreCoordinator]];

    ChannelMO* aux = (ChannelMO *)[NSEntityDescription insertNewObjectForEntityForName:@"ChannelMO" inManagedObjectContext:addingManagedObjectContext];
    return aux;
}

+(void) saveAddingContext{
    NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
    [dnc addObserver:self selector:@selector(addControllerContextDidSave:) 
                name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

    NSError *error;
    if (![addingManagedObjectContext save:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
    [dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

    // Release the adding managed object context.
    addingManagedObjectContext = nil;
}

我希望它能帮助

贡索

I tried Dave approach, but did not work for me.
I finally found the solution in Apple's example CoreDataBooks

The trick is to create a new context that shares the coordinator with you App's context. To discard the changes you dont need to do a thing, just discard the new context object. Since you share the coordinator, saving updates your main context.

Here is my adapted version, where I use a static object for the temp context to create a new ChannelMO object.

//Gets a new ChannelMO that is part of the addingManagedContext
+(ChannelMO*) getNewChannelMO{

    // Create a new managed object context for the new channel -- set its persistent store coordinator to the same as that from the fetched results controller's context.
    NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
    addingManagedObjectContext = addingContext;

    [addingManagedObjectContext setPersistentStoreCoordinator:[[self getContext] persistentStoreCoordinator]];

    ChannelMO* aux = (ChannelMO *)[NSEntityDescription insertNewObjectForEntityForName:@"ChannelMO" inManagedObjectContext:addingManagedObjectContext];
    return aux;
}

+(void) saveAddingContext{
    NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
    [dnc addObserver:self selector:@selector(addControllerContextDidSave:) 
                name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

    NSError *error;
    if (![addingManagedObjectContext save:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
    [dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];

    // Release the adding managed object context.
    addingManagedObjectContext = nil;
}

I hope it helps

Gonso

不再见 2024-08-28 22:52:17

它应该有效。您是否正确地将撤消管理器分配给了 ManagedObjectContext?如果您的操作正确,默认情况下会启用撤消注册,您应该可以开始操作了。 此处有一篇关于核心数据的好文章。 这里有一个关于核心数据和 NSUndoManager 的很好的教程。希望有帮助。

It should work. Did you correctly assign the undo manager to your managedObjectContext? If you have rightly done that, it by default has undo registration enabled, and you should be good to go. There is a good article on core data here. There is a good tutorial on core data and NSUndoManager here. Hope that helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文