Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/38.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
Iphone CoreData版本控制和阻止轻量级迁移_Iphone_Ios_Cocoa Touch_Core Data_Core Data Migration - Fatal编程技术网

Iphone CoreData版本控制和阻止轻量级迁移

Iphone CoreData版本控制和阻止轻量级迁移,iphone,ios,cocoa-touch,core-data,core-data-migration,Iphone,Ios,Cocoa Touch,Core Data,Core Data Migration,我已经启用了核心数据模型的版本控制,并且一直在使用轻量级迁移。我的代码总是尝试进行轻量级迁移,如果因为模型不兼容而失败,它会返回到删除所有现有数据并从服务器重新蚀刻。 因此,轻量级迁移只是为了提高效率,而不是为了正确性 我现在想做的是修改我的模型,理论上轻量级迁移可以处理,但实际上我需要从服务器获取新数据。我想以某种方式标记模型,而不是通过轻量级迁移进行升级。例如,如果某个字段名没有更改,但该字段的含义已更改,以致旧代码与新代码库不兼容。(这只是一个例子。) 有没有人找到一种方法将两个模型标记为

我已经启用了核心数据模型的版本控制,并且一直在使用轻量级迁移。我的代码总是尝试进行轻量级迁移,如果因为模型不兼容而失败,它会返回到删除所有现有数据并从服务器重新蚀刻。 因此,轻量级迁移只是为了提高效率,而不是为了正确性

我现在想做的是修改我的模型,理论上轻量级迁移可以处理,但实际上我需要从服务器获取新数据。我想以某种方式标记模型,而不是通过轻量级迁移进行升级。例如,如果某个字段名没有更改,但该字段的含义已更改,以致旧代码与新代码库不兼容。(这只是一个例子。)


有没有人找到一种方法将两个模型标记为不兼容,这样轻量级迁移就不会升级它们?

我以前也遇到过同样的问题

我有一种方法,可以尝试使用映射模型来迁移数据,如果要关闭轻量级迁移,应该使用映射模型

如果您不打算进行大量的数据映射,xcode将自动创建一个映射模型,该模型的工作方式与轻量级迁移完全相同。每次向核心数据添加新版本时,只需创建一个新的“映射模型”文件。只需进入“文件->新建->新建文件”,在核心数据下应该有一个映射模型模板。选择它并选择源和目标版本

我没有在github上公开我的代码,所以我只在这里发布迁移方法

- (BOOL)progressivelyMigrateURL:(NSURL*)sourceStoreURL ofType:(NSString*)type toModel:(NSManagedObjectModel*)finalModel
{
    NSError *error = nil;

    // if store dosen't exist skip migration
    NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    if(![NSBundle pathForResource:@"YongoPal" ofType:@"sqlite" inDirectory:documentDir])
    {
        migrationProgress = 1.0;
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];

        // remove migration view
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
        [self.migrationView performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];

        self.migrationView = nil;
        self.migrationProgressLabel = nil;
        self.migrationProgressView = nil;
        self.migrationSpinner = nil;

        return YES;
    }

    //START:progressivelyMigrateURLHappyCheck
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:type URL:sourceStoreURL error:&error];

    if (!sourceMetadata)
    {
        return NO;
    }

    if ([finalModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata])
    {
        migrationProgress = 1.0;
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];

        // remove migration view
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
        [self.migrationView performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];

        self.migrationView = nil;
        self.migrationProgressLabel = nil;
        self.migrationProgressView = nil;
        self.migrationSpinner = nil;

        error = nil;
        return YES;
    }
    else
    {
        migrationProgress = 0.0;
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:NO waitUntilDone:YES];
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];        
    }
    //END:progressivelyMigrateURLHappyCheck

    //START:progressivelyMigrateURLFindModels
    //Find the source model
    NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata];
    if(sourceModel == nil)
    {
        NSLog(@"%@", [NSString stringWithFormat:@"Failed to find source model\n%@", [sourceMetadata description]]);
        return NO;
    }

    //Find all of the mom and momd files in the Resources directory
    NSMutableArray *modelPaths = [NSMutableArray array];
    NSArray *momdArray = [[NSBundle mainBundle] pathsForResourcesOfType:@"momd" inDirectory:nil];
    for (NSString *momdPath in momdArray)
    {
        NSAutoreleasePool *pool = [NSAutoreleasePool new];
        NSString *resourceSubpath = [momdPath lastPathComponent];
        NSArray *array = [[NSBundle mainBundle] pathsForResourcesOfType:@"mom" inDirectory:resourceSubpath];
        [modelPaths addObjectsFromArray:array];
        [pool drain];
    }

    NSArray* otherModels = [[NSBundle mainBundle] pathsForResourcesOfType:@"mom" inDirectory:nil];
    [modelPaths addObjectsFromArray:otherModels];

    if (!modelPaths || ![modelPaths count])
    {
        //Throw an error if there are no models
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:@"No models found in bundle" forKey:NSLocalizedDescriptionKey];

        //Populate the error
        error = [NSError errorWithDomain:@"com.yongopal.coredata" code:500 userInfo:dict];
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        return NO;
    }
    //END:progressivelyMigrateURLFindModels

    //See if we can find a matching destination model
    //START:progressivelyMigrateURLFindMap
    NSMappingModel *mappingModel = nil;
    NSManagedObjectModel *targetModel = nil;
    NSString *modelPath = nil;

    for(modelPath in modelPaths)
    {
        targetModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
        mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:targetModel];

        //If we found a mapping model then proceed
        if(mappingModel)
        {
            break;
        }
        else
        {
            //Release the target model and keep looking
            [targetModel release];
            targetModel = nil;
        }
    }

    //We have tested every model, if nil here we failed
    if (!mappingModel)
    {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:@"No mapping models found in bundle" forKey:NSLocalizedDescriptionKey];
        error = [NSError errorWithDomain:@"com.yongopal.coredata" code:500 userInfo:dict];
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        return NO;
    }
    //END:progressivelyMigrateURLFindMap

    //We have a mapping model and a destination model.  Time to migrate
    //START:progressivelyMigrateURLMigrate
    NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:targetModel];

    // reg KVO for migration progress
    [manager addObserver:self forKeyPath:@"migrationProgress" options:NSKeyValueObservingOptionNew context:NULL];

    NSString *modelName = [[modelPath lastPathComponent] stringByDeletingPathExtension];
    NSString *storeExtension = [[sourceStoreURL path] pathExtension];
    NSString *storePath = [[sourceStoreURL path] stringByDeletingPathExtension];

    //Build a path to write the new store
    storePath = [NSString stringWithFormat:@"%@.%@.%@", storePath, modelName, storeExtension];
    NSURL *destinationStoreURL = [NSURL fileURLWithPath:storePath];

    if (![manager migrateStoreFromURL:sourceStoreURL type:type options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:type destinationOptions:nil error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        [targetModel release];
        [manager removeObserver:self forKeyPath:@"migrationProgress"];
        [manager release];
        return NO;
    }
    [targetModel release];
    [manager removeObserver:self forKeyPath:@"migrationProgress"];
    [manager release];
    //END:progressivelyMigrateURLMigrate

    //Migration was successful, move the files around to preserve the source
    //START:progressivelyMigrateURLMoveAndRecurse
    NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
    guid = [guid stringByAppendingPathExtension:modelName];
    guid = [guid stringByAppendingPathExtension:storeExtension];
    NSString *appSupportPath = [storePath stringByDeletingLastPathComponent];
    NSString *backupPath = [appSupportPath stringByAppendingPathComponent:guid];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager moveItemAtPath:[sourceStoreURL path] toPath:backupPath error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        //Failed to copy the file
        return NO;
    }

    //Move the destination to the source path
    if (![fileManager moveItemAtPath:storePath toPath:[sourceStoreURL path] error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        //Try to back out the source move first, no point in checking it for errors
        [fileManager moveItemAtPath:backupPath toPath:[sourceStoreURL path] error:nil];
        return NO;
    }

    //We may not be at the "current" model yet, so recurse
    return [self progressivelyMigrateURL:sourceStoreURL ofType:type toModel:finalModel];
    //END:progressivelyMigrateURLMoveAndRecurse
}
这是我从一些核心数据书中得到的一个方法的编辑版本,我记不起它的标题了。我希望我能赞扬作者

注意,我这里有一些代码,您应该在实现中删除它们。这主要是我用来更新迁移进度视图的东西

您可以这样使用此方法:

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"YongoPal.sqlite"];

// perform core data migrations if necessary
if(![self progressivelyMigrateURL:storeURL ofType:NSSQLiteStoreType toModel:self.managedObjectModel])
{
    // reset the persistent store on fail
    NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSError *error = nil;
    [[NSFileManager defaultManager] removeItemAtPath:[NSBundle pathForResource:@"YongoPal" ofType:@"sqlite" inDirectory:documentDir] error:&error];
}
else
{
    NSLog(@"migration succeeded!");
}

请记住在使用此选项之前删除轻量级迁移选项。

我以前也遇到过同样的问题

我有一种方法,可以尝试使用映射模型来迁移数据,如果要关闭轻量级迁移,应该使用映射模型

如果您不打算进行大量的数据映射,xcode将自动创建一个映射模型,该模型的工作方式与轻量级迁移完全相同。每次向核心数据添加新版本时,只需创建一个新的“映射模型”文件。只需进入“文件->新建->新建文件”,在核心数据下应该有一个映射模型模板。选择它并选择源和目标版本

我没有在github上公开我的代码,所以我只在这里发布迁移方法

- (BOOL)progressivelyMigrateURL:(NSURL*)sourceStoreURL ofType:(NSString*)type toModel:(NSManagedObjectModel*)finalModel
{
    NSError *error = nil;

    // if store dosen't exist skip migration
    NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    if(![NSBundle pathForResource:@"YongoPal" ofType:@"sqlite" inDirectory:documentDir])
    {
        migrationProgress = 1.0;
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];

        // remove migration view
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
        [self.migrationView performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];

        self.migrationView = nil;
        self.migrationProgressLabel = nil;
        self.migrationProgressView = nil;
        self.migrationSpinner = nil;

        return YES;
    }

    //START:progressivelyMigrateURLHappyCheck
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:type URL:sourceStoreURL error:&error];

    if (!sourceMetadata)
    {
        return NO;
    }

    if ([finalModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata])
    {
        migrationProgress = 1.0;
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];

        // remove migration view
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:YES];
        [self.migrationView performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];

        self.migrationView = nil;
        self.migrationProgressLabel = nil;
        self.migrationProgressView = nil;
        self.migrationSpinner = nil;

        error = nil;
        return YES;
    }
    else
    {
        migrationProgress = 0.0;
        [self.migrationView performSelectorOnMainThread:@selector(setHidden:) withObject:NO waitUntilDone:YES];
        [self performSelectorOnMainThread:@selector(updateMigrationProgress) withObject:nil waitUntilDone:YES];        
    }
    //END:progressivelyMigrateURLHappyCheck

    //START:progressivelyMigrateURLFindModels
    //Find the source model
    NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata];
    if(sourceModel == nil)
    {
        NSLog(@"%@", [NSString stringWithFormat:@"Failed to find source model\n%@", [sourceMetadata description]]);
        return NO;
    }

    //Find all of the mom and momd files in the Resources directory
    NSMutableArray *modelPaths = [NSMutableArray array];
    NSArray *momdArray = [[NSBundle mainBundle] pathsForResourcesOfType:@"momd" inDirectory:nil];
    for (NSString *momdPath in momdArray)
    {
        NSAutoreleasePool *pool = [NSAutoreleasePool new];
        NSString *resourceSubpath = [momdPath lastPathComponent];
        NSArray *array = [[NSBundle mainBundle] pathsForResourcesOfType:@"mom" inDirectory:resourceSubpath];
        [modelPaths addObjectsFromArray:array];
        [pool drain];
    }

    NSArray* otherModels = [[NSBundle mainBundle] pathsForResourcesOfType:@"mom" inDirectory:nil];
    [modelPaths addObjectsFromArray:otherModels];

    if (!modelPaths || ![modelPaths count])
    {
        //Throw an error if there are no models
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:@"No models found in bundle" forKey:NSLocalizedDescriptionKey];

        //Populate the error
        error = [NSError errorWithDomain:@"com.yongopal.coredata" code:500 userInfo:dict];
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        return NO;
    }
    //END:progressivelyMigrateURLFindModels

    //See if we can find a matching destination model
    //START:progressivelyMigrateURLFindMap
    NSMappingModel *mappingModel = nil;
    NSManagedObjectModel *targetModel = nil;
    NSString *modelPath = nil;

    for(modelPath in modelPaths)
    {
        targetModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
        mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:targetModel];

        //If we found a mapping model then proceed
        if(mappingModel)
        {
            break;
        }
        else
        {
            //Release the target model and keep looking
            [targetModel release];
            targetModel = nil;
        }
    }

    //We have tested every model, if nil here we failed
    if (!mappingModel)
    {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:@"No mapping models found in bundle" forKey:NSLocalizedDescriptionKey];
        error = [NSError errorWithDomain:@"com.yongopal.coredata" code:500 userInfo:dict];
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        return NO;
    }
    //END:progressivelyMigrateURLFindMap

    //We have a mapping model and a destination model.  Time to migrate
    //START:progressivelyMigrateURLMigrate
    NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:targetModel];

    // reg KVO for migration progress
    [manager addObserver:self forKeyPath:@"migrationProgress" options:NSKeyValueObservingOptionNew context:NULL];

    NSString *modelName = [[modelPath lastPathComponent] stringByDeletingPathExtension];
    NSString *storeExtension = [[sourceStoreURL path] pathExtension];
    NSString *storePath = [[sourceStoreURL path] stringByDeletingPathExtension];

    //Build a path to write the new store
    storePath = [NSString stringWithFormat:@"%@.%@.%@", storePath, modelName, storeExtension];
    NSURL *destinationStoreURL = [NSURL fileURLWithPath:storePath];

    if (![manager migrateStoreFromURL:sourceStoreURL type:type options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:type destinationOptions:nil error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        [targetModel release];
        [manager removeObserver:self forKeyPath:@"migrationProgress"];
        [manager release];
        return NO;
    }
    [targetModel release];
    [manager removeObserver:self forKeyPath:@"migrationProgress"];
    [manager release];
    //END:progressivelyMigrateURLMigrate

    //Migration was successful, move the files around to preserve the source
    //START:progressivelyMigrateURLMoveAndRecurse
    NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
    guid = [guid stringByAppendingPathExtension:modelName];
    guid = [guid stringByAppendingPathExtension:storeExtension];
    NSString *appSupportPath = [storePath stringByDeletingLastPathComponent];
    NSString *backupPath = [appSupportPath stringByAppendingPathComponent:guid];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager moveItemAtPath:[sourceStoreURL path] toPath:backupPath error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        //Failed to copy the file
        return NO;
    }

    //Move the destination to the source path
    if (![fileManager moveItemAtPath:storePath toPath:[sourceStoreURL path] error:&error])
    {
        if([[self.prefs valueForKey:@"debugMode"] isEqualToString:@"Y"])
        {
            NSLog(@"error: %@", error);
        }
        //Try to back out the source move first, no point in checking it for errors
        [fileManager moveItemAtPath:backupPath toPath:[sourceStoreURL path] error:nil];
        return NO;
    }

    //We may not be at the "current" model yet, so recurse
    return [self progressivelyMigrateURL:sourceStoreURL ofType:type toModel:finalModel];
    //END:progressivelyMigrateURLMoveAndRecurse
}
这是我从一些核心数据书中得到的一个方法的编辑版本,我记不起它的标题了。我希望我能赞扬作者

注意,我这里有一些代码,您应该在实现中删除它们。这主要是我用来更新迁移进度视图的东西

您可以这样使用此方法:

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"YongoPal.sqlite"];

// perform core data migrations if necessary
if(![self progressivelyMigrateURL:storeURL ofType:NSSQLiteStoreType toModel:self.managedObjectModel])
{
    // reset the persistent store on fail
    NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSError *error = nil;
    [[NSFileManager defaultManager] removeItemAtPath:[NSBundle pathForResource:@"YongoPal" ofType:@"sqlite" inDirectory:documentDir] error:&error];
}
else
{
    NSLog(@"migration succeeded!");
}
请记住在使用此选项之前删除轻量级迁移选项。

苹果公司的解决方案是,如果“一个模型有两个版本,核心数据通常将其视为等效的,而您希望被视为不同的”,我想这正是您要问的

答案是在新模型中的一个实体或属性上设置versionHashModifier

在你的例子中,你会在含义已经改变的领域中这样做。

苹果公司的说明是,如果“你有一个模型的两个版本,核心数据通常将其视为等同的,而你希望被认为是不同的”,我想这就是你要问的

答案是在新模型中的一个实体或属性上设置versionHashModifier


在您的示例中,您将在含义已更改的字段上执行此操作。

我不明白此代码如何解决我的问题。重申一下,我需要一种方式来说明这两种模式是不兼容的。在XCode中创建映射模型文件后,我应该对它做些什么吗?创建映射模型后,请尝试从XCode(4.x)中查看它,它应该会列出实体映射和属性映射的列表。在属性映射列表中,您应该可以看到目标和值表达式。只需删除要从迁移中排除的属性的值表达式(看起来应该像$source.{attribute_name}),使其为空。当您运行我的ProgressiveyMigrateUrl:ofType:toModel:方法时,它将迁移除具有空值表达式的属性之外的所有属性。该方法来自Marcus S.Zarra的“Core Data:Apple API,用于在Mac OSX上持久化数据”。我认为它现在被称为“核心数据:iOS、OSX和iCloud的数据存储和管理”。我不明白这段代码如何解决我的问题。重申一下,我需要一种方式来说明这两种模式是不兼容的。在XCode中创建映射模型文件后,我应该对它做些什么吗?创建映射模型后,请尝试从XCode(4.x)中查看它,它应该会列出实体映射和属性映射的列表。在属性映射列表中,您应该可以看到目标和值表达式。只需删除要从迁移中排除的属性的值表达式(看起来应该像$source.{attribute_name}),使其为空。当您运行我的ProgressiveyMigrateUrl:ofType:toModel:方法时,它将迁移除具有空值表达式的属性之外的所有属性。该方法来自Marcus S.Zarra的“Core Data:Apple API,用于在Mac OSX上持久化数据”。我认为它现在被称为“核心数据:iOS、OSX和iCloud的数据存储和管理”。。