Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c Can’;t撤消多个操作_Objective C_Cocoa_Core Data_Nsundomanager - Fatal编程技术网

Objective c Can’;t撤消多个操作

Objective c Can’;t撤消多个操作,objective-c,cocoa,core-data,nsundomanager,Objective C,Cocoa,Core Data,Nsundomanager,当我在删除单个对象后对上下文调用undo时,所有操作都按预期进行。但是,如果用户删除了一个对象,然后又删除了另一个对象,“撤消”将仅用于恢复第二个对象,而不管用户请求“撤消”多少次,就像“撤消级别”设置为1一样。无论UndoLevel的默认值是0(无限制),还是作为测试显式设置为6,都会发生这种情况 此外,如果一个操作删除多个对象,那么随后调用undo将无效;所有对象均未恢复。我试图用begin/endUndoGrouping显式地将删除循环括起来,但没有效果。undoManager的group

当我在删除单个对象后对上下文调用undo时,所有操作都按预期进行。但是,如果用户删除了一个对象,然后又删除了另一个对象,“撤消”将仅用于恢复第二个对象,而不管用户请求“撤消”多少次,就像“撤消级别”设置为1一样。无论UndoLevel的默认值是0(无限制),还是作为测试显式设置为6,都会发生这种情况

此外,如果一个操作删除多个对象,那么随后调用undo将无效;所有对象均未恢复。我试图用begin/endUndoGrouping显式地将删除循环括起来,但没有效果。undoManager的groupsByEvent是YES(默认情况下),但无论我调用的是直接撤消还是undoNestedGroup都没有区别

每次操作后是否以某种方式保存了上下文?否,因为如果在运行这些测试后退出并重新启动应用程序,则数据库中仍存在所有对象

我错过了什么


好的,你需要密码。以下是我认为最相关的内容:

上下文获取程序:

- (NSManagedObjectContext *) managedObjectContextMain {

if (managedObjectContextMain) return managedObjectContextMain;

NSPersistentStoreCoordinator *coordinatorMain = [self persistentStoreCoordinatorMain];
if (!coordinatorMain) {
    // present error...
    return nil;
}
managedObjectContextMain = [[NSManagedObjectContext alloc] init];
[managedObjectContextMain setPersistentStoreCoordinator: coordinatorMain];

// Add undo support. (Default methods don't include this.)
NSUndoManager *undoManager = [[NSUndoManager  alloc] init];
// [undoManager setUndoLevels:6]; // makes no difference
[managedObjectContextMain setUndoManager:undoManager];
[undoManager release];

// ...

return managedObjectContextMain;
}
多对象删除方法(通过模式面板上的按钮调用):

下面是将对象从其关系和有序缓存中移除的Series方法:

/**
Removes a special object from the index sent in.
(The obj is removed from objMembers relationship and from the transient ordered obj cache, but it is NOT removed from the transformable array of objectIDrepns.)
*/
- (void) deleteObj:(SpecialObject *)sobj fromSeriesIndex:(NSUInteger)uiIndexForDeletion {
// Don't proceed if the obj is null or the series index is invalid.
if (!sobj)
    return;
if (uiIndexForDeletion >= [self.emarrObjs count]) 
    return;

// Use the safe Core Data method for removing the obj from the relationship set.
// (To keep it private, it has not been declared in h file. PerformSelector syntax here prevents compiler warning.)
[self performSelector:@selector(removeObjMembersObject:) withObject:sobj];
// Remove the obj from the transient ordered cache at the index given.
[self.emarrObjs removeObjectAtIndex:uiIndexForDeletion];

// But do NOT remove the obj’s objectID from the transformable dataObjIDsOrdered array. That doesn't happen until contextSave. In the meantime, undo/cancel can use dataObjIDsOrdered to restore this obj.
}
下面是comm-z undo调用的方法及其后续操作:

- (void) undoLastChange {
Flixen_Foundry_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *contextMain = [appDelegate managedObjectContextMain];

// Perform the undo. (Core Data has integrated this functionality so that you can call undo directly on the context, as long as it has been assigned an undo manager.)
//  [contextMain undo]; 
printf("\ncalling undo, with %lu levels.", [contextMain.undoManager levelsOfUndo]);
[contextMain.undoManager undoNestedGroup]; 

// Do cleanup.
[self cleanupFllwgUndoRedo];
}


- (void) cleanupFllwgUndoRedo {
Flixen_Foundry_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *contextMain = [appDelegate managedObjectContextMain];
DataSourceCoordinator *dataSrc = appDelegate.dataSourceCoordinator;

// ... 

// Rebuild caches of special managed objects.
// (Some managed objects have their own caches, i.e. Series' emarrObjs. These need to be refreshed if their membership has changed. There's no need to use special trackers; the context keeps track of these.)
for (NSManagedObject *obj in [contextMain updatedObjects]) {
    if ([obj isKindOfClass:[Series class]] && ![obj isDeleted])
        [((Series *)obj) rebuildSeriesCaches];
}

// ...

// Regenerate locator's caches.
[appDelegate.objLocatorController regenerateObjCachesFromMuddies]; // also reloads table

}
下面是在撤消/唤醒后重新生成缓存的series方法:

- (void) rebuildSeriesCaches {  

// Don't proceed if there are no stored IDs.
if (!self.dataObjIDsOrdered || [self.dataObjIDsOrdered count] < 1) {    
    // printf to alert me, because this shouldn’t happen (and so far it doesn’t)
    return;
}

NSMutableArray *imarrRefreshedObjIdsOrdered = [NSMutableArray arrayWithCapacity:[self.objMembers count]];
NSMutableArray *emarrRefreshedObjs = [NSMutableArray arrayWithCapacity:[self.objMembers count]];

// Loop through objectIDs (their URIRepns) that were stored in transformable dataObjIDsOrdered.
for (NSURL *objectIDurl in self.dataObjIDsOrdered) {
    // For each objectID repn, loop through the objMembers relationship, looking for a match.
    for (SpecialObject *sobj in self.objMembers) {
        // When a match is found, add the objectID repn and its obj to their respective replacement arrays.
        if ([[sobj.objectID URIRepresentation] isEqualTo:objectIDurl]) {
            [imarrRefreshedObjIdsOrdered addObject:objectIDurl];
            [emarrRefreshedObjs addObject:sobj];
            break;
        }
        // If no match is found, the obj must have been deleted; the objectID repn doesn't get added to the replacement array, so it is effectively dropped.
    }
}

// Assign their replacement arrays to the transformable and transient attrs.
self.dataObjIDsOrdered = imarrRefreshedObjIdsOrdered;
self.emarrObjs = emarrRefreshedObjs;

}
-(void)重建场景{
//如果没有存储的ID,则不要继续。
如果(!self.dataObjIDsOrdered | |[self.dataObjIDsOrdered count]<1){
//printf提醒我,因为这不应该发生(到目前为止还没有发生)
返回;
}
NSMutableArray*imarrRefreshedObjIdsOrdered=[NSMutableArray阵列容量:[self.objMembers count]];
NSMutableArray*emarrRefreshedObjs=[NSMutableArray阵列容量:[self.objMembers count]];
//循环遍历存储在可转换dataObjIDsOrdered中的ObjectId(它们的UrirePN)。
for(self.dataObjIDsOrdered中的NSURL*ObjectDurl){
//对于每个objectID repn,循环遍历objMembers关系,寻找匹配项。
for(self.objMembers中的SpecialObject*sobj){
//找到匹配项后,将objectID repn及其obj添加到各自的替换数组中。
if([[sobj.objectID-URIRepresentation]isEqualTo:objectIDurl]){
[IMARRRefreshedObjidsOrderedAddObject:ObjectDurl];
[emarrRefreshedObjs addObject:sobj];
打破
}
//如果未找到匹配项,则obj必须已被删除;objectID repn不会添加到替换数组中,因此它会被有效删除。
}
}
//将其替换数组分配给可转换和瞬态属性。
self.dataObjIDsOrdered=imarrRefreshedObjIdsOrdered;
self.emarrObjs=emarrRefreshedObjs;
}
(我省略了定位器的RegeneratObjCachesFromMuddies,因为尽管我使用它的表来查看删除和撤消的结果,但我可以使用新的获取重新加载表,完全重新生成表的缓存,并且此测试仍然显示撤消不起作用。)


像往常一样,仅仅是把一个SO问题放在一起的任务就有助于解决问题,我现在意识到只要我处理的是不涉及交互SpecialObject系列关系的简单对象,撤消就可以很好地工作。我在那里做错了什么…

我认为您正在与自定义撤消功能和Core Data的automagic支持进行斗争

在正常的撤消/重做代码中,您有可撤消的漏斗点。通常是一个可撤消的add和它的逆可撤消的remove。调用一个将另一个注册为反向操作,反之亦然。用户撤销/重做然后在它们之间来回移动。您可以将“用户创建的新Foo”代码与“现在将此Foo以可撤销方式添加到集合”代码分开(这样,“删除Foo”和“添加Foo”工作独立于提供新创建的Foo)

对于核心数据,添加和删除意味着“插入上下文并从上下文中删除”。另外,您仍然需要自定义漏斗方法,因为(在您的情况下)您正在做一些额外的事情(更新缓存)。这对于一个Foo来说很容易做到,但是当您想要操纵在一个操作中创建的Foo/Bar程序集之间的关系时会发生什么呢

如果创建一个Foo用它创建了几个条,那是一回事(-awakeFromInsert等等),因为您只需要更新缓存(顺便说一句,您可以通过键/值来观察上下文的更改)。由于创建Foo似乎与现有的条(已经在上下文中)建立了关系,因此在尝试与CD的内置撤销支持合作时,您会遇到困难

如果您使用内置的核心数据撤消/重做支持,那么在这种情况下没有简单的解决方案。在这种情况下,您可以选择并关闭它。然后,您可以完全自己处理撤消/重做。。。但是您需要编写大量代码来观察对象对有趣属性的更改,并为每个属性注册反向操作

虽然这并不能解决你的问题,但我希望它至少能指出你正在努力做的事情的复杂性,并为你提供一条可能的前进道路。如果不了解更多关于模型的信息(至少在概念层面上)以及UI如何向用户展示模型,就很难给出具体的架构建议


我希望我在这个案子上是错的——也许其他人能给你一个更好的答案。:-)

事实证明,您可以创建包含更改与预先存在的条和自定义缓存的关系的Foo,而NSUndoManager仍然可以处理这一切,但有一个难题:每次更改后,您必须保存上下文;否则,撤消管理器将停止工作

由于撤消实际上可以返回到保存之前的状态,所以这并不是一件坏事。如果你
- (void) rebuildSeriesCaches {  

// Don't proceed if there are no stored IDs.
if (!self.dataObjIDsOrdered || [self.dataObjIDsOrdered count] < 1) {    
    // printf to alert me, because this shouldn’t happen (and so far it doesn’t)
    return;
}

NSMutableArray *imarrRefreshedObjIdsOrdered = [NSMutableArray arrayWithCapacity:[self.objMembers count]];
NSMutableArray *emarrRefreshedObjs = [NSMutableArray arrayWithCapacity:[self.objMembers count]];

// Loop through objectIDs (their URIRepns) that were stored in transformable dataObjIDsOrdered.
for (NSURL *objectIDurl in self.dataObjIDsOrdered) {
    // For each objectID repn, loop through the objMembers relationship, looking for a match.
    for (SpecialObject *sobj in self.objMembers) {
        // When a match is found, add the objectID repn and its obj to their respective replacement arrays.
        if ([[sobj.objectID URIRepresentation] isEqualTo:objectIDurl]) {
            [imarrRefreshedObjIdsOrdered addObject:objectIDurl];
            [emarrRefreshedObjs addObject:sobj];
            break;
        }
        // If no match is found, the obj must have been deleted; the objectID repn doesn't get added to the replacement array, so it is effectively dropped.
    }
}

// Assign their replacement arrays to the transformable and transient attrs.
self.dataObjIDsOrdered = imarrRefreshedObjIdsOrdered;
self.emarrObjs = emarrRefreshedObjs;

}
NSMutableArray *emarrRemoveID = [self.dataObjIDsOrdered mutableCopy];
[emarrRemoveID removeObjectAtIndex:uiIndexForDeletion];
self.dataObjIDsOrdered = emarrRemoveID;
[emarrRemoveID release];