2016-01-22 10 views
15

Прежде всего, я новичок в iOS и Swift и прихожу на фоне программирования Android/Java. Поэтому для меня идея поймать исключение из попытки записать в файл является второй натурой, в случае нехватки места, проблем с правами доступа к файлам или что-то еще, что может случиться с файлом (и это случилось, по моему опыту) , Я также понимаю, что в Swift исключения отличаются от Android/Java, поэтому я не об этом спрашиваю.Как правильно обрабатывать исключения NSFileHandle в Swift 2.0?

Я пытаюсь добавить в файл с помощью NSFileHandle, например, так:

let fileHandle: NSFileHandle? = NSFileHandle(forUpdatingAtPath: filename) 
if fileHandle == nil { 
    //print message showing failure 
} else { 
    let fileString = "print me to file" 
    let data = fileString.dataUsingEncoding(NSUTF8StringEncoding) 
    fileHandle?.seekToEndOfFile() 
    fileHandle?.writeData(data!) 
} 

Однако, как seekToEndOfFile() и writeData() функции показывают, что они бросают своего рода исключение:

Этот метод вызывает исключение, если дескриптор файла закрыт или недействителен, если приемник представляет собой не связанную трубку или конечную точку сокета, если в файловой системе не остается свободного места или возникает какая-либо другая ошибка записи. - Документация для Apple для writeData()

Итак, что же это за правильный способ справиться с Swift 2.0? Я прочитал ссылки Error-Handling in Swift-Language, try-catch exceptions in Swift, NSFileHandle writeData: exception handling, Swift 2.0 exception handling и How to catch an exception in Swift, но ни один из них не имеет прямого ответа на мой вопрос. Я прочитал что-то об использовании объектного C в коде Swift, но так как я новичок в iOS, я не знаю, что этот метод, и не может найти его где-нибудь. Я также попробовал новые блоки Swift 2.0 do-catch, но они не признают, что для методов NSFileHandle генерируется ошибка любого типа, скорее всего, поскольку в документации по функциям нет ключевого слова throw.

Я знаю, что я мог бы просто разрешить приложение сбой, если у него закончится свободное пространство или что-то еще, но так как приложение позже будет выпущено в магазин приложений позже, я не хочу этого. Итак, как мне это сделать Swift 2.0?

EDIT: В настоящее время это проект с только кодом Swift, поэтому, хотя кажется, что есть способ сделать это в Objective-C, я понятия не имею, как смешать эти два.

ответ

16

вторым (восстанавливаемым) решением было бы создание очень простой функции ObjectiveC++, которая берет блок и возвращает исключение.

Создайте файл под названием: ExceptionCatcher.ч и добавить его можно импортировать в вашем мостиковом заголовке (Xcode предложит создать для вас, если вы не один уже)

// 
// ExceptionCatcher.h 
// 

#import <Foundation/Foundation.h> 

NS_INLINE NSException * _Nullable tryBlock(void(^_Nonnull tryBlock)(void)) { 
    @try { 
     tryBlock(); 
    } 
    @catch (NSException *exception) { 
     return exception; 
    } 
    return nil; 
} 

Используя этот помощник довольно просто, я адаптировал свой код сверху использовать Это.

func appendString(string: String, filename: String) -> Bool { 
    guard let fileHandle = NSFileHandle(forUpdatingAtPath: filename) else { return false } 
    guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return false } 

    // will cause seekToEndOfFile to throw an excpetion 
    fileHandle.closeFile() 

    let exception = tryBlock { 
     fileHandle.seekToEndOfFile() 
     fileHandle.writeData(data) 
    } 
    print("exception: \(exception)") 

    return exception == nil 
} 
+0

К сожалению, я не могу это скомпилировать. Xcode (версия 7.2) дает мне ошибку «Неизвестное имя типа» NS_ASSUME_NONNULL_BEGIN'' и, конечно же, то же самое для соответствующей декларации конца. Предположительно, это было исправлено в Xcode 6.4 из того, что я читаю, и поскольку я не использую какие-либо модули или что-то еще, я понятия не имею, почему у меня есть эта ошибка. Есть идеи? – mirage

+0

Очень странно, я не видел эту проблему. попробуйте создать новый проект Swift и используйте там код. если это работает, найдите разницу между двумя файлами проекта. – Casey

+0

обновленный код для остановки использования '' 'NS_ASSUME_NONNULL_BEGIN''' – Casey

1

seekToEndOfFile() и writeData() не помечаются как throws (они не бросают NSError объект, который может быть пойман в с do-try-catch блока), что означает, что в текущем состоянии Swift, то NSException s поднятые ими не может быть " поймали».

Если вы работаете на Swift проект, вы можете создать класс Objective-C, который реализует свои NSFileHandle методы, которые ловит NSException с (как в this question), но в противном случае вы не повезло.

+1

Ответ Re @ matt: я почтительно не согласен в конкретном случае 'writeData()'. «NSException» может быть поднято, если [«если есть ... ошибка записи»] (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSFileHandle_Class/#// apple_ref/ОКК/instm/NSFileHandle/WriteData :). Возможно, у пользователя есть взломанная система или нет свободного места на их устройстве. Как вы должны учитывать все возможные сценарии, которые могут вызвать исключение с помощью 'NSFileHandle'? – JAL

+0

Я отредактировал вопрос, чтобы показать, что, действительно, я работал над проектом Swift. Не могли бы вы привести пример или ссылку на то, как включить в проект класс Objective-C, чтобы я мог правильно обрабатывать ошибки? Вопрос, на который вы ссылаетесь, просто показывает код Objective-C без контекста. Я всегда мог найти это, но для тех, кто позже рассмотрит этот вопрос, это может быть полезно. – mirage

+0

Вам нужно будет использовать [заголовок моста] (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html). – JAL

1

Это может быть достигнуто без использования кода Objective C, вот полный пример.

class SomeClass: NSObject { 
    static func appendString(string: String, filename: String) -> Bool { 
     guard let fileHandle = NSFileHandle(forUpdatingAtPath: filename) else { return false } 
     guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return false } 

     // will cause seekToEndOfFile to throw an excpetion 
     fileHandle.closeFile() 

     SomeClass.startHandlingExceptions() 
     fileHandle.seekToEndOfFile() 
     fileHandle.writeData(data) 
     SomeClass.stopHandlingExceptions() 

     return true 
    } 

    static var existingHandler: (@convention(c) NSException -> Void)? 
    static func startHandlingExceptions() { 
     SomeClass.existingHandler = NSGetUncaughtExceptionHandler() 
     NSSetUncaughtExceptionHandler({ exception in 
      print("exception: \(exception))") 
      SomeClass.existingHandler?(exception) 
     }) 
    } 

    static func stopHandlingExceptions() { 
     NSSetUncaughtExceptionHandler(SomeClass.existingHandler) 
     SomeClass.existingHandler = nil 
    } 
} 

Позвоните, позвоните SomeClass.appendString("add me to file", filename:"/some/file/path.txt"), чтобы запустить его.

+0

С некоторыми изменениями это делает исключение, как вы сказали. Изменение, которое я должен был сделать, включает NSFileHandle - хотя то, что вы компилируете, всегда инициализирует 'else' часть защиты при инициализации' fileHandle'. Это должно быть что-то вроде 'guard let fileHandle: NSFileHandle? = NSFileHandle (forUpdatingAtPath: имя_файла) else {return false} ', как в моем коде выше. Тем не менее, я не могу получить данные об исключениях для печати, даже если я изменил 'print' на' NSLog'. Вы знаете, почему это так? – mirage

+0

Кроме того, кажется, что ваш код только улавливает ошибку, печатает ее (если я могу заставить ее перейти к NSLog, как я уже упоминал), а затем переходит. Что, если я хотел бы знать, поймал ли обработчик что-то в моем коде Swift? Есть ли быстрый способ сделать это или это определенно нуждается в коде Objective C? – mirage

+0

Я не уверен, почему вы должны были изменить инструкцию охраны, это должно быть хорошо, пока имя файла является допустимым файловым путем. К сожалению, этот код не сможет восстановить из-за исключения, он просто дает вам возможность увидеть его до сбоя приложения. – Casey