Всем привет!
Рассмотрим 5 вопросов, которые вам могут задать на собеседовании на позицию iOS разработчика. Скорее всего, это вопросы уровня Junior, но т.к. сейчас непонятно кто, что и у кого спрашивает, поэтому не будем разводить холивар :)
Вопрос №1: Можно ли создать необязательные методы в протоколах?
Ответ: Существует два способа создания необязательных методов для протоколов.
Использование ключевого слова optional.
@objc protocol NameProtocol { @objc optional func nameMethod() } class NameClass: NameProtocol { }
Плюсы:
- Не нужно указывать реализацию по умолчанию
Минусы:
- Использование ключевого слова optional доступно только для @objc protocol. Это означает, что протоколу могут соответствовать только классы, унаследованные от NSObject. Структуры и перечисления не могут соответствовать протоколу.
- Перед вызовом необходимо проверять реализован ли данный метод.
Реализация по умолчанию
protocol NameProtocol { var nameProperty: String { get set } } extension NameProtocol { func nameMethod() { print("default implementation") } } class NameClass: NameProtocol { var nameProperty: String = "name" }
Плюсы:
- Протоколу могут соответствовать классы, структуры, перечисления.
- Возможность использования Generics.
- Уверенность в том, что существует или собственная реализация метода или реализация по умолчанию.
Минусы:
- Не всегда существует возможность написать универсальную реализация по умолчанию.
- Невозможно отличить реализацию по умолчанию от ее отсутствия.
Вопрос №2: Чем static отличается от class?
Ответ: static и class достаточно похожи, но существуют различия.
Сходство:
- static и class делают свойство/метод, к которому они были применены, свойством/методом типа. Вызывать такое свойство/метод можно напрямую, без создания экземпляра.
class Car { static let wheels = 4 } Car.wheels
Различие:
- class может применяться только к вычисляемым свойствам
- class может применяться только к свойства и методам класса
- class позволяет переопределять свойство/метод
class Car { class var wheels: Int { 4 } } class Mercedes: Car { override class var wheels: Int { 3 } }
Вопрос №3: Могут ли ленивые вычисляемые свойства вычисляться больше одного раза?
Ответ: Нет
lazy var - переменная, которая инициализируется только при первом доступе.
Код инициализации выполняется только один раз. Результат сохраняется в переменной.
При последующем обращении будет возвращено сохраненное значение.
class NameClass { lazy var lazyProperty: Int = { print("lazyProperty") return 0 }() } let instance = NameClass() instance.lazyProperty instance.lazyProperty instance.lazyProperty //Консоль //lazyProperty
Сообщение "lazyProperty" выведется в консоль только один раз, при инициализации lazyProperty. При последующих обращениях возвращается сохраненное значение.
Более интересный пример:
class NameClass { var a: Int var b: Int lazy var lazyProperty: Int = { a + b }() init(a: Int, b: Int) { self.a = a self.b = b } } let instance = NameClass(a: 10, b: 2) instance.lazyProperty instance.a = 20 instance.b = 15 print(instance.lazyProperty) //Консоль //12
В консоль выведется значение 12, рассчитанное и сохраненное при первом обращении.
Вопрос №4: Почему нельзя вызвать memberwise initializer, если он содержит хотя бы одно свойство с уровнем private?
Ответ: При запуске кода, расположенного ниже, возникнет ошибка.
struct NameStruct { private let first: Int } let nameStruct = NameStruct(first: 1)
В описании ошибки говориться о том, что данный инициализатор имеет уровень доступа private, поэтому вызов невозможен.
Такое поведение связано с тем, что memberwise инициализатор устанавливает значения напрямую. Т.к. свойство имеет уровень доступа private, становится невозможным установить значение из вне. Инициализатору присваивается уровень доступа private.
Вопрос №5: Почему классы не обладают memberwise инициализатором как структуры?
Ответ: При ответе на данный вопрос, предлагаю сослаться на доводы Криса Латтнера.
При реализации собственного инициализатора memberwise инициализатор пропадает. Нет простого способа вернуть его.
Пример со структурой:
struct NameStruct { let first: String let second: String init(first: String) { self.first = first self.second = "two" } } let nameStruct = NameStruct(first: "1", second: "2") //error
Контроль доступа. Для свойств с уровнем доступа private memberwise инициализатор требует установить значение по умолчанию. Если же хотя бы один из членов инициализатора имеет уровень доступа private, инициализатор также будет иметь уровень доступа private и недоступен для использования. (Вопрос №4)
memberwise инициализатор должен уметь устанавливать значение по умолчанию для переменных.
memberwise инициализатор захватывает ленивые свойства (lazy var)
