Swift | Вопросы собесов
Відкрити в Telegram
Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @easyoffer_adv
Показати більше2 167
Підписники
+124 години
-17 днів
-1830 днів
Архів дописів
🤔 Когда не имеет смысла вызывать hitTest у UIView?
hitTest не будет вызван или вернёт nil, если вью:
- Скрыта (isHidden = true)
- Прозрачна (alpha <= 0.01)
- Отключена от взаимодействия (isUserInteractionEnabled = false)
- Или точка касания не входит в её bounds (pointInside == false)
Также, если у superview не проходит проверка, все её сабвью будут проигнорированы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Чем опасна утечка памяти?
Утечка памяти (
memory leak) – это ситуация, когда память остаётся выделенной, но больше не используется программой и не освобождается.
class Person {
var pet: Pet?
deinit { print("Person удалён") }
}
class Pet {
var owner: Person?
deinit { print("Pet удалён") }
}
var person: Person? = Person()
var pet: Pet? = Pet()
person?.pet = pet
pet?.owner = person
person = nil
pet = nil
🚩Как избежать утечек памяти?
🟠Использовать `weak` или `unowned` ссылки
Слабые (weak) или безвладельческие (unowned) ссылки не увеличивают счётчик ссылок, что разрывает цикл.
class Person {
var pet: Pet?
deinit { print("Person удалён") }
}
class Pet {
weak var owner: Person? // Слабая ссылка
deinit { print("Pet удалён") }
}
var person: Person? = Person()
var pet: Pet? = Pet()
person?.pet = pet
pet?.owner = person
person = nil // "Person удалён"
pet = nil // "Pet удалён"
🟠Использовать `unowned`, если объект всегда будет существовать
unowned похож на weak, но он не может быть `nil`.
class Owner {
var car: Car?
deinit { print("Owner удалён") }
}
class Car {
unowned var owner: Owner // Безвладельческая ссылка
init(owner: Owner) { self.owner = owner }
deinit { print("Car удалён") }
}
var owner: Owner? = Owner()
owner?.car = Car(owner: owner!)
owner = nil // "Owner удалён", "Car удалён"
🟠Разрывать циклы в замыканиях (`[weak self]`)
Замыкания захватывают объекты, создавая циклические ссылки.
Проблема: self удерживается замыканием
class ViewController {
var closure: (() -> Void)?
func setup() {
closure = {
print(self) // Удерживает self, создавая цикл!
}
}
deinit { print("ViewController удалён") }
}
var vc: ViewController? = ViewController()
vc?.setup()
vc = nil // ❌ "ViewController" НЕ удалится
🚩Как найти утечки памяти?
🟠Использовать `Instrument – Leaks` в Xcode
- Открываем Xcode > Product > Profile.
- Выбираем Leaks.
- Запускаем приложение и проверяем, остаются ли объекты в памяти.
🟠Проверить `deinit`
Если метод deinit не вызывается – значит, объект утёк в память.
class Test {
deinit { print("Test удалён") }
}
var obj: Test? = Test()
obj = nil // Должно напечатать "Test удалён"
🟠Включить `Malloc Stack Logging`
- В Xcode открываем "Debug Memory Graph" (нажимая значок в Debugger).
- Смотрим, какие объекты остались в памяти.
Ставь 👍 и забирай 📚 Базу знаний🤔 Что такое Generics (обобщённое программирование)?
Generics позволяют писать обобщённый код, который может работать с разными типами, сохраняя типовую безопасность.
Например, можно создать функцию, которая сортирует любые массивы, не указывая конкретный тип. Это снижает дублирование кода и делает библиотеки гибкими.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Есть ли в свифте множественное наследование?
В Swift нет множественного наследования классов, но можно использовать множественное наследование через протоколы.
🚩Почему нет множественного наследования классов?
Swift запрещает множественное наследование классов, потому что оно может привести к конфликтам и алмазной проблеме (diamond problem).
Допустим, в языке с поддержкой множественного наследования у нас есть два родительских класса с одинаковым методом:
class A {
public:
void greet() { cout << "Hello from A"; }
};
class B {
public:
void greet() { cout << "Hello from B"; }
};
// C наследуется от A и B
class C : public A, public B {};
C obj;
obj.greet(); // Какой метод вызвать? A или B?
🚩Как обойти ограничение? Используем протоколы!
В Swift можно реализовать множественное наследование через протоколы, поскольку класс может соответствовать нескольким протоколам одновременно.
protocol Flyable {
func fly()
}
protocol Swimmable {
func swim()
}
class Animal {}
class Duck: Animal, Flyable, Swimmable {
func fly() {
print("Утка летит")
}
func swim() {
print("Утка плывёт")
}
}
let duck = Duck()
duck.fly() // Утка летит
duck.swim() // Утка плывёт
🚩Что делать, если нужен код по умолчанию?
Если хочется, чтобы протокол предоставлял реализацию по умолчанию (почти как родительский класс), можно использовать extension:
protocol Walker {
func walk()
}
extension Walker {
func walk() {
print("Иду вперёд")
}
}
class Person: Walker {}
let human = Person()
human.walk() // "Иду вперёд" (метод взят из extension)
Ставь 👍 и забирай 📚 Базу знаний🤔 Какие инструменты Xcode есть?
Xcode предлагает:
- Интегрированную среду разработки (IDE).
- Инструменты отладки: LLDB, Breakpoints, Debug View Hierarchy.
- Инструменты профилирования: Instruments.
- Интерфейсный редактор: Storyboard, Interface Builder.
- Симуляторы, автотесты, Git-интеграция, Code Review, Device logs.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие можно выделить главные принципы ООП?
Объектно-ориентированное программирование (ООП) — это парадигма, основанная на концепции "объектов", которые могут содержать данные в виде полей (часто называемых атрибутами или свойствами) и код в виде процедур (часто называемых методами). ООП фокусируется на использовании объектов для моделирования реального мира (или абстракций), облегчая разработку и поддержку сложных программ. Существует четыре основных принципа:
🟠Инкапсуляция
Механизм ООП, который объединяет данные (атрибуты) и код (методы), манипулирующий этими данными, внутри одного объекта и скрывает детали реализации от внешнего использования. Это позволяет защитить внутреннее состояние объекта от прямого доступа извне и обеспечить контролируемый интерфейс для работы с этим объектом.
🟠Наследование
Позволяет создавать новый класс на основе уже существующего класса, перенимая его свойства и методы. Новый класс может добавлять собственные свойства и методы или модифицировать унаследованные. Наследование обеспечивает повторное использование кода, упрощает его расширение и поддержку.
🟠Полиморфизм
Способность объектов с одинаковым интерфейсом предоставлять различную реализацию для одного и того же метода. Это означает, что функция или метод могут использоваться для различных типов объектов, и каждый тип может реализовать эту функцию или метод по-своему. Полиморфизм упрощает написание общего кода для работы с объектами разных классов и обеспечивает гибкость в его использовании.
🟠Абстракция
Позволяет скрыть сложность системы, представляя её ключевые аспекты и скрывая детали реализации. Это достигается за счёт использования абстрактных классов и интерфейсов, которые определяют шаблон для классов-наследников. Абстракция помогает сосредоточиться на взаимодействии объектов на более высоком уровне, игнорируя ненужные детали.
Ставь 👍 и забирай 📚 Базу знаний
🤔 Что означают IBOutlet и IBAction, для чего они нужны, и что значат для препроцессора?
- IBOutlet – указывает, что свойство связано с элементом интерфейса (Storyboard/XIB).
- IBAction – указывает, что метод вызывается при взаимодействии пользователя (например, нажатие кнопки).
Оба являются атрибутами (@property в Objective-C, аннотациями в Swift), но игнорируются препроцессором и служат только для связи с Interface Builder.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие плюсы и минусы layout'а?
Каждый способ создания макетов (layout'а) в iOS-разработке имеет свои преимущества и недостатки.
🚩Interface Builder (IB)
➕Визуальное редактирование
Удобный графический интерфейс для быстрой настройки и визуального просмотра изменений.
➕Интеграция с Auto Layout
Простая настройка ограничений (constraints) для адаптивного интерфейса.
➕Быстрое прототипирование
Возможность быстрого создания и изменения макетов без написания кода.
➖Меньшая гибкость
Ограниченные возможности для создания сложных и динамических макетов.
➖Конфликты при совместной работе
Трудности при слиянии изменений в storyboard или xib файлах в больших командах.
➖Производительность
Более медленное время загрузки по сравнению с программными подходами.
🚩Auto Layout
➕Адаптивность
Поддержка различных устройств и ориентаций экрана.
➕Мощные инструменты
Возможность создания сложных и адаптивных макетов с помощью ограничений.
➕Интеграция с Interface Builder
Упрощенная настройка ограничений через визуальный интерфейс.
➖Крутая кривая обучения
Может быть сложно освоить, особенно для новичков.
➖Управление сложными макетами
Требует тщательного планирования и может стать сложным при работе с большим количеством ограничений.
🚩Programmatic Layout
➕Полный контроль
Возможность точной настройки макета с помощью кода.
➕Гибкость
Легкость создания динамических и условных макетов.
➕Управление версиями
Легче управлять изменениями в коде по сравнению с визуальными файлами.
➖Большая трудоемкость
Требует больше времени и усилий для настройки, особенно для сложных макетов.
➖Меньшая наглядность
Отсутствие визуального редактора может затруднить представление конечного результата.
🚩Stack Views
➕Упрощение макетов
Легкость создания и управления сложными макетами с минимальными усилиями.
➕Интеграция с Auto Layout
Автоматическое управление ограничениями для вложенных элементов.
➕Адаптивность
Поддержка различных ориентаций и размеров экранов.
➖Ограниченная гибкость
Менее гибкие по сравнению с чистым Auto Layout или программными подходами.
➖Поддержка
Не всегда подходят для всех типов макетов, особенно для более сложных компоновок.
🚩SwiftUI
➕Декларативный синтаксис
Простота и понятность кода благодаря декларативному подходу.
➕Превью в реальном времени
Мгновенное обновление интерфейса при изменении кода.
➕Интеграция с Swift
Современные возможности языка и тесная интеграция с экосистемой Apple.
➕Кроссплатформенность
Поддержка различных платформ (iOS, macOS, watchOS, tvOS).
➖Требования к версии iOS
Поддержка только iOS 13 и выше, что может ограничить использование на старых устройствах.
➖Зрелость фреймворка
Некоторые функции еще находятся в стадии разработки, и могут быть ограничены возможности по сравнению с UIKit.
Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое Notification Service Extension?
Это расширение для iOS-приложений, которое позволяет модифицировать push-уведомления до их показа. Например, можно добавить изображение, вложение или изменить текст перед отображением пользователю.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Что может быть ключом и значением для dictionary?
Словарь (Dictionary) представляет собой коллекцию пар ключ-значение, где каждый ключ должен быть уникальным. Чтобы использовать какой-либо тип в качестве ключа словаря, этот тип должен соответствовать протоколу
Hashable. Это требование обусловлено тем, что Swift использует хеш-таблицу для хранения элементов словаря, что обеспечивает быстрый доступ к его элементам.
🚩Ключи
Должны быть уникальными: Каждый ключ в словаре должен быть уникальным. При попытке добавить в словарь элемент с ключом, который уже существует в словаре, старое значение будет заменено на новое.
Должны соответствовать протоколу Hashable: Это означает, что тип ключа должен иметь способность быть правильно хешированным. Большинство базовых типов Swift (например, String, Int, Double и др.) уже соответствуют Hashable, поэтому их можно использовать в качестве ключей без дополнительных усилий.
🚩Значения
Могут быть любого типа: Значения в словаре могут быть любого типа, и они не обязаны соответствовать протоколу Hashable.
Могут повторяться: Разные ключи могут иметь одинаковые значения.
var personAge: [String: Int] = ["John": 30, "Sara": 25]
🚩Собственные типы в качестве ключей
Вы также можете использовать собственные пользовательские типы в качестве ключей словаря, но для этого ваш тип должен соответствовать протоколу Hashable. Это включает в себя реализацию требуемых методов для сравнения на равенство (==) и хеширования (hash(into:)).
struct Person: Hashable {
var name: String
var id: Int
}
var peopleDictionary: [Person: String] = [Person(name: "John", id: 1): "Engineer"]
Ставь 👍 и забирай 📚 Базу знаний🤔 Что лучше: closures или delegate?
Зависит от ситуации. Делегаты лучше для долгоживущих однонаправленных связей (например, между контроллером и вью). Замыкания — для кратковременных, локальных задач (например, сетевые запросы, анимации). Делегаты лучше масштабируются, замыкания — лаконичнее.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Расскажи про две семантики в swift'е
В Swift все типы данных делятся на два вида по семантике хранения и передачи:
🚩Значимая семантика (Value Semantics)
Структуры (
struct), перечисления (enum) и кортежи (tuple) передаются по значению, то есть каждое присваивание создаёт копию объекта.
struct User {
var name: String
}
var user1 = User(name: "Alice")
var user2 = user1 // Копия!
user2.name = "Bob"
print(user1.name) // "Alice" (НЕ изменилось)
print(user2.name) // "Bob"
🚩Ссылочная семантика (Reference Semantics)
Классы (class), акторы (actor) и замыкания (closure) передаются по ссылке, то есть несколько переменных могут ссылаться на один и тот же объект.
class User {
var name: String
init(name: String) {
self.name = name
}
}
var user1 = User(name: "Alice")
var user2 = user1 // Передача ссылки!
user2.name = "Bob"
print(user1.name) // "Bob" (ИЗМЕНИЛОСЬ!)
print(user2.name) // "Bob"
Ставь 👍 и забирай 📚 Базу знаний🤔 Что будет, если убрать опционал?
1. Удаление опционалов лишит язык возможности безопасно работать с отсутствием значений.
2. Программистам придётся вручную проверять наличие значений, что повысит вероятность ошибок и крашей.
3. Опционалы делают код более безопасным и читаемым, позволяя явно выражать ситуацию, когда значение может отсутствовать.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие есть фрейм ворк механизмы в айос для выполненной бэкраунд задач?
В iOS для выполнения фоновых задач существуют несколько ключевых механизмов:
🚩Основные механизмы
🟠Grand Central Dispatch (GCD): Используется для асинхронного выполнения задач на глобальных или пользовательских очередях.
DispatchQueue.global(qos: .background).async {
// Фоновая задача
}
🟠OperationQueue: Высокоуровневый API для управления очередями операций с возможностью указания зависимостей.
let queue = OperationQueue()
queue.addOperation {
// Фоновая операция
}
🟠Background Fetch: Позволяет приложению периодически загружать новые данные в фоновом режиме.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Фоновое обновление данных
completionHandler(.newData)
}
🟠BGTaskScheduler: Новый фреймворк для планирования и выполнения фоновых задач.
import BackgroundTasks
func scheduleBackgroundTask() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
try? BGTaskScheduler.shared.submit(request)
}
🟠URLSession Background Transfers: Выполнение загрузки и выгрузки данных в фоновом режиме.
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let session = URLSession(configuration: configuration)
let url = URL(string: "https://example.com/largefile")!
let task = session.downloadTask(with: url)
task.resume()
Ставь 👍 и забирай 📚 Базу знаний🤔 Чем отличается set от dictionary?
Set хранит уникальные элементы без порядка, а Dictionary — пары ключ-значение, где ключи уникальны. Set быстрее для операций проверки принадлежности и поиска. Dictionary удобен для поиска по ключу и хранения дополнительных данных для каждого ключа.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Что будет, если открыть несколько экранов и сделать dismiss последнего?
Когда вы открываете несколько экранов (UIViewController) в iOS, они обычно создают стек представлений (View Controller Stack). В зависимости от способа открытия экранов (модально или через навигационный стек), поведение
dismiss будет разным.
🚩Модальное представление (`present`)
Если экраны открывались модально (present), то dismiss на последнем экране просто закроет только этот экран, и управление вернётся к предыдущему.
let newVC = UIViewController()
present(newVC, animated: true)
Позже вызываем:
dismiss(animated: true)
Если вызвать dismiss на первом модально представленном контроллере, все последующие модальные контроллеры закроются сразу.
// Открываем два экрана последовательно
let vc1 = UIViewController()
let vc2 = UIViewController()
present(vc1, animated: true)
vc1.present(vc2, animated: true)
Если вызвать dismiss на vc2:
vc2.dismiss(animated: true)
Если вызвать dismiss на vc1:
vc1.dismiss(animated: true)
🚩Навигационный стек (`push`)
Если экраны открывались через UINavigationController (pushViewController), то dismiss не работает для удаления последнего экрана. Нужно использовать popViewController.
let vc1 = UIViewController()
let vc2 = UIViewController()
navigationController?.pushViewController(vc1, animated: true)
navigationController?.pushViewController(vc2, animated: true)
Теперь если мы вызовем:
vc2.dismiss(animated: true)
Правильный способ закрытия последнего экрана в UINavigationController:
navigationController?.popViewController(animated: true)
Если вы хотите закрыть весь стек экранов, используйте:
navigationController?.popToRootViewController(animated: true)
Ставь 👍 и забирай 📚 Базу знаний🤔 Что происходит в системе, когда мы тапаем по экрану?
Когда пользователь касается экрана, происходит следующий процесс:
1. Аппаратный слой (дисплей) фиксирует касание и передаёт его в iOS.
2. Система генерирует объект события (UIEvent, UITouch).
3. Событие передаётся в главное окно (UIWindow) и далее — вью-контроллерам и иерархии UIView.
4. Система запускает hit-testing — определяет, какая вьюшка находится под точкой касания.
5. Вызываются методы:
- hitTest(_:with:) — определяет целевую вью.
- touchesBegan(_:with:), touchesMoved, touchesEnded — передают событие в конкретную вью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Что из себя представляет структура данных stack?
Stack (стек) – это структура данных, работающая по принципу LIFO (Last In, First Out – "последним пришел, первым ушел").
🚩Пример реализации стека в Swift
В Swift нет встроенного стека (кроме
Array), но можно создать свой:
struct Stack<T> {
private var elements: [T] = []
mutating func push(_ item: T) {
elements.append(item)
}
mutating func pop() -> T? {
return elements.popLast() // Удаляет и возвращает верхний элемент
}
func peek() -> T? {
return elements.last // Возвращает верхний элемент без удаления
}
func isEmpty() -> Bool {
return elements.isEmpty
}
}
// Пример использования:
var stack = Stack<Int>()
stack.push(10)
stack.push(20)
stack.push(30)
print(stack.pop()!) // 30
print(stack.peek()!) // 20
print(stack.isEmpty()) // false
🚩Где используется стек?
Обратный порядок выполнения (рекурсия) – стек вызовов функций.
Алгоритмы (обратная польская нотация, DFS – поиск в глубину)
История действий (назад-вперед в браузере, отмена в редакторе).
Ставь 👍 и забирай 📚 Базу знаний🤔 Как увеличить область нажатия на кнопку?
Чтобы увеличить зону клика UIButton:
1. Создать кастомную кнопку и переопределить point(inside:with:)
2. Либо обернуть кнопку в UIView с большим frame, но маленькой кнопкой внутри.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие изменения надо сделать в constrait перед вызовом layout?
Перед вызовом
layoutIfNeeded() или layoutSubviews() в iOS-приложении, нужно изменить значение констрейнта и вызвать анимацию, если необходимо. Вот основные шаги:
🟠Обновление значения констрейнта
Перед вызовом layoutIfNeeded(), измените свойство констрейнта (например, constant у NSLayoutConstraint).
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.changeButtonHeight()
}
}
func changeButtonHeight() {
heightConstraint.constant = 100 // Меняем значение констрейнта
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded() // Перестраиваем макет
}
}
}
🟠Если меняется набор констрейнтов
Если нужно удалить/добавить констрейнты, используйте `activate()` / `deactivate()`.
var expanded = false
@IBOutlet weak var smallHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var largeHeightConstraint: NSLayoutConstraint!
func toggleHeight() {
expanded.toggle()
if expanded {
NSLayoutConstraint.deactivate([smallHeightConstraint])
NSLayoutConstraint.activate([largeHeightConstraint])
} else {
NSLayoutConstraint.deactivate([largeHeightConstraint])
NSLayoutConstraint.activate([smallHeightConstraint])
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
🟠Если работа идёт в `viewDidLoad()`
Во viewDidLoad() элементы ещё не отрисованы, поэтому layoutIfNeeded() не сработает. Используйте viewDidAppear() или вызовите layoutIfNeeded() после view.layoutIfNeeded().
override func viewDidLoad() {
super.viewDidLoad()
heightConstraint.constant = 100
view.layoutIfNeeded() // НЕ обновит макет, потому что он ещё не загружен
}
Решение
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
heightConstraint.constant = 100
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
Разница между layoutIfNeeded() и setNeedsLayout()
heightConstraint.constant = 100
view.setNeedsLayout() // Обновление произойдет на следующем цикле рендера
Ставь 👍 и забирай 📚 Базу знаний