Swift | Вопросы собесов
2.24K subscribers
30 photos
993 links
Download Telegram
🤔 Чем опасна утечка памяти?

Утечка памяти (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).
- Смотрим, какие объекты остались в памяти.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
🤔 Что такое Auto Layout?

Auto Layout — это система в iOS и macOS, которая автоматически рассчитывает размеры и положение элементов интерфейса на основе ограничений (constraints). Это позволяет адаптировать интерфейс под различные размеры экранов и ориентации устройств. Auto Layout поддерживает как верстку в коде, так и визуальное редактирование в Interface Builder. С помощью системы Auto Layout можно создавать интерфейсы, которые динамически изменяются в зависимости от контекста.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Расскажи про классы

Классы — это ссылочные типы в Swift, которые позволяют создавать объекты с общими свойствами и методами. Они поддерживают наследование, работают с ARC (Automatic Reference Counting) и хранятся в куче (heap).

🚩Основные характеристики классов

🟠Ссылочный тип
Когда вы присваиваете класс другой переменной, копируется не сам объект, а ссылка на него. Изменения, сделанные через одну ссылку, затронут и другие.

🟠Хранятся в куче
В отличие от структур, которые хранятся в стеке, классы создают объекты в куче. Это позволяет передавать объекты между функциями без копирования.

🟠Automatic Reference Counting (ARC)
Swift использует ARC для управления памятью. Объект удаляется из памяти, когда на него больше нет ссылок.

🟠Наследование
В отличие от структур, классы могут наследовать свойства и методы от других классов. Поддерживают переопределение методов (override).

🟠Идентичность объектов
Два объекта класса можно сравнивать по ссылке (===), а не только по значению (==).

class Animal {
var name: String

init(name: String) {
self.name = name
}

func speak() {
print("Some sound")
}
}

class Dog: Animal {
override func speak() {
print("Woof!")
}
}

let dog1 = Dog(name: "Buddy")
let dog2 = dog1 // Копируется ссылка, а не объект

dog2.name = "Charlie"

print(dog1.name) // "Charlie", так как dog1 и dog2 указывают на один объект


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
🤔 Какие есть нюансы в работе с closures?

Ключевые нюансы: захват значений (в т.ч. возможные retain cycles), необходимость указывать [weak self], использование замыканий в качестве обратных вызовов и обработчиков. Также важно понимать отличия между escaping и non-escaping closures — то есть тех, которые выходят за пределы области вызова.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как устроена память?

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

🟠Регистры процессора
Это самая быстрая память, непосредственно встроенная в процессор. Регистры хранят те данные, с которыми процессор работает в данный момент времени.

🟠Кэш-память
Она находится непосредственно на процессоре или рядом с ним. Кэш-память используется для временного хранения копий часто используемых данных из основной памяти для ускорения доступа к ним. Кэш-память делится на несколько уровней (L1, L2, и иногда L3), где L1 — самый быстрый и обычно самый маленький.

🟠Оперативная память (ОЗУ)
Здесь хранятся данные и программы, с которыми компьютер работает в данный момент. Доступ к ОЗУ быстрый, но оно является временным хранилищем: при выключении устройства данные в ОЗУ теряются.

🟠Постоянная память (ПЗУ, SSD, HDD)
Это память для долговременного хранения данных. Она сохраняет информацию даже при выключении питания. HDD (жесткие диски) использовались ранее и работают на принципе магнитного записывания данных, в то время как SSD (твердотельные накопители) работают на основе флеш-памяти и обеспечивают более быстрый доступ к данным.

🟠Виртуальная память
Это техника, которая позволяет операционной системе использовать часть жесткого диска (или SSD) как дополнительную оперативную память. Когда ОЗУ заполнено, операционная система может перемещать редко используемые данные из ОЗУ на диск в специальный файл подкачки (swap file), освобождая ОЗУ для других задач.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 Что дают параметры Auto Layout?

Auto Layout управляет позиционированием и размером элементов UI независимо от размера экрана. Параметры позволяют задать зависимости между элементами, адаптивность интерфейса, привязку к краям, отступы, пропорции.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Где фреймы наиболее яркий пример использования?

Фреймы (frames) в iOS-разработке используются для задания размеров и расположения элементов интерфейса (UIView) вручную.

🟠Кастомные анимации
Когда нужно анимировать движение элемента, проще всего работать с его frame, так как он напрямую управляет origin (координаты) и size (размеры).
UIView.animate(withDuration: 0.5) {
self.button.frame.origin.x += 100
}


🟠Ручная верстка (без Auto Layout)
Если вы не используете Auto Layout или хотите задать положение элементов программно, frame позволяет точно указать размеры и координаты.
let button = UIButton(type: .system)
button.frame = CGRect(x: 50, y: 100, width: 200, height: 50)
button.setTitle("Нажми меня", for: .normal)
view.addSubview(button)


🟠Оптимизация производительности
При динамической подгрузке ячеек в UITableView или UICollectionView можно вручную вычислять frame для ускорения работы, вместо использования Auto Layout, который может замедлить скроллинг.

🟠Работа с Core Graphics и CALayer
При рисовании или настройке слоев CALayer используется frame, чтобы точно определить размеры слоя.
let borderLayer = CALayer()
borderLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
borderLayer.borderWidth = 2
borderLayer.borderColor = UIColor.red.cgColor
view.layer.addSublayer(borderLayer)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
🤔 Как включить статическую диспетчеризацию?

Статическая диспетчеризация включается автоматически при работе с struct, enum, final class, или при вызове private методов. Она компилируется напрямую без использования vtable, что повышает производительность.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
🤔 Какие знаешь способы внедрения зависимостей?

Внедрение зависимостей (Dependency Injection, DI) в iOS-приложениях позволяет сделать код более модульным, тестируемым и поддерживаемым. Рассмотрим основные способы внедрения зависимостей в Swift.

🟠Внедрение через инициализатор (Initializer Injection)
Это самый распространенный и рекомендуемый способ. Зависимости передаются в объект через его инициализатор.
protocol NetworkServiceProtocol {
func fetchData()
}

class NetworkService: NetworkServiceProtocol {
func fetchData() {
print("Данные загружены")
}
}

// Класс, которому нужна зависимость
class ViewModel {
private let networkService: NetworkServiceProtocol

init(networkService: NetworkServiceProtocol) {
self.networkService = networkService
}

func loadData() {
networkService.fetchData()
}
}

// Использование
let networkService = NetworkService()
let viewModel = ViewModel(networkService: networkService)
viewModel.loadData()


🟠Внедрение через свойства (Property Injection)
Зависимость передается через свойство класса.
class ViewModel {
var networkService: NetworkServiceProtocol?

func loadData() {
networkService?.fetchData()
}
}

// Использование
let viewModel = ViewModel()
viewModel.networkService = NetworkService()
viewModel.loadData()


🟠Внедрение через метод (Method Injection)
Зависимость передается непосредственно в метод, который её использует.
class ViewModel {
func loadData(with networkService: NetworkServiceProtocol) {
networkService.fetchData()
}
}

// Использование
let viewModel = ViewModel()
let networkService = NetworkService()
viewModel.loadData(with: networkService)


🟠Использование Service Locator (Антипаттерн)
Класс сам запрашивает зависимость у глобального локатора.
class ServiceLocator {
static let shared = ServiceLocator()

private var services: [String: Any] = [:]

func register<T>(_ service: T) {
let key = String(describing: T.self)
services[key] = service
}

func resolve<T>() -> T? {
let key = String(describing: T.self)
return services[key] as? T
}
}

// Регистрация зависимостей
let locator = ServiceLocator.shared
locator.register(NetworkService() as NetworkServiceProtocol)

// Использование
class ViewModel {
func loadData() {
let networkService: NetworkServiceProtocol? = ServiceLocator.shared.resolve()
networkService?.fetchData()
}
}


🟠Использование DI-контейнеров (например, Swinject)
Специальные библиотеки помогают управлять зависимостями.
import Swinject

let container = Container()
container.register(NetworkServiceProtocol.self) { _ in NetworkService() }

class ViewModel {
private let networkService: NetworkServiceProtocol

init(networkService: NetworkServiceProtocol) {
self.networkService = networkService
}

func loadData() {
networkService.fetchData()
}
}

// Разрешение зависимости через контейнер
let networkService = container.resolve(NetworkServiceProtocol.self)!
let viewModel = ViewModel(networkService: networkService)
viewModel.loadData()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как обновлять коллекции?

Для UICollectionView и UITableView можно использовать:
- reloadData() (перезагружает всё);
- performBatchUpdates() (для анимации и вставок);
- DiffableDataSource (современный подход с snapshot);
- reloadItems(at:), insertItems(at:), deleteItems(at:).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое runLoop?

RunLoop — это фундаментальный механизм в iOS и macOS, который управляет циклом обработки событий в приложении. Он отслеживает и обрабатывает входящие события, такие как нажатия клавиш, касания экрана, таймеры и сетевые запросы, и поддерживает приложение в активном состоянии, пока оно не завершится.

🚩Основные аспекты `RunLoop`

🟠Цикл обработки событий
RunLoop постоянно выполняет цикл, ожидая входящие события и обрабатывая их по мере поступления. Этот цикл состоит из нескольких этапов: ожидание события, обработка события и повтор цикла.

🟠Режимы (Modes)
RunLoop может работать в разных режимах, которые определяют, какие источники событий будут отслеживаться и обрабатываться. Основные режимы включают default и tracking (для событий отслеживания, таких как прокрутка). В каждой итерации RunLoop обрабатывает события только для текущего режима.
RunLoop.current.run(mode: .default, before: Date.distantFuture)     


🟠Источники событий (Event Sources)
RunLoop может отслеживать различные источники событий, такие как таймеры (Timer), порты (Port), ввод пользователей (такие как касания экрана и клики мыши), а также пользовательские источники (Input Source).

🟠Таймеры
RunLoop может управлять таймерами, которые выполняют задачи через определенные интервалы времени.
     let timer = Timer(timeInterval: 1.0, repeats: true) { _ in
print("Timer fired!")
}
RunLoop.current.add(timer, forMode: .default)


🟠Обработка событий
RunLoop используется для обработки событий в основном потоке (main thread) приложения. Это особенно важно для поддержания отзывчивости пользовательского интерфейса, поскольку все взаимодействия с UI происходят в основном потоке.

🚩Пример использования `RunLoop`

import Foundation

class Example {
var timer: Timer?

func startRunLoop() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
RunLoop.current.run()
}

@objc func timerFired() {
print("Timer fired!")
}
}

let example = Example()
example.startRunLoop()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как меняется высота и ширина при скроллинге?

При прокрутке сам контейнер (UIScrollView) остаётся того же размера, но смещается contentOffset, чтобы показать другую часть контента. Размер контента задаётся через contentSize.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие есть инструменты для работы с потоками?

В Swift есть несколько инструментов для работы с многопоточностью и параллельным выполнением кода. Вот основные из них:

🟠GCD (Grand Central Dispatch) – главный инструмент для потоков
GCD – это низкоуровневая технология, позволяющая управлять задачами (тасками) в очередях (DispatchQueue).
DispatchQueue.global(qos: .background).async {
print("Фоновый поток")

DispatchQueue.main.async {
print("Вернулись в главный поток")
}
}


🟠OperationQueue – более удобный API для задач
OperationQueue – это более гибкая и объектно-ориентированная альтернатива GCD.
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // Ограничение на 2 задачи одновременно

queue.addOperation {
print("Операция 1")
}

queue.addOperation {
print("Операция 2")
}


🟠Actors – безопасная работа с потоками в Swift 5.5+
С actor можно работать с потоками без гонок данных, потому что все его свойства защищены от одновременного доступа.
actor Counter {
private var value = 0

func increment() {
value += 1
}

func getValue() -> Int {
return value
}
}

let counter = Counter()

Task {
await counter.increment()
print(await counter.getValue()) // Потокобезопасный доступ
}


🟠Task & Async/Await (Swift 5.5+) – современный подход к асинхронности
С async/await код становится читаемым и удобным.
func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 секунда задержки
return "Данные загружены"
}

Task {
let result = await fetchData()
print(result)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как работает garbage collector?

Он периодически просматривает все объекты в памяти и определяет, какие из них больше не доступны из текущих переменных. Такие объекты помечаются как мусор и удаляются. В Swift такой механизм не используется — вместо него применяется ARC.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое многопоточность?

Это возможность центрального процессора (ЦПУ) или программы выполнять несколько задач (потоков) одновременно. В контексте программирования она используется для улучшения производительности приложений за счёт параллельной обработки задач. Это особенно важно для задач, требующих интенсивных вычислений, или приложений, которые должны одновременно реагировать на множество входных данных, таких как пользовательский интерфейс.

🚩Основные понятия

🟠Поток (Thread)
Минимальная единица обработки, которая может быть выполнена операционной системой.

🟠Конкуренция (Concurrency)
Способность программы делать прогресс в нескольких задачах одновременно. Конкуренция достигается за счёт переключения между задачами.

🟠Параллелизм (Parallelism)
Способность программы выполнять несколько операций одновременно, используя множество процессоров или ядер.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое garbage collector?

Garbage collector — это автоматический механизм управления памятью, который периодически ищет и удаляет объекты, на которые больше нет активных ссылок. Он используется, например, в Java, но не в Swift.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как отлавливать, где допущена ошибка в свифте?

В Swift есть несколько способов отлавливать и диагностировать ошибки в коде:

🚩Обработка ошибок через `do-catch`

Используется, если функция генерирует ошибку (throws).
enum LoginError: Error {
case wrongPassword
case userNotFound
}

func login(user: String, password: String) throws {
if user != "admin" { throw LoginError.userNotFound }
if password != "1234" { throw LoginError.wrongPassword }
}

do {
try login(user: "admin", password: "wrong")
} catch LoginError.wrongPassword {
print("Ошибка: Неверный пароль")
} catch {
print("Ошибка: \(error)")
}


🚩`assert()`, `precondition()`, `fatalError()` (для отладки)

Эти функции прерывают выполнение программы, если что-то пошло не так.
assert() (только в Debug)
let age = -5
assert(age >= 0, "Возраст не может быть отрицательным")


precondition() (работает в Release)
precondition(age >= 0, "Возраст не может быть отрицательным")


fatalError() (прерывает программу)
func getData() -> String {
fatalError("Метод ещё не реализован")
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Какая сложность поиска по массиву в Swift?

– В несортированном массиве — O(n), так как приходится просматривать каждый элемент.
– В отсортированном массиве можно использовать бинарный поиск — O(log n), но только вручную или через методы binarySearch из алгоритмов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Почему приложения под Swift массивные?

Приложения, написанные на Swift, могут быть довольно большими по размеру.

🟠Статическая связка Swift Runtime
Swift — это относительно новый язык, и его стандартная библиотека не встроена в iOS (как, например, Objective-C runtime). Это значит, что при компиляции приложения в его бинарь включаются стандартные Swift-библиотеки.

🟠Bitcode и архитектуры (Fat Binary)
При публикации в App Store, Xcode компилирует приложение для нескольких архитектур (arm64, armv7, x86_64). Это называется Fat Binary — один исполняемый файл включает версии для разных процессоров.

🟠Swift ABI Stability – улучшение, но не панацея
С версии Swift 5.0 ABI (Application Binary Interface) стабилизирован. Это означает, что в iOS 12.2+ уже есть встроенные Swift-библиотеки.

🟠Использование SwiftUI и Combine
SwiftUI и Combine — новые технологии, они не так оптимизированы, как UIKit. При их использовании код разрастается за счёт декларативного подхода и дополнительной логики от Apple.

🟠Дополнительные ресурсы и Assets
Если приложение использует локализацию, изображения, шрифты, CoreML, ARKit, это тоже увеличивает размер .ipa.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что известно про механизм, оптимизирующий копирование?

Это называется Copy-on-Write (COW). Он работает так:
- Пока копия массива (или строки) не изменяется, они разделяют один и тот же блок памяти.
- При попытке модификации создаётся новая копия. Такой механизм экономит память и повышает производительность при передаче больших структур.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что из себя представляет структура данных 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 – поиск в глубину)
История действий (назад-вперед в браузере, отмена в редакторе).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM