2017-01-21 1 views
-2

Примечание: Я отправил ленивый вопрос перед для преобразования кода Swift 3 (удалил)Как обрабатывать ошибки с Swift (FileManager и другие в целом)

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.... 
       } 
      } 
     } 

     }); 
    } 
} 

Есть мысли?

+1

«оптимальный способ» на самом деле не является вопросом переполнения стека ... – matt

+0

Кроме того, ваш код не имитирует поведение оригинала Objective-C. Это для вас важно? Разве это не настоящая цель, чтобы сделать то же самое, что и оригинал Objective-C? Ну, ты этого не делаешь. – matt

ответ

1

Вы забыли повторить операцию копирования после удаления существующей резервной копии. Кроме того, «catch let error» может быть записана как «catch», потому что ошибка будет автоматически назначена константе с именем «ошибка», если вы не укажете шаблон catch. Вот ваш код с этими изменениями:

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)") 
       } 
      } 
     } 
    } 
} 
+0

Спасибо! Показывать оба способа, как это действительно разъясняет вещи – JEL

+0

Добро пожаловать :) – Owen

-1

В Swift 2 и 3 существует 3 способа использования метода, который может создавать ошибки.

  1. Если ошибка произошла, скажите мне.

    do { 
        try something() 
        try somethingElse() 
        print("No error.") 
    } catch { 
        print("Error:", error) 
    } 
    
  2. Не волнует ошибка. Если ошибка произойдет, просто верните нуль.

    try? something() 
    
  3. Я не верю, что это приведет к ошибке. Если ошибка случится, пожалуйста, сообщите мне о своем приложении.

    try! something() 
    
+0

Правильно, но это похоже на код Яблок, они цепляют 'fileManager.removeItem' внутри' fileManager.copyItem'. Apple указывает причину в комментариях, потому что 'fileManager.copyItem' может файл, если файл уже существует. Итак, что я сделал, это проверить эту ошибку и вызвать там 'fileManager.removeItem'. Ваш код имеет смысл, но не уверен, что он выполнит первоначальное намерение Apple здесь – JEL

0

Ваш перевод из Исход объектива-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.

+0

Хорошие моменты, спасибо. Ответ выше действительно показал мне обработку вложенности/ошибок, как вы указали. Наконец, этот документ действительно очистил ситуацию, [Apple, ошибка обработки какао] https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216 -CH7-ID10. В общем, мое понимание обработки ошибок не было большим, за исключением основных задач – JEL

+0

Хорошо, рад, что вы счастливее об этом. – matt

+0

@matt, Objective-C вложенная, если структура может быть переведена в строчную вложенную структуру if (которая не форматируется в комментарии :(): let FM = FileManager if (try? FM.copyItem (at: appDataDir , to: backupDir)) == nil { if (попробуйте? FM.removeItem (at: backupDir))! = nil { if (try? FM.copyItem (at: appDataDir, to: backupDir)) == nil { } } } – Owen

Смежные вопросы