Материал: iOS сессия ответы

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

52.Псевдоним Any.Приведение типа Any.Псевдоним AnyObject

Swift предлагает два специальных псевдонима, позволяющих работать с не определенными типами:

AnyObject соответствует произвольному экземпляру любого класса;

Any соответствует произвольному типу данных.

Данные псевдонимы позволяют корректно обрабатывать ситуации, когда конкретное наименование типа или класса неизвестно либо набор возможных типов может быть разнородным.

Псевдоним Any

Благодаря псевдониму Any можно создавать хранилища неопределенного типа данных. Объявим массив, который может содержать элементы произвольных типов.Пример:

var things = [Any]()

things.append(0)

things.append(0.0)

things.append(42)

things.append("hello")

things.append((3.0, 5.0))

things.append({ (name: String) -> String in "Hello, \(name)" })

Массив things содержит значения типов: Int, Double, String, (Double, Double) и (String)->String. То есть перед вами целый набор различных типов данных.

При запросе любого из элементов массива вы получите значение не того типа данных, который предполагался при установке конкретного значения, а типа Any.

ПРИМЕЧАНИЕ Псевдоним Any несовместим с протоколом Hashable, поэтому использование типа Any там, где необходимо сопоставление, невозможно. Это относится, например, к ключам словарей.

Приведение типов - это способ проверки типа экземпляра или обработки этого экземпляра как другого суперкласса или подкласса где-то еще в его собственной иерархии классов.

Приведение типа Any

Для анализа каждого элемента массива необходимо выполнить приведение типа. Так вы сможете получить каждый элемент, преобразованный в его действительный тип данных. Рассмотрим пример, в котором разберем объявленный ранее массив поэлементно и определим тип данных каждого элемента.

for thing in things {

switch thing {

case let someInt as Int:

print("an integer value of \(someInt)")

case let someDouble as Double where someDouble > 0:

print("a positive double value of \(someDouble)")

case let someString as String:

print("a string value of \"\(someString)\"")

case let (x, y) as (Double, Double):

print("an (x, y) point at \(x), \(y)")

case let stringConverter as (String) -> String:

print(stringConverter("Troll"))

default:

print("something else")

}

}

Консоль

an integer value of 0

something else

an integer value of 42

a string value of "hello"

an (x, y) point at 3.0, 5.0

Hello, Troll

Каждый из элементов массива преобразуется в определенный тип при

помощи оператора as. При этом в конструкции switch-case данный

оператор не требует указывать какой-либо постфикс (знак восклицания или вопроса).

Псевдоним AnyObject

Псевдоним AnyObject позволяет указать на то, что в данном месте должен или может находиться экземпляр любого класса. При этом вы будете довольно часто встречать массивы данного типа при разработке программ с использованием фреймворка Cocoa Foundation. Данный фреймворк написан на Objective-C, а этот язык не имеет массивов с явно указанными типами данных. Объявим массив экземпляров с помощью псевдонима AnyObject.

let someObjects: [AnyObject] = [Dog(), NoisyDog(), Dog()]

При использовании псевдонима AnyObject нет ограничений на использование классов только из одной иерархической структуры. В данном примере если вы извлекаете произвольный элемент массива, то получаете экземпляр класса AnyObject, не имеющий свойств и методов для взаимодействия с ним.

Приведение типа AnyObject

Порой вы точно знаете, что все элементы типа AnyObject на самом деле имеют некоторый определенный тип. В таком случае для анализа элементов типа AnyObject необходимо выполнить приведение типа.

for object in someObjects {

let animal = object as! Dog

print(animal.type) }

Консоль

dog

dog

dog

53.Инициализаторы

Инициализатор — это специальный метод, выполняющий подготовительные действия при создании экземпляра объектного типа данных. Инициализатор срабатывает при создании экземпляра, а при его удалении вызывается деинициализатор.

Инициализатор выполняет установку начальных значений хранимых свойств и различные настройки, которые нужны для использования экземпляра.

Назначенные инициализаторы

При реализации собственных типов данных во многих случаях не требуется создавать собственный инициализатор, так как классы и структуры имеют встроенные инициализаторы:

классы имеют пустой встроенный инициализатор init(){};

структуры имеют встроенный инициализатор, принимающий в качестве входных аргументов значения всех свойств.

Инициализаторы класса и структуры, производящие установку значений свойств, называются назначенными (designated). Вы можете разработать произвольное количество назначенных инициализаторов с отличающимся набором параметров в пределах одного объектного типа. При этом должен существовать хотя бы один назначенный инициализатор, производящий установку значений всех свойств (если они существуют), и один из назначенных инициализаторов должен обязательно вызываться при создании экземпляра. Назначенный инициализатор не может вызывать другой назначенный инициализатор, то есть использование конструкции self.init() запрещено.

Помимо назначенных, в Swift существуют вспомогательные (convenience) инициализаторы. Они являются вторичными и поддерживающими. Вы можете определить вспомогательный инициализатор для проведения настроек и обязательного вызова одного из назначенных инициализаторов. Вспомогательные инициализаторы не являются обязательными для их реализации в типе. Создавайте их, если это обеспечивает наиболее рациональный путь решения поставленной задачи.

СИНТАКСИС

convenience init(параметры) {

// тело инициализатора

}

Наследование инициализаторов

Наследование инициализаторов отличается от наследования обычных методов суперкласса. Есть два важнейших правила, которые необходимо помнить:

Если подкласс имеет собственный назначенный инициализатор, то инициализаторы родительского класса не наследуются.

Если подкласс переопределяет все назначенные инициализаторы суперкласса, то он наследует и все его вспомогательные инициализаторы.

Отношения между инициализаторами

В вопросах отношений между инициализаторами Swift соблюдает следующие правила:

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

Вспомогательный инициализатор должен вызвать назначенный инициализатор того же объектного типа.

Вспомогательный инициализатор в конечном счете должен вызвать назначенный инициализатор

Проваливающиеся инициализаторы

В некоторых ситуациях бывает необходимо определить объектный тип, создание экземпляра которого может закончиться неудачей, вызванной некорректным набором внешних параметров, отсутствием какого-либо внешнего ресурса или иным обстоятельством. Для этой цели служат проваливающиеся (failable) инициализаторы. Они способны возвращать nil при попытке создания экземпляра. И это их основное предназначение.

Обязательные инициализаторы

Обязательный (required) инициализатор — это инициализатор, который обязательно должен быть определен во всех подклассах данного класса.

При реализации инициализатора в подклассе ключевое слово override не используется

54.Деинициализаторы

Деинициализаторы являются отличительной особенностью классов. Деинициализатор автоматически вызывается во время уничтожения экземпляра класса. Вы не можете вызвать деинициализатор самостоятельно. Один класс может иметь максимум один деинициализатор. С помощью деинициализатора вы можете, например, освободить используемые экземпляром ресурсы, вывести на консоль журнал или выполнить любые другие действия.

СИНТАКСИС

deinit {

// тело деинициализатора }

Деинициализатор суперкласса наследуется подклассом и вызывается автоматически в конце работы деинициализаторов подклассов. Деинициализатор суперкласса вызывается всегда, даже если деинициализатор подкласса отсутствует. Кроме того, экземпляр класса не удаляется, пока не закончит работу деинициализатор, поэтому все значения свойств экземпляра остаются доступными в теле деинициализатора.

class SuperClass {

init?(isNil: Bool){

if isNil == true {

return nil }

else{

print("Экземпляр создан") }

}

deinit {

print("Деинициализатор суперкласса") }

}

class SubClass:SuperClass{

deinit {

print("Деинициализатор подкласса") }

}

var obj = SubClass(isNil: false)

obj = nil

Консоль

Экземпляр создан

Деинициализатор подкласса

Деинициализатор суперкласса

При создании экземпляра класса SubClass на консоль выводится соответствующее сообщение, так как данный функционал находится в наследуемом от суперкласса проваливающемся инициализаторе. В конце программы мы удаляем созданный экземпляр, передав ему в качестве значения nil. При этом вывод на консоль показывает, что первым выполняется деинициализатор подкласса, потом — суперкласса.

  1. Архитектура MVC. На примере приложения

MVC расшифровывается как Model-View-Controller (Модель-ОтображениеКонтроллер) и является основой построения программных продуктов в среде Xcode Лучше всего понять концепцию MVC можно на реальном примере — ресторане с фастфудом. В нём посетители (пользователи) подходят к кассиру (одновременно вид и контроллер), видят меню и заказывают какое-нибудь блюдо.Кассир проверяет, всё ли в порядке с заказом, и после оплаты передаёт нужные данные повару (модель). Повар готовит заказанное блюдо, хотя понятия не имеет о том, как выглядит посетитель, оплатил ли он заказ и так далее. Когда модель закончит свою работу, она отправит результат в вид — обратно кассиру, который, в свою очередь, отдаст готовое блюдо посетителю. Если же говорить о приложениях, то компоненты будут следующие:

‰‰ Модель — классы, которые обеспечивают хранение данных ваших приложений .

‰‰ Отображение (Представление) — позволяют создать различные графические элементы, которые видит пользователь при работе с приложением .

‰‰ Контроллер — обеспечивает совместную работу «отображения» и «модели» . Данный блок содержит логику работы приложения .

Пример

<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">

<StackPanel Orientation="Horizontal">

<TextBox Name="Num1TextBox" Margin="5" Width="50"/>

<TextBlock Text="+" Margin="5" FontWeight="Bold"/>

<TextBox Name="Num2TextBox" Margin="5" Width="50"/>

</StackPanel>

<TextBlock Text="0" Name="ResultTextBlock" TextAlignment="Center" Margin="5"/>

<Button Content="Посчитать" Name="CalculateButton" Click="CalculateButton_Click" Margin="5"/>

</StackPanel>

Это и есть View — его видит пользователь. Тут есть кнопка, при нажатии на которую вызывается Controller:

private void CalculateButton_Click(object sender, RoutedEventArgs e) //Метод, который вызывается при нажатии на кнопку "Посчитать"

{

//Валидация полученных данных

string text1 = Num1TextBox.Text.Trim();

string text2 = Num2TextBox.Text.Trim();

int num1 = 0;

int num2 = 0;

if (!string.IsNullOrEmpty(text1) && !string.IsNullOrEmpty(text2))

{

try

{

num1 = Convert.ToInt32(text1);

num2 = Convert.ToInt32(text2);

}

catch (Exception exc) { }

Calculate(num1, num2); //Передача данных модели

}

}

Контроллер получает пользовательский ввод и обрабатывает данные. Он также может проверять права пользователя. Если валидация проходит успешно, данные передаются в Model:

public void Calculate(int num1, int num2)

{

result = num1 + num2; //Проведение операций с полученными данными

UpdateView(); //Вызов обновления представления

}

Модель проводит с этими данными необходимые операции, а затем вызывает метод обновления вида:

public void UpdateView()

{

ResultTextBlock.Text = result.ToString(); //Изменение вида

}

Вот результат работы такой программы:

  1. Использование фреймов, основы Auto Layout

Auto Layout занимается динамическим вычислением позиции и размера всех view в view иерархии, на основе constraints — правил заданных для того или иного view. Самый большой и очевидный плюс для разработчика в использовании Auto Layout в том, что исчезает необходимость в подгонке размеров приложения под определенные устройства — Auto Layout делает это за вас, динамически изменяя интерфейс в зависимости от внешних или внутренних изменений. Примером внешних изменений может быть: Изменение размера окна в macOS, изменение ориентации экрана, различные размеры экранов. Пример внутренних изменений: Изменение контента в окне, изменения в зависимости от языка и т.д. Создать свой интерфейс можно 3-мя способами: программно, на основе маски, которая автоматически подстраивается под изменения или использовать Auto Layout. Отличие Auto Layout от других способов в том, что вам больше не нужно писать код, который изменяет интерфейс в зависимости от размера окна и других элементов, вместо этого Auto Layout самостоятельно вычисляет расположение элемента интерфейса в приложении и изменяет его относительно окружения.