执行 Carbon FSExchangeObjectsCompat 调用的 Cocoa 方法是什么?
What is the Cocoa method for doing the Carbon FSExchangeObjectsCompat call?
旧的 MoreFilesX FSExchangeObjectsCompat 中有这个很棒的功能,"exchanges the data between two files"。它通常用作安全保存方法的一部分,其中写出临时文件,然后调用 FSExchangeObjectsCompat 以将新保存的临时文件与旧 "original" 文件交换。它保留了所有元数据、权限等。
我发现此功能在 High Sierra 和 APFS 卷上失败,而在 HFS+ 卷上从未失败。不足为奇——其中许多调用已被弃用。
但是做同样事情的 Cocoa NSFileManager 方法是什么?
您可以使用较低级别的函数执行类似的操作。这是我编写的用于 10.12 之前的 SDK 的代码。如果针对 10.12 SDK 或更高版本进行编译,则可以使它更简单一些,如果您的部署目标是 10.12 或更高版本,则更简单。
#ifndef RENAME_SWAP
#define RENAME_SWAP 0x00000002
#endif
/*!
@function ExchangeFiles
@abstract Given full paths to two files on the same volume,
swap their contents.
@discussion This is often part of a safe-save strategy.
@param inOldFile Full path to a file.
@param inNewFile Full path to a file.
@result 0 if all went well, -1 otherwise.
*/
int ExchangeFiles( const char* inOldFile, const char* inNewFile )
{
int result = -1;
static dispatch_once_t sOnce = 0;
static renameFuncType sRenameFunc = NULL;
// Try to get a function pointer to renamex_np, which is available in OS 10.12 and later.
dispatch_once( &sOnce,
^{
sRenameFunc = (renameFuncType) dlsym( RTLD_DEFAULT, "renamex_np" );
});
// renamex_np is only available on OS 10.12 and later, and does not work on HFS+ volumes
// but does work on APFS volumes. Being the latest and greatest, we try it first.
if (sRenameFunc != NULL)
{
result = (*sRenameFunc)( inOldFile, inNewFile, RENAME_SWAP );
}
if (result != 0)
{
// exchangedata is an older function that works on HFS+ but not APFS.
result = exchangedata( inOldFile, inNewFile, 0 );
}
if (result != 0)
{
// Neither function worked, we must go old school.
std::string nameTemplate( inOldFile );
nameTemplate += "-swapXXXX";
// Make a mutable copy of the template
std::vector<char> workPath( nameTemplate.size() + 1 );
memcpy( &workPath[0], nameTemplate.c_str(), nameTemplate.size() + 1 );
mktemp( &workPath[0] );
std::string tempPath( &workPath[0] );
// Make the old file have a temporary name
result = rename( inOldFile, tempPath.c_str() );
// Put the new file data under the old name.
if (result == 0)
{
result = rename( inNewFile, inOldFile );
}
// Put the old data under the new name.
if (result == 0)
{
result = rename( tempPath.c_str(), inNewFile );
}
}
return result;
}
你想要-[NSFileManager replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:]
.
旧的 MoreFilesX FSExchangeObjectsCompat 中有这个很棒的功能,"exchanges the data between two files"。它通常用作安全保存方法的一部分,其中写出临时文件,然后调用 FSExchangeObjectsCompat 以将新保存的临时文件与旧 "original" 文件交换。它保留了所有元数据、权限等。
我发现此功能在 High Sierra 和 APFS 卷上失败,而在 HFS+ 卷上从未失败。不足为奇——其中许多调用已被弃用。
但是做同样事情的 Cocoa NSFileManager 方法是什么?
您可以使用较低级别的函数执行类似的操作。这是我编写的用于 10.12 之前的 SDK 的代码。如果针对 10.12 SDK 或更高版本进行编译,则可以使它更简单一些,如果您的部署目标是 10.12 或更高版本,则更简单。
#ifndef RENAME_SWAP
#define RENAME_SWAP 0x00000002
#endif
/*!
@function ExchangeFiles
@abstract Given full paths to two files on the same volume,
swap their contents.
@discussion This is often part of a safe-save strategy.
@param inOldFile Full path to a file.
@param inNewFile Full path to a file.
@result 0 if all went well, -1 otherwise.
*/
int ExchangeFiles( const char* inOldFile, const char* inNewFile )
{
int result = -1;
static dispatch_once_t sOnce = 0;
static renameFuncType sRenameFunc = NULL;
// Try to get a function pointer to renamex_np, which is available in OS 10.12 and later.
dispatch_once( &sOnce,
^{
sRenameFunc = (renameFuncType) dlsym( RTLD_DEFAULT, "renamex_np" );
});
// renamex_np is only available on OS 10.12 and later, and does not work on HFS+ volumes
// but does work on APFS volumes. Being the latest and greatest, we try it first.
if (sRenameFunc != NULL)
{
result = (*sRenameFunc)( inOldFile, inNewFile, RENAME_SWAP );
}
if (result != 0)
{
// exchangedata is an older function that works on HFS+ but not APFS.
result = exchangedata( inOldFile, inNewFile, 0 );
}
if (result != 0)
{
// Neither function worked, we must go old school.
std::string nameTemplate( inOldFile );
nameTemplate += "-swapXXXX";
// Make a mutable copy of the template
std::vector<char> workPath( nameTemplate.size() + 1 );
memcpy( &workPath[0], nameTemplate.c_str(), nameTemplate.size() + 1 );
mktemp( &workPath[0] );
std::string tempPath( &workPath[0] );
// Make the old file have a temporary name
result = rename( inOldFile, tempPath.c_str() );
// Put the new file data under the old name.
if (result == 0)
{
result = rename( inNewFile, inOldFile );
}
// Put the old data under the new name.
if (result == 0)
{
result = rename( tempPath.c_str(), inNewFile );
}
}
return result;
}
你想要-[NSFileManager replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:]
.