2015-10-16 2 views
2

Как следует обрабатывать ошибки, связанные с NSCoding, в Swift?Обработка ошибок NSCoding в Swift

Если объект инициализирован с использованием init?(coder:), он может быть не инициализирован, если данные недействительны. Я хотел бы поймать эти ошибки и надлежащим образом обработать их. Почему init?(coder:) не определен как функция бросания в Swift?

ответ

5

NSCoding определяет его как Дополнительно:

init?(coder aDecoder: NSCoder) 

Так что, конечно, можно обнаружить ошибки.

90% «почему Swift ...?» на вопросы можно ответить «потому что какао». Какао не определяет initWithCoder: как возвращающую ошибку, поэтому он не переводится в throws в Swift. Невозможно было бы чисто перевести его в существующий код. (NSCoding идет назад NeXTSTEP. Мы создали много программного обеспечения, не возвращая NSError. Не означает, что иногда это может быть нехорошо, но традиционно было недостаточно «init init».)

Проверить nil. Это означает, что что-то не получилось. Это вся информация, которая предоставляется.


Мне никогда не приходилось проверять слишком глубоко, чтобы весь график объекта был правильным. Если это не так, вы все равно можете получить другие ошибки, и помните, что NSKeyedUnarchiver поднимет исключение ObjC (!!!), если он не сможет декодировать. Если вы не обернете это в ObjC @catch, вы все равно потерпите крах. (И да, это довольно сумасшествие, но все же так.)

Но если бы я хотел быть предельно осторожным и убедиться, что вещи, которые, как я ожидал, были в архиве, действительно были в архиве (даже если они были nil) Я мог бы сделать это таким образом (непроверенный, он компилирует, но я не сделал, что он действительно работает):

import Foundation 

enum DecodeError: ErrorType { 
    case MissingProperty(String) 
    case MalformedProperty(String) 
} 

extension NSCoder { 
    func encodeOptionalObject(obj: AnyObject?, forKey key: String) { 
     let data = obj.map{ NSKeyedArchiver.archivedDataWithRootObject($0) } ?? NSData() 
     self.encodeObject(data, forKey: key) 
    } 

    func decodeRequiredOptionalObjectForKey(key: String) throws -> AnyObject? { 
     guard let any = self.decodeObjectForKey(key) else { 
      throw DecodeError.MissingProperty(key) 
     } 

     guard let data = any as? NSData else { 
      throw DecodeError.MalformedProperty(key) 
     } 

     if data.length == 0 { 
      return nil // Found nil 
     } 

     // But remember, this will raise an ObjC exception if it's malformed data! 
     guard let prop = NSKeyedUnarchiver.unarchiveObjectWithData(data) else { 
      throw DecodeError.MalformedProperty(key) 
     } 

     return prop 
    } 
} 

class MyClass: NSObject, NSCoding { 
    static let propertyKey = "property" 
    let property: String? 
    init(property: String?) { 
     self.property = property 
    } 
    required init?(coder aDecoder: NSCoder) { 
     do { 
      property = try aDecoder.decodeRequiredOptionalObjectForKey(MyClass.propertyKey) as? String 
     } catch { 
      // do something with error if you want 
      property = nil 
      super.init() 
      return nil 
     } 
     super.init() 
    } 

    func encodeWithCoder(aCoder: NSCoder) { 
     aCoder.encodeOptionalObject(property, forKey: MyClass.propertyKey) 
    } 
} 

Как я уже говорил, я никогда на самом деле сделали это в программе какао. Если что-то было действительно повреждено в архиве, вы почти наверняка завершаете создание исключения ObjC, поэтому вся эта проверка ошибок, вероятно, будет излишней. Но это позволяет вам различать «ноль» и «не в архиве». Я просто кодирую свойство отдельно как NSData, а затем кодирую NSData. Если это nil, я кодирую пустой NSData.

+0

Это имеет смысл. Вы намереваетесь, что это построение существующего поведения Какао вполне справедливо. Я думаю, что это может быть проблемой в моем понимании «NSCoding». Выполнение подобной вещи в Objective-C я обернул загрузку объекта верхнего уровня графа некоторой сложности в блок try/catch, но ошибки здесь должны быть в том, что я присваиваю значения nil нефактивным свойствам, когда что-то не может быть декодируется во время инициализации. Вместо этого весь инициализатор должен возвращать нуль, если любое из значений равно nil. Правильно ли это звучит? –

+0

Исправить. Вам нужно вернуть nil (что также означает, к сожалению, сегодня), что вы должны найти * some * способ инициализации всех свойств. Вы должны * инициализировать все, прежде чем возвращать нуль, которые команда Swift признает, немного нарушена. –

+0

Получил. Я еще не переварил отказоустойчивые инициализаторы, но я могу это узнать. У вас есть предложения по обработке действительных значений nil? Например: в архиве объект типа A имеет необязательное свойство типа B. Если экземпляр типа B не может быть инициализирован, это 'init? (Coder:)' возвращает nil. Объект A будет иметь свое свойство, инициализированное «nil» и находиться в допустимом состоянии. Я бы предпочел, чтобы объект A все еще терпел неудачу, потому что, если какие-либо свойства на графике терпят неудачу, это не имеет смысла.Имеет ли это смысл? Есть ли способ справиться с этим в этом случае? –

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