Лекция 2 CS193P Winter 2015 — Больше Xcode и Swift, MVC. (часть 1)

Title iOS 8

STANFORD UNIVERSITY: Разработка iOS 8 приложений с Swift CS193P

 Лекция 2: Больше Xcode и Swift

Профессор Пол Хэгарти (Paul Hegarty)

Лекцию на английском языке и слайды можно найти на  iTunes название “2. More Xcode and Swift, MVC”.

Русскоязычный неавторизованный конспект лекции приводится ниже.

Вы также можете читать PDF-файл конспекта и использовать его offline

Лекция 2 CS193P Winter 2015 iOS 8 .pdf

Начало: 1 — ая часть лекции (0 — 30 минут) — код для этой части на GitHub.

Продолжение: 2 — ая часть лекции (30 минута — 45 минута) здесь.

Продолжение: 3 — ая часть лекции (45 минута — конец) здесь.

Это лекция номер 2 и у нас две основные темы.

  • Продолжение демонстрационного примера Calculator, начатого на прошлой лекции
  • Паттерн объектно — ориентированного конструирования MVC, который мы будем использовать во всем, что будем делать на этом курсе при построении приложений

Screen Shot 2015-02-01 at 9.05.32 AM

       Демо

  • Продолжаем Calculator…

Array <T>
«Вычисляемые» свойства (переменные экземпляра класса, которые вычисляются, а не запоминаются)
switch
Функции как типы
Синтаксис замыканий (closure) для определения функций «на лету»
Методы с тем же самым именем, но с разным типом аргументов
Еще об Autolayout (Разметка)

Вернемся к Демо. На слайдах представлены важные вещи, о которых мы собираемся говорить. Они приведены здесь не потому, что об этом прямо сейчас надо читать, а как некоторые заметки для вас. Чтобы вы смогли потом вернуться к ним и посмотреть, а о чем же шла речь тогда.
Итак, Демо. Я хочу напомнить, где мы остановились в прошлый раз. Мы получили полностью работающую цифровую клавиатуру для калькулятора, мы немного говорили о свойствах (properties) и переменных экземпляра класса ( instant variables) и о том, как их инициализовать. Мне также задали вопрос в конце лекции — идеальное время для вопросов — об outlet , к которому мы притянули нашу метку (UILabel) дисплея калькулятора. Этот outlet имеет тип UILabel! и мы ничего ему не присвоили: ни при инициализации, ни просто написав

[js]
@IBOutlet weak var display: UILabel! = …
[/js]

Если мы посмотрим на переменную

[js]
var userIsInTheMiddleOfTypingANumber: Bool = false
[/js]

то вспомним, что пока мы не присвоили значение этой переменной, компилятор жаловался, что у нашего класса нет инициализатора. А в случае с меткой display нет никаких жалоб. И я вам говорил, что вы должны инициализировать все ваши переменные в любом классе. Почему так?
Потому что переменная display:UILabel! Optional. Переменным типа Optional автоматически присваиваются значения nil, поэтому вам нет необходимости писать = nil

Screen Shot 2015-02-01 at 10.29.08 AM

Эта операция делается автоматически, и переменная display оказывается инициализированной. Но мы знаем, что если переменная имеет Optional тип — это вопросительный знак ?, а не восклицательный ! . Потому что если мы посмотрим, например, на переменную currentTitle, то она имеет тип String?
Screen Shot 2015-01-30 at 1.52.53 PMВы видите здесь вопросительный знак ?, а не восклицательный !
В чем разница?
ОТВЕТ: Никакой разницы в действительном типе переменной — нет, она все равно — Optional. В нашем случае это будет UILabel? . Но наличие восклицательного   !  или вопросительного  знака влияет на использование переменной. Это просто некоторая помощь вам со стороны компилятора. Если я поставлю ?, а не восклицательный !  знак, вы увидите ошибки

Screen Shot 2015-02-01 at 10.39.11 AM

Ошибки говорят о том, что у UILabel? нет ни метода, ни переменной по имени text. Другими словами, вы не можете послать UILabel? сообщение text, потому что UILabel? — Optional, а Optional не отвечают на сообщение textUILabel отвечает, но не Optionals.
Почему мы получили эти сообщения, а раньше не получали, ведь тип переменной display не изменился? Как нам избавиться от этих ошибок?
Мы можем избавиться от этих ошибок очень просто — добавим восклицательный !  знак здесь и здесь.

Screen Shot 2015-02-01 at 10.59.05 AM
Когда вы ставите восклицательный !  знак, вы разворачиваете (unwrap) этот Optional. И «развернутый»  Optional становится UILabel  и способен принимать сообщение text. В результате мы избавились от ошибок.

Но что интересного относительно свойства display. Оно не устанавливается в nil при создании экземпляра класса. По мере того, как загружается пользовательский интерфейс нового ViewController, это свойство становится привязанным, и привязанным навсегда, к метке на UI, оно устанавливается именно здесь, во время привязки.

Screen Shot 2015-02-01 at 11.19.46 AM

Поэтому очень жаль, что нам приходится каждый раз разворачивать его. Это свойство такого специального типа, оно устанавливается очень рано, возможно, до создания экземпляра класса нашего Controller, и остается установленным все время, пока «живет» наш Controller.

Поэтому, вместо размещения вопросительного  знака при декларировании display, я поставлю  восклицательный !  знак.

Screen Shot 2015-02-01 at 11.35.07 AMЧто нам говорит такое декларирование? Это просто Optional, но оно всегда автоматически разворачивается, и вам нет необходимости ставить   восклицательный !  знак при каждом использовании этой переменной. Поэтому я убираю восклицательные !  знаки, поставленные ранее рядом с переменной  display . Видите? Никаких ошибок не генерируется. Потому что display уже «развернута», превращена в UILabel и готова принимать соответствующие сообщения. Но, если по каким-то причинам display установится в nil, ваша программа закончится аварийно. В нашем приложении такая ситуация невозможна, потому что цифровая кнопка, нажатие которой и вызывает к жизни наш код, не была бы на пользовательское интерфейсе, если бы наш Controller не загрузился, а переменная display привязана и установлена.

Но если каким-то образом в вашем Controller есть какое-то место, где вы получаете доступ к display перед тем, как UI загрузился и все outlets привязались, то приложение закончится аварийно, потому что переменная display не развернулась в UILabel, а вы ее используете как автоматически развернутую. Поэтому переменная display, объявленная как UILabel! называется implicitly unwrapped Optional (неявно развернутое Optional). Такого типа переменные в реальной жизни полезны только для такого типа свойств, которые устанавливаются очень рано и остаются установленными навсегда.  В действительности, это «синтаксический сахар».

Давайте перйдем к другим кнопкам на нашем пользовательское интерфейсе.

Нам нужна кнопка «enter«. Нашему калькулятору не нужна кнопка «=«. Возможно, вы привыкли к калькуляторам, в которых вы набираете «6 + 3 =»  и он вам показывает результат. Наш калькулятор немного напоминает научный калькулятор. У него есть кнопка  «enter» и так называемый стэк (stack)  —структура данных, представляющая собой список элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно снять верхнюю.

Вы помещаете в стэк числа, которые вы хотите складывать, умножать или что-то еще с ними делать, и когда вы нажимаете на кнопку с операцией, например, «+» — сложить, калькулятор вытягивает числа из стэка и производит нужную операцию.

То есть вы набираете «6» «enter» «3» «х» и операция  «х» вытаскивает числа 6 и 3 и перемножает их. Можно также набрать  «6»  «enter»  «5» «enter» «4» «enter» «х» «+«. Перемножатся те, которые находятся на вершине стэка и к ним добавится последнее. То есть в индексной форме это будет  6 + 5 х 4 .

Для такого калькулятора необходима кнопка  «enter«. Для этого я просто сделаю
copy и paster любой кнопки и помещу ее в нижний ряд. Заметьте, что я выровнял ее по голубым линиям, я хочу, чтобы она была идеально выровнена и далее я возьму символ «enter»  из специальных символов: для этого я пойду в меню Edit -> Special Characters... буду искать e n t e r

Screen Shot 2015-02-01 at 2.09.22 PMВыбираем символ и теперь у меня есть кнопка . Далее я использую тот же самый механизм, что и до этого, то есть сделаю CTRL-перетягивание в мой код, чтобы создать Action. Как только я меняю Outlet  на Action,  UIButton меняется на AnyObject, но в данном случае мне это неважно. Я задаю название  enter.

Screen Shot 2015-02-01 at 2.22.50 PM

Это ничем не отличается от связывания предыдущих кнопок за исключением того, что здесь нам не нужен Arguments, поэтому  устанавливаем его в None. Так как мы связываем только одну кнопку и совершенно не собираемся с ней разговаривать, нам нужен только сам факт, что она нажата.

Screen Shot 2015-02-01 at 2.32.36 PMЧто мы собираемся написать внутри этого метода?
Основное, что нам нужно сделать, — это взять число с дисплея и поместить его во внутренний стэк. Нам понадобится внутренний стэк через мгновение.  Другая вещь, которую я хочу сделать, это установить usersInTheMiddleOfTypingANumber в  false. Потому чтобы мы, возможно, набирали число, и как только нажали «enter», мы положили его в стэк. В следующий раз, когда мы начнем печатать цифры, мы начнем новое число. Давайте запустим приложение.

Screen Shot 2015-02-01 at 3.09.45 PM

Оно работает, но у него есть маленькая проблема: я не хочу показывать символ в конце моего дисплея. Но что он здесь делает? 

Дело в том, что кнопка  ⏎ привязана к  @IBAction func enter()

Screen Shot 2015-02-01 at 3.20.47 PMМы делали эту CTRL- привязку, и это нормально. Но проблема состоит в том, что  кнопка   привязана и к @IBAction func appendDigit( sender: UIButton)

Screen Shot 2015-02-01 at 3.28.34 PMЭто произошло потому, что мы получили кнопку  ⏎ копированием одной из кнопок нашей цифровой клавиатуры.
То есть кнопка  посылает сообщение обоим @IBAction func. Я подробно остановился на этом, потому что это очень распространенная ошибка и она происходит при copy и paster какого-то объекта  UI.

Как нам ее исправить? CTRL-кликаем на нашей кнопке  ⏎ и получаем всплывающее окно со всеми связями этого UI элемента с кодом. 
Screen Shot 2015-02-01 at 3.38.45 PMВы видите, что имеют место обе привязки:  appendDigit: и enter. Самое замечательное, что я могу нажать на этот маленький крестик и связь удалится. Я так и делаю, и остается только enter. Теперь, если я вернусь обратно, то увижу, что что моя кнопка  ⏎ уже не посылает сообщение  appendDigit:.

Screen Shot 2015-02-01 at 3.56.12 PM

Вы можете так поступать с любым UI элементом, если хотите узнать, к чему этот элемент подсоединен в коде. Если вы хотите что-то рассоединить, всегда CTRL-клик и удаляете соединение только там.

Есть еще одна тонкость при соединениях. Вы видите, что  @IBAction func enter()  предстален в черном окошке с соединениями без двоеточия после названия enter, потому что у него нет аргумента.

@IBAction func appendDigit( sender: UIButton) представлена с двоеточием appendDigit: в черном всплывающем окошке со всеми связями этого UI элемента с кодом ( получаем CTRL-кликая на нашей кнопке ) , потому что у функции есть аргумент. Если вы передумали, и решили, допустим добавить аргумент в @IBAction func enter(sender: UIButton), то новая функция должна иметь представление в черном окошке со связями вид c двоеточием enter: , но в черном окошке с связями по-прежнему осталось названия без двоеточия  enter . Так как сообщения  enter: и enter — это разные сообщения, то связь нарушилась. Для того, чтобы это исправить, надо перепривязать UI элемент с помощью CTRL-перетягивания.

Screen Shot 2015-02-01 at 5.23.51 PM

Поэтому, если вы вдруг решили что-то поменять в такого рода методах, то нужно заново выполнить CTRL-перетаскивание.

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

Screen Shot 2015-02-01 at 5.30.16 PMЭто массив, и, как и во многих языках, таких, например, как Java при спецификации массивов употребляется слово Array. Какого типа этот массив? Я поставлю Double. Хотя я и не ввожу Double числа, только целые, но я могу набрать  «3»  ⏎  «6» «/» и  получу 0.5, то есть  Double числа появляются в процессе вычисления.

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

Screen Shot 2015-02-01 at 5.56.46 PMМы еще не говорили об инициализаторах. Класс или структура данных могут иметь множество инициализаторов. Некоторые из них могут иметь аргументы. Но в большинстве случаев они могут позволить создавать себя вообще без аргументов. В нашем случае инициализация без аргументов означает пустой массив.
Но печать слова Array дважды кажется надоедливой.

Ответ состоит в том, что мы вообще не должны задавать тип, если мы задали начальное значение.  Swift является строго типизованным языком — вы знаете, что все наши свойства строго типизированы. Но в Swift также работает «вывод типа» (inference). Мы не должны указывать тип OperandStack, мы сможем вывести его из начального значения.

Screen Shot 2015-02-01 at 6.13.41 PM

И если мы option — кликнем на operandStack, то увидим, что это массив Double.
Фактически, указание типа в Swift рассматривается как плохой стиль, потому что если «вывод типа» может быть осуществлен, пусть он работает.

Что мы будем делать с нашим стэком? Добавим новый элемент с помощью метода append (добавить).  Это сообщение, которое можно послать массиву, и он добавит этот элемент в массив. Вызываем метод точно также, как и свойства — через точку. Точка, а потом имя метода.

Screen Shot 2015-02-01 at 6.25.43 PMВы видите, что новый элемент имеет тип T, то есть тип элементов массива. В нашем случае это Double. Но у нас возникает проблема — то, что я хочу положить в стэк, находится на экране дисплея в виде текста. Я мог бы здесь написать что-то типа display.text, но это не String, это Optional строка — String? Мы можем развернуть эту строку display.text! c помощью восклицательного знака. Но проблема не исчезает.

Screen Shot 2015-02-01 at 6.40.28 PM

Потому что это все еще строка и вы все еще видите ошибку: String не может быть преобразована в Double.

Мы должны где-то сделать это преобразование, прежде чем положим число в стэк. Я хочу воспользоваться этой задачей и показать вам другую конструкцию Swift, называемую «вычисляемые свойства» (сomputed properties). Я создам еще одно свойство displayValue, которое я хочу, чтобы было Double. Оно должно содержать то, что отображается на дисплее, преобразованное в Double. Мне нужно иметь возможность устанавливать (set) его в Double и получать (get) Double значение. При установке displayValue  в Double , мой дисплей должен отображать это значение, преобразованное в текст. Получать его я должен как преобразованный в Double текст с дисплея. Такого типа свойства действительно привязаны к некоторым данным, находящимся где-то еще. Я не хочу задавать displayValue начальное значение, я хочу, чтобы оно всегда было вычисленным. Мы сделаем это, но вместо того, чтобы писать "displayValue = ", я помещаю фигурные скобки после указания типа, пишу «get» и «set» и собираюсь вычислять это свойство.

Screen Shot 2015-02-01 at 8.03.22 PMДавайте вначале напишем код для set. Он немного проще. Внутри set есть «магическая переменная» с именем  newValue. Это новое значение. Если кто-то в коде напишет displayValue = 5, то 5  и будет этим newValue.

Screen Shot 2015-02-02 at 1.48.40 PM

Я хотел бы написать display.text = newValue, но, во-первых, display.text — это String? (Optional) и оно не может быть установлено в Double, потому что newValue —  Double. Но мы знаем как преобразовать что-то в String. Это то, что мы делали перед распечаткой наших цифр. Эта конструкция, "\(newValue)", знает как делать такие преобразования.  И вы установили нужный текст на дисплее. Еще мы добавляем строку кода, говорящего о том, что мы не находимся в середине набора числа.

[js]
userIsInTheMiddleOfTypingANumber = false
[/js]

Теперь перейдем к get. Во-первых, мы должны вернуть значение — пишем  return , а во-вторых, я сейчас буду использовать нечто дополнительное, по которому вы сами должны посмотреть документацию, так как здесь я не буду это объяснять. Я создаю экземпляр класса NSNumberFormatter() , использую его метод numberFromString, на вход которого подаю текст на моем дисплее display.text!,  конечно, развернутый, результат оказывается Optional, поэтому разворачиваю его и преобразую в Double. Number дает мне некоторое обобщенное (generic) число, которое я могу преобразовать в Double.

Итак, у нас есть свойство, displayValue , установка которого и получение приводят к установке и получению значения нашего дисплея. Возвращаемся к добавлению операнд в стэк. Мы заменим то, что мы помещаем в наш внутренний стэк,  operandStack на это значение.  Это работает, потому что operandStack — это массив Double, а мы добавляем Double, которое знает, как себя преобразовать в строку. Мы увидим это через мгновение на консоле, так как добавили оператор печати стэка println("\(operandStack)").

Запускаем приложение. Пробуем «5» «6»  ⏎ . Затем «7» «2»  ⏎ .

Screen Shot 2015-02-02 at 8.54.32 AM

Мы получили операнды, расположенные в стэке operandStack.

Теперь нам нужны кнопки для операций «+«, ««, «x«, «/» и т.д.  Эти операции будут взаимодействовать с нашим стэком. Если бы я нажал  на «+«, то калькулятор должен достать 55.0 из стэка, затем 4.0 из стэка, сложить и получить 59.0, показать его на дисплее и вернуть его в стэк.  Если бы я опять нажал  на «+«, то достается  59.0 из стэка, затем 72.0 из стука, складываем и получаем 131.0, показываем его на дисплее и возвращаем его в стэк.

Так что начнем делать кнопки для операций. Как обычно путем копирования цифровой кнопки. Но я уже знаю проблему, которая возникает при копировании кнопок. После копирования нужно вызвать всплывающую панель, отражающую связи кнопки с кодом, и удалить не нужные привязки.

Screen Shot 2015-02-02 at 9.19.22 AMПосле этого вновь скопированная кнопка ни к чему не привязана и она не может посылать сообщения. Мы должны изменить заголовок на математический символ «x«.

Screen Shot 2015-02-02 at 9.26.26 AMЗатем создадим Target / Action для этой кнопки CTRL-перетягиванием от кнопки в код, получим метод и назовем этот метод operate.
Screen Shot 2015-02-02 at 9.31.29 AMТак как я собираюсь сделать эту кнопку общей для множества операций, то мне нужен Sender типа UIButton, как и в случае с цифровыми кнопками. Не забывайте поменять AnyObject на UIButton.
Создаем остальные кнопки операций и все они могут посылать только сообщение operate:.

Screen Shot 2015-02-02 at 9.41.28 AMЧто мы собираемся делать внутри этого метода? Тоже самое, что и прежде. Создадим операцию, константу operation из текущего заголовка кнопки, конечно, он должен быть развернут. И дальше я покажу вам оператор Swift, который есть и в других языках, но в Swift он очень мощный, мощнее, чем в других языках.
Это оператор switch и я покажу сейчас простейшее его применение, но мы увидим его на следующей демонстрации в Понедельник в более мощном варианте.

Screen Shot 2015-02-02 at 9.52.22 AMОн похож на switch в С, здесь тоже используется case:. В данном случае мы будем переключаться по строке, которая является математическим символом операции, поэтому используйте те же математические символы, которые у вас на заголовках кнопок с операциями. Обратите особое внимание на минус, так как их два в палитре специальных символов.

Оператор switch в Swift  требует, чтобы вы указали абсолютно все варианты (cases).  Так  как мы переключаемся по строке, то таких вариантов почти бесконечное число. Но к счастью, мы можем добавит default: break. Это означает, что если вы не попали ни в какой из вариантов, то попадете в default, и, если там стоит слово break, то  выполнение  switch прервется.
Я закомментирую все опции, кроме «x» ( я хочу сосредоточиться на умножении)  и добавлю еще одну строку кода сразу после получения операции operation. Для комментирования выделенных строк я использую «Cmd+ /«.

Дополнительная строка кода нужна для автоматического ввода кнопки ⏎ . Допустим я ввел «6» ⏎ «3«  +. Это требует автоматического ввода ⏎ после  «3»

Screen Shot 2015-02-02 at 11.08.32 AMТо есть, если вы находились в процессе вода числа и нажали клавишу операции, то ввод числа считается законченным.

Внутри case «x»:  я использую переменную displayValue, которая установит мой дисплей в результат умножения двух операндов, взятых из стэка operandStack.  Это возможно потому, что мы написали соответствующим образом setter и getter  для этой переменной. Для извлечения операндов из стэка, я применю метод removeLast(), который возвращает последний элемент массива.

Screen Shot 2015-02-02 at 11.21.55 AM

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

Screen Shot 2015-02-02 at 11.40.52 AM

Нам понадобится  автоматический ввод кнопки ⏎  для размещения результата в стэке.

Все. Запускаем приложение.

Screen Shot 2015-02-02 at 11.51.21 AM

Вводим  «3» ⏎ «6«  х . Получаем «18.0»  на дисплее и 18.0 в стэке. Вводим  «5» х . Получаем «90.0»  на дисплее и 90.0 в стэке.

Работает прекрасно. Теперь перейдем к другим операциям.

Код для этой части можно найти на GitHub.
——————- 31- ая минута лекции—————

Продолжение: 2 — ая часть лекции (35 минута — 45 минута) здесь.

 

 

Лекция 2 CS193P Winter 2015 — Больше Xcode и Swift, MVC. (часть 1): 20 комментариев

  1. В то время как @IBAction func appendDigit( sender: UIButton) предстален в черном окошке с соединениями с двоеточием appendDigit:

    Мне кажется тут в скобках нужно дописать, как вызвать это окошко, потому как в начале это окно называется «всплывающее окошко» 🙂

    CTRL-кликаем на нашей кнопке ⏎ и получаем всплывающее окно со всеми связями этого UI элемента с кодом.

    • Спасибо огромное, поправила. Очень рада любым редакторским правкам, так как время на это реально нет. Но если есть возможность, то присылайте пожалуйста готовые фразы, которые следует вставить или заменить. Буду очень признательно.

  2. Добрый создатель сайта!
    Помоги мне пожалуйста чайнику и дураку.
    Я очень хочу научиться программировать так в голове иногда возникают действительно хорошие идеи для реализации их на просторах приложений.
    Я просмотрел 1 урок и не укоснительно следовал всему что учили, но во 2 начинается уже какая-то жесть которую я не понимаю.
    Сразу хочу уточнить я вообще никогда не занимался программированием.
    Подскажи, что делать в данной ситуации?

    • Может быть стоит начать изучать программирование с другого более устоявшегося языка: C или Python или Java. Есть замечательный курс Гарварда CS50 для абсолютно начинающих программирование с нуля. Учит языку С и лектор с большим чувством юмора: будете учить и смеяться. Но если вы хотите непременно Swift, то в интернете (включая Youtube) просто шквал курсов типа «Как создать на Swift первое приложение «Hello, world!». Начните с этого, потом вернетесь к этому курсу. Потому что здесь нужно уже знать такие понятия, как «наследование», «переопределение» (override) функций, «перегрузка» (overloading) функций и т.д. и т.п. Это все-таки «высший пилотаж».

  3. Здравствуйте!
    Исправьте ошибку перед третьей картинкой снизу.
    «Внутри case «x»: я использую переменную defaultValue» на
    «Внутри case «x»: я использую переменную displayValue»

  4. Не знаю в чем дело, выполнил первую часть, проверил по гитхабу — все совпадает(даже полностью копировал), но выдает ошибку
    fatal error: unexpectedly found nil while unwrapping an Optional value

    Собственно ошибка как я понял в геттере, появляется здесь:
    displayValue = operandStack.removeLast() * operandStack.removeLast()

    В итоге заменил:
    return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
    на аналог Objective-C:
    return (display.text! as NSString).doubleValue

    Как мы получаем nil я не понял, может какие нововведения в swift 1.2. Собираю проект под ios 8.1, но SDK 8.4

    • Хм, а если скомпилить в симуляторе под ios 8.4 — то все ок.
      Видимо, пора уже обновить 8.1. Но если бы кто-нибудь пояснил, в чем проблема — был бы благодарен.

    • Тороплюсь с комментариями. Под симулятор 8.1 тоже все нормально работает вариант автора, а на реальном устройстве ошибка. Чудеса.

      • Если вы запускаете на реальном устройстве, в котором Регион установлен так, что там не применяется «.» для десятичных чисел, приложение закончится аварийно. Но на симуляторе оно работает. Что с этим делать посмотрите в постах, посвященных выполнению Задания 1 и 2. Но все равно, скажите какая у вас ошибка на реальном устройстве.

        • Да, проблема была именно в этом. Для меня это немного удивительно: т.е ты можешь протестировать работу приложения у себя в US, выложить в appstore, а в России оно работать не будет?
          return (display.text! as NSString).doubleValue
          — с таким решением, как и вашим(заставить работать будто бы это US регион), проблемы нет.

          • Интересное решение состоит в том, чтобы на кнопке калькулятора ставить не точку, а тот знак, который принят в этом регионе. Оно приводится в посте Задание 2. Решение. Если хотите интернационализировать свою приложение полностью — смотрите Лекцию 17 Internationalization and Setting. Очень интересно. Никогда не думала, что такую рутинную проблему можно так интересно решать.

      • В 8.4 все работает. И на симуляторе, и на реальном приборе. Возможно на реальном приборе ошибка связана с регионом — смотри форум с самого начала.

  5. Добрый день!

    у меня вопрос. Для добавления новых элементов в стэк после нажатия enter лектор преобразует текст из дисплея в double через создание новой переменной с set и get. А почему он просто не преобразует текст с помощью функции Double? : operandStack.append(Double(display.text!)!)
    в чем подвох?

    • Лектор демонстрирует вам вычисляемое свойство
      var displayValue :Double {
      get{
      return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
      }
      set {
      display.text = "\(newValue)"
      userIsInTheMiddleOfTypingANumber = false

      }
      }

      Кроме того, в выражении
      operandStack.append( displayValue )
      работает getter, а значит используется NSNumberFormatter, который делает преобразование из строки в число с учетом local (то есть как принято представлять число в данном регионе).
      Каждый раз,когда displayValue будет стоять справа от знака присваивания, будет работать getter и все его преобразования будут работать автоматически.
      Если displayValue будет стоять слева от знака присваивания, то будет работать setter со своими преобразованиями.
      Это очень удобно. На этой лекции показан простейший пример и вы должны понять возможности вычисляемых свойств.
      В дальнейшем Пол Хэгерти будет часто использовать вычисляемые свойства, например,при работе с NSUserDefaul, и это выглядит очень красиво.

  6. Рад вопросу alschel Декабрь 21, 2015 в 3:46 пп, но прошу пояснить ответ

    в get
    действительно проще написать return Double(display.text!)! //это намного понятнее, чем:
    return NSNumberFormatter().numberFromString(display.text!)!.doubleValue

    Ваше пояснение «из строки в число с учетом local (то есть как принято представлять число в данном регионе)» — не совсем понятно, можно пример, где не сработает одно, но сработает другое?

    • Попробуйте набрать код
      let a = Double («3.75»)
      print (a)
      let a = Double («3,75»)
      print (a)

      Один из них даст nil, другой — Optional число. Именно поэтому при выполнении Задания 1 я не завожу на экране «.» (точку), а
      let decimalSeparator = formatter.decimalSeparator ?? «.»
      Об этом много было разговоров на форуме приблизительно год назад.

      • Double («3,75»)!
        это та же ошибка разворачивания nil, что и
        NSNumberFormatter().numberFromString(«3,75»)!.doubleValue
        Тогда как использование «.» в обоих случаях работает, так может, разницы нет? )

        В задании 1 добавил именно «.» и жил спокойно, каюсь.
        Что, где-нибудь на Бали код крошиться будет?

        И спасибо, ваш перевод этого курса — просто золото!

        • Почему Бали? По-моему, Казахстан, Германия — все близко. Вдруг ваше приложение будет популярно?

Обсуждение закрыто.