如何使用Cocoa创建临时文件?

45

多年前,当我使用C#时,我可以轻松地创建一个临时文件并使用这个函数获取其名称:

Path.GetTempFileName();

这个函数会在临时目录中创建一个唯一命名的文件,并返回该文件的完整路径。

在Cocoa API中,我能找到最接近的是:

NSTemporaryDirectory

我是否漏掉了一些显而易见的东西,还是说没有内置的方法可以做到这一点?


使用C# API时需要注意:它的命名空间仅限于65k个文件,一旦超出这个限制就会抛出异常。我们在生产环境中遇到过这种情况——并非所有程序都会认真清理临时文件。 - fzwo
11个回答

36

1
不完全是“使用Cocoa”...更像是“使用POSIX”。 - Ky -
文档中指出:“这些函数的实现调用了arc4random(3),它不是可重入的。您必须在此和其他使用arc4random(3) API的地方提供自己的锁定。”我不知道如何同步我的项目中所有arc4random消费者的使用以及它所使用的所有第三方库。 - itotsev

21

[注意:此适用于iPhone SDK,而非Mac OS SDK]

据我所知,在SDK中并没有这些函数(与标准的Mac OS X 10.5文件相比,unistd.h文件大幅减少)。建议使用类似以下方式的替代方法:

[NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: @"%.0f.%@", [NSDate timeIntervalSinceReferenceDate] * 1000.0, @"txt"]];

虽然不够美观,但功能齐全。


5
这段话与mktemp(3)存在相同的竞争条件:将文件名的创建与临时文件的创建分开会打开一个漏洞窗口。按照Graham Lee的建议,使用mkstemp(3)。 - Chris Hanson
2
在 iPhone 上,每个应用程序都有自己的文件系统子树。NSTemporaryDirectory() 返回捆绑包内的内容。您仍然需要与自己竞争,以及任何其他具有写入权限的临时目录,但我认为这只适用于特权进程。 - Ken
2
@Ken,提问者在哪里说他们在使用iPhone? - user23743
2
当从多个线程“同时”调用时,此方法会失败。我不知道这个方法的粒度有多细(文档没有详细说明),但对我来说,我的NSOperations被调用得太快了,文件名相同。我的解决方案是将NSOperations地址添加到文件名中,因为它们肯定不同: [NSString stringWithFormat:@“%d_%.0f.% @”,self,[NSDate timeInterv... - Christian Beer
或者直接使用 NSUUID,即 NSString *uuidString = [[NSUUID UUID] UUIDString]; - Jay
显示剩余2条评论

17

苹果提供了一种很好的方式来访问临时目录并为临时文件创建唯一的名称。

- (NSString *)pathForTemporaryFileWithPrefix:(NSString *)prefix
{
    NSString *  result;
    CFUUIDRef   uuid;
    CFStringRef uuidStr;

    uuid = CFUUIDCreate(NULL);
    assert(uuid != NULL);

    uuidStr = CFUUIDCreateString(NULL, uuid);
    assert(uuidStr != NULL);

    result = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@", prefix, uuidStr]];
    assert(result != nil);

    CFRelease(uuidStr);
    CFRelease(uuid);

    return result;
}

链接::::http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009245 查看文件::::AppDelegate.m


11

我通过给NSFileManager创建一个分类,使用NSTemporary()和全局唯一标识符的组合,来实现了一个纯Cocoa解决方案。

以下是头文件:

@interface NSFileManager (TemporaryDirectory)

-(NSString *) createTemporaryDirectory;

@end

实现文件:

@implementation NSFileManager (TemporaryDirectory)

-(NSString *) createTemporaryDirectory {
 // Create a unique directory in the system temporary directory
 NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
 NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
 if (![self createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil]) {
  return nil;
 }
 return path;
}

@end

这将创建一个临时目录,但可以轻松地修改以使用createFileAtPath:contents:attributes:而不是createDirectoryAtPath:来创建文件。


11

如果目标是 iOS 6.0 或 Mac OS X 10.8 或更高版本:

NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];

6

Swift 5 和 Swift 4.2


import Foundation

func pathForTemporaryFile(with prefix: String) -> URL {
    let uuid = UUID().uuidString
    let pathComponent = "\(prefix)-\(uuid)"
    var tempPath = URL(fileURLWithPath: NSTemporaryDirectory())
    tempPath.appendPathComponent(pathComponent)
    return tempPath
}

let url = pathForTemporaryFile(with: "blah")
print(url)
// file:///var/folders/42/fg3l5j123z6668cgt81dhks80000gn/T/johndoe.KillerApp/blah-E1DCE512-AC4B-4EAB-8838-547C0502E264

或者是Ssswift的一行代码:
let prefix = "blah"
let url2 = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(prefix)-\(UUID())")
print(url2)

2
这段代码已经失效了,但是一个等价的版本甚至更加简单:URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(prefix)-\(UUID())") - Ssswift
感谢 @Ssswift 更详细地更新了答案。 - Bart van Kuik

2
现代的方法是使用FileManagerurl(for:in:appropriateFor:create:)
通过这种方法,您可以指定一个SearchPathDirectory来确定您想要的临时目录类型。例如,.cachesDirectory将在用户的库中持久存在(如果可能),而.itemReplacementDirectory将在目标文件所在的卷上。

2
你可以使用 mktemp 来获取一个临时文件名。

5
在mktemp(3)中存在竞争条件,最好使用mkstemp(3)。 - user23743

2

不要使用像NSTemporaryDirectory这样的遗留API,而是从FileManager中获取一个合适的URL

let tmpURL = FileManager
    .default
    .temporaryDirectory
    .appendingPathComponent(UUID().uuidString)

您仍然需要创建该目录。


0
你可以使用 NSTask 来调用 uuidgen 命令来获取一个唯一的文件名,然后将其附加到从 NSTemporaryDirectory() 获取的字符串中。但这在 Cocoa Touch 上不起作用。虽然有点冗长。

如果你只想要一个UUID,使用NSTask来执行uuidgen有点过头了。 - dreamlax
请注意,10.8+版本有NSUUID类可在Cocoa环境下创建UUID:https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUUID_Class/Reference/Reference.html - Jay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接