Всем привет!
Рассмотрим 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)