2

NSFileManagerメソッドreplaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:をiOS6で機能させることができません。このメソッドを呼び出してiOS5で正常に機能するアプリには、iOS6で大きな問題があります。この問題は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."];
}

まだ存在しない場合は、documentsディレクトリにファイルを作成します。次に、documentsディレクトリ内のファイルをアプリバンドルのファイルのコピーに置き換えようとします。次に、ファイルの作成/置換のステータスを報告します。前に述べたように、iOS 5以下で実行されている場合、またはプロセスにXcodeが接続されたiOSシミュレーターで実行されている場合は、正常に置き換えられます。ただし、Xcodeを使用せずにiOS6デバイスまたはiOSSimulatorで実行した場合、置換は失敗し、エラーが返されます。ローカライズされたエラーの説明は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}";
}

この方法に依存するアプリがAppStoreにあります。ライブアプリはiOS5でも問題なく動作し続けますが、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 に答える