Когда вы должны использовать утверждения и предварительные условия, а когда вы можете использовать защитные операторы, принудительную распаковку и обработку ошибок?

Я уже прочитал Разницу между "предварительным условием" и "утверждением" в swift. Но до сих пор не могу провести четкую границу между (различными способами развертывания, т.е. guard и ! + обработкой ошибок) и утверждениями.

Если я хочу, чтобы мое приложение больше не работало, могу ли я просто принудительно развернуть что-то и заменить его в качестве предварительного условия?

  1. Это потому, что мы хотим остановить/выйти из приложения и в основном не хотим, чтобы какой-либо поток управления или изменение состояния, и поэтому мы используем утверждения/предварительные условия, которые также приходят с простое ведение журнала удобочитаемых сообщений (помогает нам не писать постоянно prints)?
  2. Вещи, для которых мы используем утверждения, имеют жизненно важное значение, операторы защиты в конечном итоге являются системой потока управления, которая, даже если ваша функция возвращается раньше, не обязательно означает, что ваше приложение должно рухнуть.

И если это нечто большее, чем nils, например, вам нужна String, а пользователь дает вам Int, тогда вы можете использовать обработку ошибок.

ИЗМЕНИТЬ:

Мне не нужны мнения, я спрашиваю об этом только для того, чтобы понять, какое удобство утверждения предоставляют по сравнению с упомянутыми альтернативами. Нумерованный список является ядром моего вопроса.


person Honey    schedule 24.02.2017    source источник
comment
К сведению: использование if let противоположно принудительному развертыванию (при котором используется !).   -  person rmaddy    schedule 24.02.2017
comment
@rmaddy исправлено. Спасибо   -  person Honey    schedule 24.02.2017
comment
Я думаю, что в конечном итоге это сводится к стилю кодирования, который в первую очередь основан на мнении.   -  person JAL    schedule 24.02.2017
comment
@JAL Я не спрашиваю о стиле кодирования. Я просто хочу понять разницу (утверждения против защиты + принудительная распаковка + обработка ошибок). И иметь возможность использовать из того, что предоставила Apple. Но, похоже, вы одобряете различия, которые я вижу, верно? Это все, что я хочу знать.   -  person Honey    schedule 24.02.2017
comment
Вы также понимаете, что assert и precondition можно использовать без опций, верно? Вы можете утверждать любое логическое условие, а не только значение nil. Если вы погуглите, когда использовать утверждения, вы увидите кучу обсуждений на эту тему. Это языковой агностик.   -  person JAL    schedule 24.02.2017
comment
@JAL Спасибо, я только что прочитал некоторые из них, но это не мой вопрос. Мой вопрос: для мест, где вы можете использовать утверждения, почему бы вместо этого не использовать (защиту + принудительную распаковку + обработку ошибок)?   -  person Honey    schedule 24.02.2017
comment
Я не вижу веских причин использовать assert, когда можно использовать guard и другие современные функции, предоставляемые Swift. Вполне возможно, что устаревший программист, который не знает всех функций, предоставляемых Swift, вернется к использованию утверждений, но я вообще не нахожу тот самый Swifty. Но опять же, это все стиль и на усмотрение программиста.   -  person JAL    schedule 24.02.2017
comment
@JAL Есть ли какой-нибудь сценарий, в котором assert может сделать что-то проще или просто может сделать то, что другие альтернативы не могут?   -  person Honey    schedule 24.02.2017
comment
Давайте продолжим обсуждение в чате.   -  person JAL    schedule 24.02.2017
comment
@Honey Ну да, asserts будут удалены в сборках -O (как говорится в связанных вопросах и ответах), в то время как другие упомянутые вами параметры не будут (они останутся в производственном коде, хотя ! не будет проверять nil в -Ounchecked builds) — что делает его пригодным для проверки работоспособности значений во время отладки.   -  person Hamish    schedule 24.02.2017
comment
ИМО: assert и precondition предназначены для обнаружения состояния, которое настолько необычное и неожиданное, что вы не можете с ним справиться, и, возможно, вы не хотите его обрабатывать, потому что оно указывает на что-то очень неправильное, например, повреждение стека или кучи. Кстати, принудительная распаковка сама по себе смутно эквивалентна func forceUnrwap<T>(thing: T?) -> T { guard let thing = thing else { preconditionFailure() }; return thing }, так что вот что. Но, как уже упоминалось, это стилистическая вещь, такая как вкладки против пробелов или фигурная скобка перевода строки.   -  person joeybladb    schedule 24.02.2017


Ответы (1)


  • Ошибки — это форма управления потоком, наравне с if и while. В частности, они включают согласованную отправку сообщений и ранний выход. Идея состоит в том, чтобы немедленно завершить текущую область действия и вернуть управление вызывающей стороне, сообщив вызывающей стороне, что "что-то пошло не так".

  • Утверждения — это способ немедленного падения.

Таким образом, они принадлежат к совершенно разным концептуальным мирам. Ошибки относятся к вещам, которые могут пойти не так в режиме реального времени, и от которых нам нужно последовательно восстанавливаться. Утверждения предназначены для вещей, которые никогда не должны идти не так, и к которым мы так сильно относимся, что даже не хотим, чтобы программа была выпущена в мир при таких обстоятельствах, и могут использоваться там, где нельзя использовать Ошибки.

Пример из моего собственного кода:

final class Board : NSObject, NSCoding, CALayerDelegate {
    // ...
    fileprivate var xct : Int { return self.grid.xct }
    fileprivate var yct : Int { return self.grid.yct }
    fileprivate var grid : Grid // can't live without a grid, but it is mutable
    // ...
    fileprivate lazy var pieceSize : CGSize = {
        assert((self.xct > 0 && self.yct > 0), "Meaningless to ask for piece size with no grid dimensions.")
        let pieceWidth : CGFloat = self.view.bounds.size.width / (CGFloat(self.xct) + OUTER + LEFTMARGIN + RIGHTMARGIN)
        let pieceHeight : CGFloat = self.view.bounds.size.height / (CGFloat(self.yct) + OUTER + TOPMARGIN + BOTTOMMARGIN)
        return CGSize(width: pieceWidth, height: pieceHeight)
    }()
    // ...
}

Если pieceSize когда-либо вызывается с нулевым размером сетки, что-то очень не так со всей моей программой. Это не вопрос тестирования ошибок во время выполнения; сама программа основана на ошибочных алгоритмах. Вот что я хочу обнаружить.

person matt    schedule 24.02.2017
comment
1. Эта строка assert((self.xct > 0 && self.yct > 0) неразрывная — с принудительной распаковкой, охранами, ошибками. Правильно? Единственное, что я могу сделать, это использовать fatalError(), который является утверждением формы. 2. Я могу вылететь из-за принудительной распаковки в течение nil с, но это не позволит мне регистрировать сообщения. Правильно? - person Honey; 24.02.2017
comment
Что развернуть? Чем может помочь охранник? Как бы вы сбросить ошибку здесь? Я прямо заявляю, что я предполагаю, что это правда, self.xct > 0 && self.yct > 0, и я терплю крах, если ошибаюсь. Что может быть лучше? - person matt; 24.02.2017
comment
Разве ваш вопрос не похож на вопрос «Зачем использовать for...in», когда мы всегда можем развернуть цикл while и итератор? Если вам не нужны удобные, подходящие функции языка высокого уровня, почему бы не программировать на языке ассемблера? - person matt; 24.02.2017
comment
Упс да слишком много вариантов в моей голове.... Мой вопрос был 1. разница в удобстве? Какой ваш ответ Да/Нет. Ваши 2 пули создают отличный функциональный контраст для их использования. Тем не менее, с опциями, возможно, это вопрос удобства. 2. Есть ли какие-либо области, где это уже не удобство, а необходимость, поскольку в альтернативах просто не может делать то, что задумано. - person Honey; 24.02.2017