AFAIK,没有办法得到文件的最后一次打开.相反,你必须得到最后一次读,书面或其目录条目已被修改。
Leo 在 cmets 中关于使用 URLResourceValues.contentAccessDate 的另一个答案的建议可能是最干净的方法,特别是因为您已经有一个 URL,这在最近很常见。
func lastAccessDate(forURL url: URL) -> Date?
{
return try? url.resourceValues(
forKeys: [.contentAccessDateKey]).contentAccessDate
}
您还可以使用以下路径深入到 BSD 层:
import Darwin // or Foundation
func lastAccessDate(forFileAtPath path: String) -> Date?
{
return path.withCString
{
var statStruct = Darwin.stat()
guard stat($0, &statStruct) == 0 else { return nil }
return Date(
timeIntervalSince1970: TimeInterval(statStruct.st_atimespec.tv_sec)
)
}
}
如果指定的 URL 是符号链接,我不是 100% 的 resourceValues 的行为,但 stat() 将返回有关链接指向的文件系统 inode 的信息。如果您想直接了解有关链接本身的信息,请改用lstat()。 stat() 和lstat() 在其他方面是一样的。
我很确定 URLResourceValues.contentAccessDate 在后台使用 stat() 或 lstat()。
要记住的一件事是最后访问时间是不是上次打开文件的时间,而是最后一次打开文件的时间读. stat 的 man 页面说:
struct stat与时间相关的字段如下:
st_atime 上次访问文件数据的时间。由 mknod(2)、utimes(2) 和 read(2) 系统调用更改。
st_mtime 最后一次修改文件数据的时间。由 mknod(2)、utimes(2) 和 write(2) 系统调用更改。
st_ctime 最后一次更改文件状态(inode 数据修改)的时间。由 chmod(2)、chown(2)、link(2)、mknod(2)、rename(2)、unlink(2)、
utimes(2) 和 write(2) 系统调用。
st_birthtime 文件创建时间。仅在创建文件时设置一次。该字段仅在 64 位 inode 变体中可用。在文件系统上
出生时间不可用,此字段设置为 0(即纪元)。
man 页面指的是 32 位成员字段名称,但同样适用于 64 位名称,st_atimespec、st_mtimespec、st_ctimespec 和 st_birthtimespec。
为了近似获取上次打开文件的时间,您需要获取最新的 st_atimespec、st_mtimespec 和 st_ctimespec,如果您还想包含对不包含的目录条目的更改修改内容,例如重命名文件或设置其权限。所以你需要这样的东西:
func lastReadOrWrite(forFileAtPath path: String) -> Date?
{
return path.withCString
{
var statStruct = Darwin.stat()
guard stat($0, &statStruct) == 0 else { return nil }
let lastRead = Date(
timeIntervalSince1970: TimeInterval(statStruct.st_atimespec.tv_sec)
)
let lastWrite = Date(
timeIntervalSince1970: TimeInterval(statStruct.st_mtimespec.tv_sec)
)
// If you want to include dir entry updates
let lastDirEntryChange = Date(
timeIntervalSince1970: TimeInterval(statStruct.st_ctimespec.tv_sec)
)
return max( lastRead, max(lastWrite, lastDirEntryChange) )
}
}
或使用URLResourceValues
func lastReadOrWriteDate(forURL url: URL) -> Date?
{
let valKeys: Set<URLResourceKey> =
[.contentAccessDateKey, .contentModificationDateKey, .attributeModificationDateKey]
guard let urlVals = try? url.resourceValues(forKeys:valKeys)
else { return nil }
let lastRead = urlVals.contentAccessDate ?? .distantPast
let lastWrite = urlVals.contentModificationDate ?? .distantPast
// If you want to include dir entry updates
let lastAttribMod = urlVals.attributeModificationDate ?? .distantPast
return max(lastRead, max(lastWrite, lastAttribMod))
}
当然,如果某个进程只是打开一个文件然后在没有读取或写入的情况下关闭它,那将不会被注意到,但是如果它没有读取或写入,它打开文件有关系吗?