When should you use assertions and preconditions a

2019-09-21 16:15发布

问题:

I've already read Difference between “precondition” and “assert” in swift. But still can't draw a clear line between the (different ways of unwrapping ie guard & ! + error handling) vs assertions.

If I want my application to no longer work, can't I just force unwrap something and substitute as for a precondition?

  1. Is it because we want to stop/exit the app and basically don't want any control flow or state changing and therefore we use asserts/preconditions which also happens to come with easy logging of human readable messages (helps us to not constantly write prints)?
  2. Things that we use asserts for have are vital, guard statements are eventually a control flow system that even if your function returns early doesn't necessarily mean your app should crash.

And if it's anything beyond nils like you want a String and the user is giving you an Int then you can use error handling.

EDIT:

I'm not after opinions, I'm asking this only to understand what convenience assertions provide over the mentioned alternatives. The numbered list is the core of my question.

回答1:

  • Errors are a form of flow control, on a par with if and while. In particuar, they involve coherent message sending and early exit. The idea is to wind up the current scope immediately and return control to the caller, telling the caller "something went wrong".

  • Assertions are a way to crash immediately.

Thus they belong to completely different conceptual worlds. Errors are for things that can go wrong in real time, from which we need to recover coherently. Assertions are for things that should never go wrong, and about which we feel so strongly that we don't want the program even to be released into the world under these circumstances, and can be used in places where Errors can't be used.

Example from my own code:

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)
    }()
    // ...
}

If pieceSize is ever called with a zero grid dimension, something is very wrong with my entire program. It's not a matter of testing for a runtime error; the program itself is based on faulty algorithms. That is what I want to detect.