TL / DR: Компилятором также можно избежать недопустимых проверок, которые, как вы говорите, можно избежать при чистом программировании. *1003* И нулевые проверки, которые вы говорите, необходимы, могут быть принудительно установлены компилятором. Необязательные - это конструкция типа, которая делает это возможным.
var length = text?.length
На самом деле это хороший пример того, как полезны дополнительные функции. Если текст не имеет значения, он также не может иметь длину. В Objective-C, если text
равен nil, то любое отправленное вами сообщение ничего не делает и возвращает 0. Этот факт иногда был полезен и позволял пропустить большую проверку nil, но также мог привести к незначительным ошибкам .
С другой стороны, многие другие языки указывают на тот факт, что вы отправили сообщение с нулевым указателем, предупредив аварийное завершение сразу же при выполнении этого кода. Это немного облегчает определение проблемы во время разработки, но ошибки времени выполнения не так велики, когда они случаются с пользователями.
Swift использует другой подход: если text
не указывает на то, что имеет длину, то не имеет длины . Необязательный не указатель, это тип типа, который либо имеет значение, либо не имеет значения. Можно предположить, что переменная length
является Int
, но на самом деле это Int?
, что является совершенно другим типом.
Если я вызову функцию длины, я ожидаю, что она будет содержать длину. Если этого не произойдет, код должен разобраться с этим прямо сейчас, а не продолжать.
Если text
равен nil, то нет объекта для отправки сообщения length
, поэтому length
даже не вызывается, и результат равен nil. Иногда это хорошо - имеет смысл, что если нет text
, то не может быть и length
. Вы можете не заботиться об этом - если вы готовились нарисовать символы в text
, то тот факт, что нет length
, не будет беспокоить вас, потому что рисовать нечего. Необязательный статус text
и length
заставляет вас иметь дело с тем фактом, что эти переменные не имеют значений в той точке, где вам нужны значения.
Давайте рассмотрим немного более конкретную версию:
var text : String? = "foo"
var length : Int? = text?.count
Здесь text
имеет значение, поэтому length
также получает значение, но length
все еще является необязательным, поэтому в какой-то момент в будущем вам придется проверить, существует ли значение, прежде чем использовать это.
var text : String? = nil
var length : Int? = text?.count
В приведенном выше примере text
равен нулю, поэтому length
также получает ноль. Опять же, вам придется иметь дело с тем фактом, что и text
, и length
могут не иметь значений, прежде чем пытаться использовать эти значения.
var text : String? = "foo"
var length : Int = text.count
Угадай, что здесь происходит? Компилятор говорит: О, нет, не надо! , поскольку text
является необязательным, а это означает, что любое значение, полученное из него, также должно быть необязательным. Но код указывает length
как необязательный Int
. Если компилятор укажет на эту ошибку во время компиляции, то он будет намного приятнее , чем когда пользователь укажет на нее намного позже.
var text : String? = "foo"
var length : Int = text!.count
Здесь !
говорит компилятору, что вы думаете, что знаете, что делаете. В конце концов, вы просто присвоили фактическое значение text
, так что довольно безопасно предположить, что text
не равно нулю. Вы можете написать такой код, потому что вы хотите учесть тот факт, что text
может позже стать нулем. Не раскручивайте принудительно дополнительные опции, если вы точно не знаете, потому что ...
var text : String? = nil
var length : Int = text!.count
... если text
равно ноль, то вы предали доверие компилятора и заслуживаете ошибку времени выполнения, которую вы (и ваши пользователи) получаете:
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Теперь, если text
является , а не необязательным, тогда жизнь довольно проста:
var text : String = "foo"
var length : Int = text.count
В этом случае вы знаете , что text
и length
оба безопасны в использовании без какой-либо проверки, поскольку они не могут быть равны нулю. Вам не нужно быть осторожным, чтобы быть «чистым» - вы буквально не можете присвоить ничего, что не является допустимым String
на text
, и каждый String
имеет count
, поэтому length
будет получить значение.
Зачем мне когда-либо использовать опционы вместо нулей?
В старые времена Objective-C мы использовали управление памятью вручную. Было небольшое количество простых правил , и если вы неукоснительно следовали правилам, то система подсчета удержания Objective-C работала очень хорошо. Но даже лучшие из нас время от времени оказывались в затруднительном положении, и иногда возникали сложные ситуации, в которых было трудно точно знать, что делать. Огромная часть вопросов Objective-C на StackOverflow и других форумах, связанных с правилами управления памятью. Затем Apple представила ARC (автоматический подсчет хранения), в котором компилятор взял на себя ответственность за сохранение и освобождение объектов, а управление памятью стало на намного проще. Готов поспорить, что здесь меньше 1% вопросов по Objective-C и Swift, касающихся SO, теперь относятся к управлению памятью.
Необязательные опции таковы: они переносят ответственность за отслеживание того, имеет ли переменная значение, имеет или не может иметь значение от программиста к компилятору.