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
.
Это имеет смысл. Вы намереваетесь, что это построение существующего поведения Какао вполне справедливо. Я думаю, что это может быть проблемой в моем понимании «NSCoding». Выполнение подобной вещи в Objective-C я обернул загрузку объекта верхнего уровня графа некоторой сложности в блок try/catch, но ошибки здесь должны быть в том, что я присваиваю значения nil нефактивным свойствам, когда что-то не может быть декодируется во время инициализации. Вместо этого весь инициализатор должен возвращать нуль, если любое из значений равно nil. Правильно ли это звучит? –
Исправить. Вам нужно вернуть nil (что также означает, к сожалению, сегодня), что вы должны найти * some * способ инициализации всех свойств. Вы должны * инициализировать все, прежде чем возвращать нуль, которые команда Swift признает, немного нарушена. –
Получил. Я еще не переварил отказоустойчивые инициализаторы, но я могу это узнать. У вас есть предложения по обработке действительных значений nil? Например: в архиве объект типа A имеет необязательное свойство типа B. Если экземпляр типа B не может быть инициализирован, это 'init? (Coder:)' возвращает nil. Объект A будет иметь свое свойство, инициализированное «nil» и находиться в допустимом состоянии. Я бы предпочел, чтобы объект A все еще терпел неудачу, потому что, если какие-либо свойства на графике терпят неудачу, это не имеет смысла.Имеет ли это смысл? Есть ли способ справиться с этим в этом случае? –