replaceItemAtURL: 方法在某些情况下无法删除原始移动文件

Posted

技术标签:

【中文标题】replaceItemAtURL: 方法在某些情况下无法删除原始移动文件【英文标题】:The replaceItemAtURL: method fails to remove original moved files in some cases 【发布时间】:2018-09-19 18:56:37 【问题描述】:

我正在使用 NSFileManager 的 replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: 方法移动一个 sqlite 文件,以防使用 replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error: 方法移动失败。该文件包含三个组件文件——一个以 .sqlite 结尾,一个以 .sqlite-wal 结尾,一个以 .sqlite-shm 结尾。所有文件都使用replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error: 方法正确替换了它们现有的对应文件;但是,只有移动的 .sqlite 文件实际上是从其原始位置删除的。 .sqlite-wal 和 .sqlite-shm 文件确实会根据需要进行替换,但似乎它们实际上是被复制而不是移动的,因为这两个原始文件在它们成功的 replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error: 操作结束时仍然存在。一切都在同一卷内发生,因此似乎没有理由制作副本。有人可以帮我弄清楚为什么会发生这种情况吗?

这里是代码。稍后记录的状态消息显示为:

已成功替换 SQLITE 文件。 SQLITE 文件不存在于 原来的位置。成功替换 WAL 文件。 WAL 文件仍然存在 存在于原始位置。已成功替换 SHM 文件。 SHM 文件 DOES 仍然存在于原始位置。

- (void)moveSqliteFileFromMigrateStorePathToFinalStorePathWithCompletionHandler:(void(^)(NSURL * migrateStoreURL,NSString * statusMessage,BOOL actuallyMovedFiles,BOOL movingHudIsRunning))handler 

    __block NSString *statusMessage = nil;
    __block BOOL actuallyMovedFiles = NO;
    __block BOOL hudIsRunning = NO;

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *applicationDocumentsDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    NSString *migrateStorePath = [applicationDocumentsDirectory stringByAppendingPathComponent:@"MyAppData.sqlite"];
    NSURL *migrateStoreURL = [NSURL fileURLWithPath:migrateStorePath];
    NSURL *finalStoreURL = [CoreDataController desiredFinalStoreURL];
    NSString *finalStorePath = finalStoreURL.path;
    NSString *fromWalPath = [migrateStorePath stringByAppendingString:@"-wal"];
    NSString *fromShmPath = [migrateStorePath stringByAppendingString:@"-shm"];
    BOOL walFileExists = [NSFileManager.defaultManager fileExistsAtPath:fromWalPath];
    BOOL shmFileExists = [NSFileManager.defaultManager fileExistsAtPath:fromShmPath];
    BOOL sqliteFileExists = [NSFileManager.defaultManager fileExistsAtPath:migrateStorePath];

    if (sqliteFileExists || shmFileExists || walFileExists) 

        [SVProgressHUD setForegroundColor:CPS_DARK_BLUE_COLOR];
        [SVProgressHUD showWithStatus:NSLocalizedString(@"My App is updating. This one-time operation may take several minutes.",@"")];

        hudIsRunning = YES;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^

            if (sqliteFileExists) 
                BOOL finalStorePathFileExists = [NSFileManager.defaultManager fileExistsAtPath:finalStorePath];
                NSError * sqliteMoveError = nil;
                BOOL successfulSqliteMove = NO;
                BOOL replacingSqliteFile = NO;
                if (finalStorePathFileExists) 
                    replacingSqliteFile = YES;
                    NSURL *migrateStoreURL = [NSURL fileURLWithPath:migrateStorePath];
                    NSURL *finalStoreURL = [NSURL fileURLWithPath:finalStorePath];
                    successfulSqliteMove = [[NSFileManager defaultManager] replaceItemAtURL:finalStoreURL withItemAtURL:migrateStoreURL backupItemName:@"sqliteBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&sqliteMoveError];//NSFileManagerItemReplacementUsingNewMetadataOnly
                
                else 
                    successfulSqliteMove = [[NSFileManager defaultManager] moveItemAtPath:migrateStorePath toPath:finalStorePath error:&sqliteMoveError];
                

                if (sqliteMoveError) 
                    DLog(@"The error for the SQLITE move: %@",sqliteMoveError.localizedDescription);
                

                if (successfulSqliteMove) 
                    actuallyMovedFiles = YES;
                    if([NSFileManager.defaultManager fileExistsAtPath:migrateStorePath]) 
                        statusMessage = replacingSqliteFile?NSLocalizedString(@"Successfully replaced SQLITE file. SQLITE file DOES still exist in original location.", @""):NSLocalizedString(@"Successfully moved SQLITE file. SQLITE file DOES still exist in original location.", @"");
                    
                    else 
                        statusMessage = replacingSqliteFile?NSLocalizedString(@"Successfully replaced SQLITE file. SQLITE file does NOT still exist in original location.", @""):NSLocalizedString(@"Successfully moved SQLITE file. SQLITE file does NOT still exist in original location.", @"");
                    
                
                else 
                    statusMessage = replacingSqliteFile?[NSString stringWithFormat:@"%@ (%@). ",NSLocalizedString(@"Failed to replace SQLITE file", @""),sqliteMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ (%@). ",NSLocalizedString(@"Failed to move SQLITE file", @""),sqliteMoveError.localizedDescription];
                
            
            else 
                statusMessage = NSLocalizedString(@"No SQLITE file to move.", @"");
            

            if (walFileExists) 
                NSString *toWalPath = [finalStorePath stringByAppendingString:@"-wal"];
                BOOL toWalFileExists = [NSFileManager.defaultManager fileExistsAtPath:toWalPath];
                NSError * walMoveError = nil;
                BOOL successfulWalMove = NO;
                BOOL replacingWalFile = NO;
                if (toWalFileExists) 
                    replacingWalFile = YES;
                    NSURL *fromWalURL = [NSURL fileURLWithPath:fromWalPath];
                    NSURL *toWalURL = [NSURL fileURLWithPath:toWalPath];
                    //successfulWalMove = [[NSFileManager defaultManager] replaceItemAtURL:fromWalURL withItemAtURL:toWalURL backupItemName:@"walBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&walMoveError];
                    //THE ABOVE CODE WAS WRONG, WHICH WAS WHAT WAS CAUSING THE ISSUE
                    successfulWalMove = [[NSFileManager defaultManager] replaceItemAtURL:toWalURL withItemAtURL:fromWalURL backupItemName:@"walBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&walMoveError];
                
                else 
                    successfulWalMove = [[NSFileManager defaultManager] moveItemAtPath:fromWalPath toPath:toWalPath error:&walMoveError];
                

                if (walMoveError) 
                    DLog(@"The error for the WAL move: %@",walMoveError.localizedDescription);
                

                if (successfulWalMove) 
                    actuallyMovedFiles = YES;

                    if([NSFileManager.defaultManager fileExistsAtPath:fromWalPath]) 
                        statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced WAL file. WAL file DOES still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved WAL file. WAL file DOES still exist in original location.", @"")];
                    
                    else 
                        statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced WAL file. WAL file does NOT still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved WAL file. WAL file does NOT still exist in original location.", @"")];
                    
                
                else 
                    statusMessage = replacingWalFile?[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to replace WAL file", @""),walMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to move WAL file", @""),walMoveError.localizedDescription];
                
            
            else 
                statusMessage = NSLocalizedString(@"No WAL file to move.", @"");
            

            if (shmFileExists) 
                NSString *toShmPath = [finalStorePath stringByAppendingString:@"-shm"];
                BOOL toShmFileExists = [NSFileManager.defaultManager fileExistsAtPath:toShmPath];
                NSError * shmMoveError = nil;
                BOOL successfulShmMove = NO;
                BOOL replacingShmFile = NO;
                if (toShmFileExists) 
                    replacingShmFile = YES;
                    NSURL *fromShmURL = [NSURL fileURLWithPath:fromShmPath];
                    NSURL *toShmURL = [NSURL fileURLWithPath:toShmPath];
                    //successfulShmMove = [[NSFileManager defaultManager] replaceItemAtURL:fromShmURL withItemAtURL:toShmURL backupItemName:@"shmBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&shmMoveError];
                    //THE ABOVE CODE WAS WRONG, WHICH WAS WHAT WAS CAUSING THE ISSUE
                    successfulShmMove = [[NSFileManager defaultManager] replaceItemAtURL:toShmURL withItemAtURL:fromShmURL backupItemName:@"shmBackup" options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:nil error:&shmMoveError];
                
                else 
                    successfulShmMove = [[NSFileManager defaultManager] moveItemAtPath:fromShmPath toPath:toShmPath error:&shmMoveError];
                

                if (shmMoveError) 
                    DLog(@"The error for the SHM move: %@",shmMoveError.localizedDescription);
                

                if (successfulShmMove) 
                    actuallyMovedFiles = YES;

                    if([NSFileManager.defaultManager fileExistsAtPath:fromWalPath]) 
                        statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced SHM file. SHM file DOES still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved SHM file. SHM file DOES still exist in original location.", @"")];
                    
                    else 
                        statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully replaced SHM file. SHM file does NOT still exist in original location.", @"")]:[NSString stringWithFormat:@"%@ %@",statusMessage,NSLocalizedString(@"Successfully moved SHM file. SHM file does NOT still exist in original location.", @"")];
                    
                
                else 
                    statusMessage = replacingShmFile?[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to replace SHM file", @""),shmMoveError.localizedDescription]:[NSString stringWithFormat:@"%@ %@ (%@). ",statusMessage,NSLocalizedString(@"Failed to move SHM file", @""),shmMoveError.localizedDescription];
                
            
            else 
                statusMessage = NSLocalizedString(@"No SHM file to move.", @"");
            

            if (handler) 
                handler(migrateStoreURL,statusMessage,actuallyMovedFiles,hudIsRunning);
            
        );
    
    else 
        if (handler) 
            actuallyMovedFiles = NO;
            hudIsRunning = NO;
            statusMessage = NSLocalizedString(@"No SQLITE files to move.", @"");
            handler(migrateStoreURL,statusMessage,actuallyMovedFiles,hudIsRunning);
        
    

编辑: 感谢@matt,问题解决了——对于 shm 和 wal 文件,'to' 和 'from' 混淆了。我不敢相信我错过了。所以现在,正如我通过在线研究所期望的那样——即使它没有写在文档中——当替换方法成功时,每个文件实际上都是移动的,而不是复制的。使用我修改和修复的代码,这是我现在得到的信息:

已成功替换 SQLITE 文件。 SQLITE 文件不存在于 原来的位置。成功替换 WAL 文件。 WAL 文件不 仍然存在于原来的位置。已成功替换 SHM 文件。 SHM 文件不再存在于原始位置。

【问题讨论】:

不清楚问题是什么(特别是因为您没有显示任何代码)。我在文档中没有看到任何暗示原始文件将被删除的内容,那么您为什么会有这种期望呢?如果您以后还想删除原始文件,为什么不直接删除呢? 这是关于 SO 的一个答案。也许是错的。 ***.com/a/13606841/477641 我不复制是因为我首先使用这种方法的原因是某些用户的数据库太大以至于无法使用迁移数据库的首选方法(设备上的内存不允许)。复制大文件可能会对内存产生不利影响。我只想移动文件。我不想复制它们。我担心留下的副本的原因是这对我来说似乎是不一致的行为,我试图理解它为什么会发生。在这一点上我需要非常小心,我想尝试了解应该会发生什么。 我可能是错的,但乍一看,在我看来,您使用“from”和“to”的概念与我使用它们的方式相反。所以我对目标是什么感到有点困惑。您能否用文字解释一下文件在哪里以及您希望它们在哪里 @matt,你刚刚救了我的命。我真是个白痴。我把 shm 和 wal 文件的 'to' 和 'from' 搞混了。哇。谢谢你。现在,一切都按预期工作,所有三个文件都在移动到新位置后被删除。你是救生员。您可以添加您的 cmets 作为答案吗? 【参考方案1】:

我可能是错的,但乍一看,在我看来,您使用“from”和“to”的概念与我使用它们的方式相反。您可能对将文件移到哪里以及要将它们移到的位置感到困惑。

【讨论】:

谢谢@matt。有时只需要一双额外的眼睛和一个愿意花时间提供帮助的人。我非常感谢您在这个论坛上给予我和其他人的所有帮助。 这就是我们刚刚所做的:en.wikipedia.org/wiki/Rubber_duck_debugging 当你试图用语言说出代码应该做什么时,一切都到位了!

以上是关于replaceItemAtURL: 方法在某些情况下无法删除原始移动文件的主要内容,如果未能解决你的问题,请参考以下文章

正确测试方法在某些情况下啥都不做

C++:有没有办法在不暴露其他私有成员的情况下限制对某些类的某些方法的访问?

PHP 通知抑制;只有某些情况/方法

在浮子上施加(某些情况下)周期性边界条件的有效方法?

在某些情况下可以使用 Thread.suspend() 吗?

在某些情况下禁用滚动视图右侧