TFileStream.Create 使用某些设备语言在 iOS 上失败

TFileStream.Create fails on iOS with certain device languages

当我将 iOS 设备设置为例如越南语,那么下面的代码会失败sometimes:

var
  lFilePath: String
...
lFilePath := TPath.GetTempPath + '/MyDBfile.db';
lFileStream := TFileStream.Create(lFilePath, fmOpenReadWrite or fmShareExclusive or fmCreate);

TFileStream.Create 调用引发断言:"EFCreateError: Cannot create file "/private/var/mobile/Containers/Data/Application/{containerID}/tmp/MyDBfile.db" 没有这样的文件或目录。 只有当设备设置为某些语言(包括越南语)时,断言才不会在西欧语言中出现。

我将创建代码追溯到 System.SysUtilsFileCreate 函数的这一行:

FileHandle := Integer(__open(M.AsAnsi(FileName, CP_UTF8).ToPointer,
  O_RDWR or O_CREAT or O_TRUNC or Exclusive[(Mode and [=13=]04) shr 2], Rights));

引发断言时文件句柄为 -1。

有什么问题吗?

PS:为了弄清楚发生了什么,我添加了一个 Fileexists调用:

lFilePath := TPath.GetTempPath + '/MyDBfile.db';
if Fileexists(lFilePath) then
  System.Sysutils.DeleteFile(lFilePath);
lFileStream := TFileStream.Create(lFilePath, fmOpenReadWrite or fmShareExclusive or fmCreate);

现在,在代码失败的情况下,我有以下奇怪的发现: 在XCode中,可以显示App的Container,显示Container中的文件tmp/MyDBfile.db,即文件确实存在(该文件只是引用代码创建的,所以创建了一个代码成功的次数)。但是,同时 Fileexists returns false.

该文件是一个 SQLite 文件,稍后由 sqlite3_open_v2 打开并在不久后由 sqlite3_close 关闭。 SQLite 是否可以将文件置于 Fileexists returns false 的状态? (重启应用后状态依旧)

问题是 TPath.GetTempPath 的当前实施。它使用通用 Posix 方法 ExpandFileName('~/tmp/')。不过Apple推荐的方法是使用NSTemporaryDirectory.

两种方法 return tmp 目录的路径,但是 NSTemporaryDirectory 如果文件夹不存在,也会创建该文件夹 - 这似乎是不同之处。奇怪的是 Delphi 实现仅在某些 iOS 设备语言(包括越南语)下失败。我没有进一步调查,但现在的解决方案是简单地将 TPath.GetTempPath 替换为 Delphi 代码:

NSStrToStr(TNSString.Wrap(NSTemporaryDirectory))