注意:我之前发布了一个懒惰的问题,用于将代码转换为Swift 3(删除它)
Apple有一些用于管理文件的示例代码。这是一个老指南,全部是Objective-C。我将代码段转换为Swift 3. 我关心的是错误处理部分。我嵌套了多个do
/ catch
块...只是想知道如果这是做事的最佳方式?
对此here提出了类似的问题/答案。
文档为:Apple File System Programming Guide,在"管理文件和目录"部分。
这是我的代码(转换为Swift 3):
func backupMyApplicationData() {
// Get the application's main data directory
let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
guard directories.count > 0,
let appSupportDir = directories.first,
let bundleID = Bundle.main.bundleIdentifier else {
return
}
// Build a path to ~/Library/Application Support/<bundle_ID>/Data
// where <bundleID> is the actual bundle ID of the application.
let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")
// Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
let backupDir = appDataDir.appendingPathExtension("backup")
// Perform the copy asynchronously.
DispatchQueue.global(qos: .default).async { _ in
// It's good habit to alloc/init the file manager for move/copy operations,
// just in case you decide to add a delegate later.
let fileManager = FileManager()
do {
// Just try to copy the directory.
try fileManager.copyItem(at: appDataDir, to: backupDir)
} catch CocoaError.fileWriteFileExists {
// If an error occurs, it's probably because a previous backup directory
// already exists. Delete the old directory and try again.
do {
try fileManager.removeItem(at: backupDir)
} catch let error {
// If the operation failed again, abort for real.
print("Operation failed again, abort with error: \(error)")
}
} catch let error {
// If the operation failed again, abort for real.
print("Other error: \(error)")
}
}
}
这是他转换的文档中的Apple代码:
- (void)backupMyApplicationData {
// Get the application's main data directory
NSArray* theDirs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if ([theDirs count] > 0)
{
// Build a path to ~/Library/Application Support/<bundle_ID>/Data
// where <bundleID> is the actual bundle ID of the application.
NSURL* appSupportDir = (NSURL*)[theDirs objectAtIndex:0];
NSString* appBundleID = [[NSBundle mainBundle] bundleIdentifier];
NSURL* appDataDir = [[appSupportDir URLByAppendingPathComponent:appBundleID]
URLByAppendingPathComponent:@"Data"];
// Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
NSURL* backupDir = [appDataDir URLByAppendingPathExtension:@"backup"];
// Perform the copy asynchronously.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// It's good habit to alloc/init the file manager for move/copy operations,
// just in case you decide to add a delegate later.
NSFileManager* theFM = [[NSFileManager alloc] init];
NSError* anError;
// Just try to copy the directory.
if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
// If an error occurs, it's probably because a previous backup directory
// already exists. Delete the old directory and try again.
if ([theFM removeItemAtURL:backupDir error:&anError]) {
// If the operation failed again, abort for real.
if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
// Report the error....
}
}
}
});
}
}
有什么想法吗?
答案 0 :(得分:1)
删除现有备份后,您忘记重试复制操作。此外,&#34;抓住错误&#34;可以写成只是&#34; catch&#34;因为错误将自动分配给名为&#34;错误&#34;如果你没有指定捕获模式。以下是包含这些更改的代码:
func backupMyApplicationData() {
// Get the application's main data directory
let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
guard
directories.count > 0,
let appSupportDir = directories.first,
let bundleID = Bundle.main.bundleIdentifier
else {
return
}
// Build a path to ~/Library/Application Support/<bundle_ID>/Data
// where <bundleID> is the actual bundle ID of the application.
let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")
// Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
let backupDir = appDataDir.appendingPathExtension("backup")
// Perform the copy asynchronously.
DispatchQueue.global(qos: .default).async { _ in
// It's good habit to alloc/init the file manager for move/copy operations,
// just in case you decide to add a delegate later.
let fileManager = FileManager()
do {
// Just try to copy the directory.
try fileManager.copyItem(at: appDataDir, to: backupDir)
} catch CocoaError.fileWriteFileExists {
// Error occurred because a previous backup directory
// already exists. Delete the old directory and try again.
do {
try fileManager.removeItem(at: backupDir)
} catch {
// The delete operation failed, abort.
print("Deletion of existing backup failed. Abort with error: \(error)")
return
}
do {
try fileManager.copyItem(at: appDataDir, to: backupDir)
} catch {
// The copy operation failed again, abort.
print("Copy operation failed again. Abort with error: \(error)")
}
} catch {
// The copy operation failed for some other reason, abort.
print("Copy operation failed for other reason. Abort with error: \(error)")
}
}
}
如果您希望翻译更接近Objective-C原创,只有一个错误输出,请尝试以下操作:
func backupMyApplicationData() {
// Get the application's main data directory
let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
guard
directories.count > 0,
let appSupportDir = directories.first,
let bundleID = Bundle.main.bundleIdentifier
else {
return
}
// Build a path to ~/Library/Application Support/<bundle_ID>/Data
// where <bundleID> is the actual bundle ID of the application.
let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")
// Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
let backupDir = appDataDir.appendingPathExtension("backup")
// Perform the copy asynchronously.
DispatchQueue.global(qos: .default).async { _ in
// It's good habit to alloc/init the file manager for move/copy operations,
// just in case you decide to add a delegate later.
let fileManager = FileManager()
// Just try to copy the directory.
if (try? fileManager.copyItem(at: appDataDir, to: backupDir)) == nil {
// If an error occurs, it's probably because a previous backup directory
// already exists. Delete the old directory and try again.
if (try? fileManager.removeItem(at: backupDir)) != nil {
do {
try fileManager.copyItem(at: appDataDir, to: backupDir)
} catch {
// The copy retry failed.
print("Failed to backup with error: \(error)")
}
}
}
}
}
答案 1 :(得分:0)
您对Objective-C原文的翻译不正确,正如另一个答案中所指出的那样。但是,这似乎不是你的问题。看来你的问题是关于嵌套。
要回答它,只需看看你想要模仿的原件:
NSFileManager* theFM = [[NSFileManager alloc] init];
NSError* anError;
if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
if ([theFM removeItemAtURL:backupDir error:&anError]) {
if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
}
}
}
注意什么? 嵌套。因此,代码结构与原始结构之间的唯一区别是嵌套的。 Objective-C原文创建了if
子句的嵌套。您的Swift转换会创建一个do / catch块的嵌套 - 因为它必须这样做,因为例如Objective-C copyItemAtURL
返回BOOL,而Swift copyItem(at:)
则不返回 - 如果有问题,则抛出。
所以我认为我们可以得出结论,嵌套正是正确的做法。实际上,你的代码有什么问题(原因不是原文的准确翻译)是因为它没有足够的嵌套!
您可以尝试通过替换catch
块替换if
块中的至少一个来测试这是什么类型的错误,但是您仍然会嵌套,那么为什么要这么麻烦呢?你只会抛弃do / catch构造的所有优雅和清晰度。
答案 2 :(得分:-1)
在Swift 2和3中,有3种方法可以使用可能产生错误的方法。
如果发生错误,请告诉我。
do {
try something()
try somethingElse()
print("No error.")
} catch {
print("Error:", error)
}
我不在乎错误。如果发生错误,只需返回nil。
try? something()
我不相信这会有任何错误。如果发生错误,请崩溃我的应用程序。
try! something()