Swift | Вопросы собесов
2.13K subscribers
28 photos
952 links
Download Telegram
🤔 Преимущества и недостатки синхронного и асинхронного соединения?

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

🚩Синхронное соединение

Плюсы
Простота реализации
Синхронные операции проще понимать и реализовывать, так как они выполняются последовательно.

Легкость отладки
Отладка синхронного кода проще, так как последовательность выполнения предсказуема.

Определённость выполнения
Синхронный код выполняется по порядку, что делает логику более прозрачной.

Минусы
Блокировка ресурсов
Синхронные операции могут блокировать основной поток выполнения, что приводит к замедлению работы интерфейса или приложения в целом.

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

Меньшая масштабируемость
Синхронный подход может ограничивать возможности масштабирования, так как каждая операция ожидает завершения предыдущей.

🚩Асинхронное соединение

Плюсы

Высокая производительность
Асинхронные операции позволяют выполнять другие задачи, не дожидаясь завершения долгих операций, что увеличивает общую производительность приложения.

Отзывчивость интерфейса
Асинхронный подход позволяет поддерживать отзывчивость пользовательского интерфейса, так как долгие операции выполняются в фоновом режиме.

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

Минусы

Сложность реализации
Асинхронные операции требуют более сложной реализации и понимания концепций, таких как колбэки, промисы или async/await.

Сложность отладки
Отладка асинхронного кода может быть сложнее из-за непредсказуемой последовательности выполнения.

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

🚩Пример в Swift

Синхронный пример
func fetchDataSynchronously() {
let url = URL(string: "https://api.example.com/data")!
let data = try? Data(contentsOf: url)
if let data = data {
print("Данные загружены: \(data)")
} else {
print("Ошибка загрузки данных")
}
}


Асинхронный пример
func fetchDataAsynchronously() {
let url = URL(string: "https://api.example.com/data")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print("Данные загружены: \(data)")
} else if let error = error {
print("Ошибка загрузки данных: \(error)")
}
}.resume()
}


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

Это модификатор, который предотвращает изменения:
- Класс: нельзя наследовать.
- Метод: нельзя переопределять.
- Переменная: значение нельзя изменить после инициализации.


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

Не поддерживает множественное наследование для классов, что является общим выбором в современных объектно-ориентированных языках программирования из-за сложностей и проблем, которые множественное наследование может создать (например, проблема алмаза смерти). Однако предлагаются альтернативные механизмы, позволяющие имитировать некоторые аспекты множественного наследования.

🟠Протоколы
Похожи на интерфейсы в других языках. Они позволяют классам, структурам и перечислениям принимать "контракты", которые определяют методы и свойства, которые должны быть реализованы. Протоколы могут наследоваться и расширяться, предоставляя гибкий способ добавления функциональности к типам.
protocol Movable {
func move()
}

protocol Drawable {
func draw()
}

class Character: Movable, Drawable {
func move() {
print("Персонаж двигается")
}

func draw() {
print("Персонаж рисуется")
}
}


🟠Расширения
Позволяют добавлять новую функциональность к существующим типам, включая те, которые определены в стандартной библиотеке или внешних библиотеках. Хотя расширения не могут добавлять хранимые свойства, они могут добавлять новые вычисляемые свойства, методы и протоколы.
extension Character: Drawable {
func draw() {
print("Персонаж рисуется через расширение")
}
}


🟠Композиция
Это альтернативный подход к наследованию, при котором один тип включает другой как часть своей реализации, вместо того чтобы наследовать от него. Это предпочтительный метод для достижения повторного использования кода, позволяющий избежать ограничений и сложностей наследования.
class Engine {
func start() {
print("Двигатель запущен")
}
}

class Car {
let engine = Engine()

func startEngine() {
engine.start()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1
🤔 В чём разница между View и Layer?

- View: отображает контент на экране и управляет взаимодействием с пользователем.
- Layer: низкоуровневый компонент (из Core Animation), который отвечает за рендеринг содержимого и может использоваться для сложных анимаций или оптимизаций.


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

Используется в замыканиях (closures) для управления тем, как замыкание захватывает переменные и константы из окружающего контекста. Могут захватывать и хранить ссылки на любые константы и переменные из контекста, в котором они определены. Это удобно, но может привести к сильным ссылочным циклам и утечкам памяти, если замыкание захватывает self или другие экземпляры класса.

🚩Как он работает

Предоставляет способ определить правила захвата переменных в замыкании. Она задаётся в начале замыкания и позволяет избежать нежелательных сильных ссылок, особенно при работе с self в методах класса, что очень важно для предотвращения утечек памяти в приложениях.

Синтаксис Capture List
{ [capture rule] (parameters) -> return type in
// Код замыкания
}


Пример с простым замыканием
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}

a = 10
b = 10
closure() // Выведет "0 10"


Использование Capture List для предотвращения сильных ссылочных циклов
class MyClass {
var property = "Property"

func doSomething() {
let closure = { [weak self] in
print(self?.property ?? "нет self")
}
closure()
}

deinit {
print("MyClass экземпляр был деинициализирован")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🤔 Когда value type может храниться в куче?

Value type может быть размещён в куче, если он используется внутри reference type, например, передан как свойство объекта класса.

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

Термин "диспетчеризация" часто используется для описания механизма, посредством которого вызывается метод или функция в ответ на вызов. В контексте ООП, особенно в языках с поддержкой полиморфизма, диспетчеризация может быть статической (ранней) или динамической (поздней).

🚩Статическая диспетчеризация (Static Dispatch)

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

🟠Плюсы
Быстродействие: Так как адрес метода известен на этапе компиляции, нет необходимости в дополнительных проверках или вычислениях во время выполнения.
Простота: Проще для оптимизации компилятором.

🟠Структура
В языках, таких как C и Swift (при использовании final классов или структур), методы, известные во время компиляции, могут быть вызваны без дополнительной нагрузки, связанной с поиском в таблице виртуальных функций.

🚩Динамическая диспетчеризация (Dynamic Dispatch)

Используется в языках программирования, поддерживающих полиморфизм и наследование, и означает, что метод, который будет вызван, определяется во время выполнения. Это позволяет объектам различных классов отвечать на одни и те же сообщения (вызовы методов), каждый по-своему.

🟠Плюсы
Гибкость: Позволяет объектам разных классов обрабатывать одинаковые вызовы методов различным образом.
Поддержка полиморфизма: Один и тот же вызов метода может вести к выполнению разных функций.

🟠Структура
Таблица виртуальных методов (VMT): В объектно-ориентированных языках, таких как C++ или Java, каждый класс с виртуальными методами имеет таблицу виртуальных методов. Эта таблица содержит адреса всех виртуальных методов, которые могут быть вызваны для объекта данного класса. Поиск по таблице: Во время выполнения, когда вызывается метод, производится поиск соответствующего метода в таблице VMT, и используется адрес, найденный в таблице, что вносит задержку по сравнению со статической диспетчеризацией.

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

- Сильные ссылки (strong): увеличивают счётчик ссылок объекта, предотвращая его удаление.
- Слабые ссылки (weak): не увеличивают счётчик ссылок, используются для предотвращения циклических зависимостей.


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

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

🚩Основы наследования

🟠Определение базового класса
Базовый класс определяет общие свойства и методы, которые могут быть унаследованы подклассами.

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

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

🟠Предотвращение переопределения
Можно предотвратить переопределение методов, свойств или индексаторов с помощью ключевого слова final. Если метод, свойство или индексатор объявлен как final, то он не может быть переопределён в подклассе.
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// Этот метод будет переопределен в подклассах, если необходимо
}
}

class Bicycle: Vehicle {
var hasBasket = false
}

class Car: Vehicle {
var gear = 1
final func drive() {
print("Car is moving")
}
override func makeNoise() {
print("Vroom!")
}
}


🚩Использование super

Подклассы могут вызывать методы своего суперкласса с помощью ключевого слова super. Это позволяет подклассам расширять, а не заменять поведение суперкласса.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое Grand Central Dispatch (GCD)?

Это технология для работы с многопоточностью в iOS и macOS. Она управляет очередями задач (sync/async, serial/concurrent) и позволяет эффективно распределять задачи между потоками.

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

Обычно имеют в виду различия между использованием стека вызовов (память стека) и кучи (heap) в контексте управления памятью в программировании. Эти два типа памяти имеют разные способы выделения и освобождения памяти, каждый со своими преимуществами и недостатками.

🟠Способ выделения памяти
Память в стеке выделяется и освобождается по очень простому и быстрому принципу: LIFO (Last In, First Out). Это означает, что для выделения памяти достаточно переместить указатель стека вверх (при добавлении данных) или вниз (при удалении данных). Этот процесс почти мгновенен и не требует дополнительных вычислений.
В куче память выделяется динамически, что требует управления доступными блоками памяти. Аллокатор памяти должен найти достаточно большой свободный блок, что может занять значительное время, особенно если память фрагментирована. Также освобождение памяти в куче требует более сложной обработки, включая возможную дефрагментацию.

🟠Скорость доступа
Доступ к данным в стеке очень быстрый, потому что данные всегда добавляются и удаляются с "вершины" стека, где находится указатель стека. Это делает доступ к текущим локальным переменным очень быстрым и предсказуемым. Доступ к данным в куче может быть менее эффективным, поскольку данные могут быть разбросаны по разным частям памяти. Кроме того, дополнительное время требуется для поиска и управления блоками памяти.

🟠Детерминированное управление памятью
Его память автоматически очищается при выходе из области видимости, что упрощает управление памятью и снижает риск утечек памяти. Память, выделенная в куче, остаётся занятой до тех пор, пока явно не будет освобождена. Это увеличивает риск утечек памяти, если разработчик забудет освободить память.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что под капотом кучи?

Это область памяти, управляемая системой, где объекты выделяются динамически. Управление включает:
- Аллокацию памяти.
- Освобождение через сборщик мусора (в Java, Swift) или вручную (в C++).
- Компактирование для предотвращения фрагментации.


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

Это абстрактная структура данных, работающая по принципу LIFO (Last In, First Out), что означает "последний пришёл — первый вышел". Это значит, что последний добавленный элемент будет первым при извлечении из стека. Под капотом реализации стека могут быть разные, и они зависят от конкретного языка программирования и задач, которые необходимо решить.

🟠Массивы
Один из самых распространённых способов реализации стека — это использование массива. В такой реализации элементы стека хранятся в массиве, и индекс последнего элемента (вершина стека) отслеживается отдельной переменной.
struct Stack<Element> {
private var storage: [Element] = []

mutating func push(_ element: Element) {
storage.append(element)
}

mutating func pop() -> Element? {
return storage.popLast()
}

func peek() -> Element? {
return storage.last
}

var isEmpty: Bool {
return storage.isEmpty
}
}


🟠Связные списки
Стек можно реализовать с использованием связных списков, где каждый элемент списка содержит данные и ссылку на следующий элемент в стеке. Вершина стека в такой реализации — это начало связного списка.
class Node<Element> {
var value: Element
var next: Node?

init(value: Element) {
self.value = value
}
}

struct Stack<Element> {
private var head: Node<Element>?

mutating func push(_ element: Element) {
let node = Node(value: element)
node.next = head
head = node
}

mutating func pop() -> Element? {
let node = head
head = head?.next
return node?.value
}

func peek() -> Element? {
return head?.value
}

var isEmpty: Bool {
return head == nil
}
}


🟠Стек вызовов
Это системный стек, который используется во время выполнения программы для хранения информации о вызовах функций/методов. Он хранит адреса возврата, параметры функций, локальные переменные и другие данные, необходимые для управления вызовами функций и их возврата.

🚩Зачем он нужен?

Обратную польскую нотацию для вычисления арифметических выражений. Управление вызовами функций в программном стеке. Поддержка операций undo в приложениях.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
4
🤔 Когда value типы могут стать reference?

Value типы становятся reference, если они упакованы (например, через Box в Swift) или если они хранятся в контейнерах, которые управляются ссылочным механизмом (например, массивы при копировании).

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

В iOS-приложениях можно создавать анимации несколькими способами.

🟠Использование `UIView.animate`
Самый простой способ анимации представлений (views) в iOS - это использование метода UIView.animate. Вот пример кода, который изменяет положение и прозрачность представления за 1 секунду:
UIView.animate(withDuration: 1.0) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}


🟠Использование `CABasicAnimation`
Для более сложных анимаций можно использовать Core Animation, например, CABasicAnimation. Вот пример анимации изменения позиции слоя (layer):
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: CGPoint(x: 50, y: 50))
animation.toValue = NSValue(cgPoint: CGPoint(x: 150, y: 150))
animation.duration = 1.0
myView.layer.add(animation, forKey: "positionAnimation")


🟠Использование `UIViewPropertyAnimator`
Этот класс предоставляет более детальный контроль над анимациями. Его можно использовать для создания и управления анимациями в реальном времени:
let animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}
animator.startAnimation()


🟠Использование анимаций с пружинным эффектом
Для создания реалистичных анимаций с пружинным эффектом можно использовать метод UIView.animate с параметрами пружинного демпфирования:
UIView.animate(withDuration: 1.0,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: [],
animations: {
myView.frame.origin.y += 100
myView.alpha = 0.5
}, completion: nil)


🟠Анимация переходов между контроллерами
Для анимации переходов между экранами используется UIViewControllerAnimatedTransitioning. Это требует реализации методов протокола UIViewControllerAnimatedTransitioning:
class CustomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to) else { return }

transitionContext.containerView.addSubview(toView)
toView.alpha = 0.0

UIView.animate(withDuration: 0.5, animations: {
toView.alpha = 1.0
}, completion: { finished in
transitionContext.completeTransition(finished)
})
}
}


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

Это базовый класс для обработки событий в UIKit. Объекты типа UIView, UIViewController, UIApplication наследуются от UIResponder и участвуют в цепочке обработки событий, таких как нажатия, свайпы или жесты.

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

Это ключевое слово, используемое для объявления слабой ссылки (weak reference) на объект, которая не увеличивает счетчик ссылок этого объекта. В отличие от weak, unowned ссылки никогда не становятся nil, поэтому они используются в тех случаях, когда можно гарантировать, что объект, на который ссылаются, будет существовать так же долго, как и сама ссылка.

🚩Когда использовать?

🟠Оба объекта существуют одновременно
Например, если один объект никогда не будет существовать дольше другого объекта, и тем самым вы уверены, что ссылка всегда будет действительной.

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

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

``` swift
class Person {
let name: String
var creditCard: CreditCard?

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

deinit {
print("\(name) is being deinitialized")
}
}

class CreditCard {
let number: String
unowned let owner: Person

init(number: String, owner: Person) {
self.number = number
self.owner = owner
}

deinit {
print("CreditCard #\(number) is being deinitialized")
}
}

// Пример использования
var john: Person? = Person(name: "John Appleseed")
john?.creditCard = CreditCard(number: "1234 5678 9012 3456", owner: john!)

john = nil
```

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

Это метод, который вызывается перед удалением объекта из памяти. В Swift используется метод deinit. Он позволяет выполнять действия, такие как освобождение ресурсов или закрытие соединений, когда объект больше не нужен.


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

Это структура данных, которая работает по принципу "первым пришел - первым ушел" (FIFO, First In, First Out). Это значит, что элементы добавляются в конец очереди и извлекаются из начала очереди.

🚩Основные операции с очередью

🟠Добавление (enqueue)
Вставка элемента в конец очереди.
🟠Удаление (dequeue)
Удаление элемента из начала очереди.
🟠Проверка первого элемента (peek)
Просмотр элемента в начале очереди без его удаления.
🟠Проверка на пустоту (isEmpty)
Проверка, содержит ли очередь элементы.

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

Очереди часто используются в задачах, где порядок обработки элементов важен.
Управление задачами в системах реального времени.
Обработка запросов в сетевых приложениях.
Имплементация алгоритмов обхода графов (например, поиск в ширину).

struct Queue<T> {
private var elements: [T] = []

// Добавление элемента в конец очереди
mutating func enqueue(_ element: T) {
elements.append(element)
}

// Удаление элемента из начала очереди
mutating func dequeue() -> T? {
return isEmpty ? nil : elements.removeFirst()
}

// Просмотр элемента в начале очереди
func peek() -> T? {
return elements.first
}

// Проверка на пустоту
var isEmpty: Bool {
return elements.isEmpty
}
}

// Пример использования
var queue = Queue<Int>()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)

print(queue.dequeue() ?? "Queue is empty") // Вывод: 1
print(queue.peek() ?? "Queue is empty") // Вывод: 2


🚩Варианты использования в iOS

🟠Очереди задач (DispatchQueue)
GCD (Grand Central Dispatch) использует очереди для управления многозадачностью. Например, DispatchQueue.main.async добавляет задачи в очередь для выполнения на главном потоке:
DispatchQueue.main.async {
// Код выполняется на главном потоке
}


🟠Операционные очереди (OperationQueue)
Используются для управления и упорядочивания выполнения множества Operation объектов:
let queue = OperationQueue()
queue.addOperation {
print("Operation 1")
}
queue.addOperation {
print("Operation 2")
}


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

Это механизм управления памятью в системах с подсчетом ссылок (Reference Counting):
1. Retain: увеличивает счетчик ссылок объекта, указывая, что объект используется.
2. Release: уменьшает счетчик ссылок. Когда счетчик достигает нуля, объект освобождается из памяти.
Эта модель широко использовалась в Objective-C до появления ARC (Automatic Reference Counting).


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