2

我无法让 NSFileManager 方法replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:在 iOS 6 中工作。调用此方法并在 iOS 5 上正常工作的应用程序在 iOS 6 上存在重大问题。在运行低于 6.0 的 iOS 版本的设备上不会出现该问题。如果应用程序通过 Xcode 在 iOS 模拟器中启动,则不会出现此问题。否则问题似乎是普遍的。

这是我要执行的测试代码:

NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"test.txt"];
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"test.txt"];

// Create initial file in documents directory
if (![fileManager fileExistsAtPath:destinationPath])
{
    BOOL fileCopied = [fileManager copyItemAtPath:sourcePath
                                           toPath:destinationPath
                                            error:&error];
    if (!fileCopied)
        [[self statusLabel] setText:[NSString stringWithFormat:@"Creation Error:\n\n%@",
                                     [error localizedDescription]]];
}

// Replace file in documents directory with copy of file from app bundle
if ([fileManager fileExistsAtPath:destinationPath])
{
    NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath];
    BOOL fileReplaced = [fileManager replaceItemAtURL:destinationURL
                                        withItemAtURL:[NSURL fileURLWithPath:sourcePath]
                                       backupItemName:nil
                                              options:0
                                     resultingItemURL:&destinationURL
                                                error:&error];
    if (!fileReplaced)
        [[self statusLabel] setText:[NSString stringWithFormat:@"Replacement Error:\n\n%@",
                                     [error localizedDescription]]];
    else
        [[self statusLabel] setText:@"Successfully replaced file."];
}

如果该文件尚不存在,它会在文档目录中创建该文件。然后它会尝试用应用程序包中的文件副本替换文档目录中的文件。然后它报告文件创建/替换的状态。正如我之前所说,如果它在 iOS 5 或更低版本上运行,或者如果它在 iOS 模拟器中运行,并且 Xcode 附加到进程,它会很好地替代它。但是,如果它在没有 Xcode 的 iOS 6 设备或 iOS 模拟器上运行,则替换失败并返回错误。本地化的错误描述是The operation couldn’t be completed. (Cocoa error 512.)

错误的用户信息字典是:

{
NSFileNewItemLocationKey = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt";
NSFileOriginalItemLocationKey = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Documents/test.txt";
NSURL = "file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Documents/test.txt";
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=513 \"The operation couldn\U2019t be completed. (Cocoa error 513.)\" UserInfo=0x1d58d350 {NSFilePath=/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt, NSURLUnsetValueKeysKey=<CFArray 0x1d58d180 [0x39b9d100]>{type = immutable, count = 2, values = (\n\t0 : <CFString 0x39b945b4 [0x39b9d100]>{contents = \"NSURLFileSecurityKey\"}\n\t1 : <CFString 0x39b943d4 [0x39b9d100]>{contents = \"NSURLCreationDateKey\"}\n)}, NSUnderlyingError=0x1d58d010 \"The operation couldn\U2019t be completed. Operation not permitted\", NSURL=file://localhost/var/mobile/Applications/487FBB9E-A2BD-4CF2-BB38-F36764623C2F/Test.app/test.txt}";
}

我在 App Store 上有一个依赖于这种方法的应用程序。实时应用程序在 iOS 5 上继续正常运行,但在 iOS 6 上,由于方法失败,它存在巨大问题。有谁知道为什么这种方法会失败?

4

1 回答 1

8

NSFileManager 方法replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:不是复制方法;这是一种移动方法。即,文件不是替换为替换文件的副本,而是替换文件本身。由于应用程序不应该能够修改自己的包,因此上述代码不应该在任何版本的 iOS 中工作。

为了保留原子性,解决方法是先将替换文件的副本保存到临时目录中,然后将文件替换为临时目录中的副本。

这是固定的测试代码:

NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"test.txt"];

// Create initial file in documents directory
if (![fileManager fileExistsAtPath:destinationPath])
{
    BOOL fileCopied = [fileManager copyItemAtPath:sourcePath
                                           toPath:destinationPath
                                            error:&error];
    if (!fileCopied)
        [[self statusLabel] setText:[NSString stringWithFormat:@"Creation Error:\n\n%@", [error localizedDescription]]];
}

// Replace file in documents directory with copy of file from app bundle
if ([fileManager fileExistsAtPath:destinationPath])
{
    // Create temporary file
    NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.txt"];

    BOOL tempCopied = [fileManager copyItemAtPath:sourcePath
                                           toPath:tempPath
                                            error:&error];
    if (!tempCopied)
        [[self statusLabel] setText:[NSString stringWithFormat:@"Temp Creation Error:\n\n%@", [error localizedDescription]]];

    // Replace file with temporary file
    NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath];

    BOOL fileReplaced = [fileManager replaceItemAtURL:destinationURL
                                        withItemAtURL:[NSURL fileURLWithPath:tempPath]
                                       backupItemName:nil
                                              options:0
                                     resultingItemURL:&destinationURL
                                                error:&error];
    if (!fileReplaced)
        [[self statusLabel] setText:[NSString stringWithFormat:@"Replacement Error:\n\n%@", [error localizedDescription]]];
    else
        [[self statusLabel] setText:@"Successfully replaced file."];
}
于 2012-11-28T14:01:05.920 回答