Swift | Вопросы собесов
2.13K subscribers
28 photos
948 links
Download Telegram
🤔 Какие есть известные проблемы с многопоточностью?

Известные проблемы с многопоточностью включают гонки данных, блокировки и взаимоблокировки (deadlocks). Гонки данных происходят, когда несколько потоков одновременно пытаются изменить одни и те же данные без должной синхронизации. Блокировки возникают, когда потоки ждут освобождения ресурсов друг от друга, что замедляет работу программы. Взаимоблокировки случаются, когда два потока захватывают ресурсы и не могут завершить работу из-за зависимостей.

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

Метод lazy используется для отложенной (ленивой) инициализации свойств. Это означает, что значение ленивого свойства не вычисляется до первого обращения к нему. Ленивые свойства особенно полезны, когда вычисление значения свойства требует значительных ресурсов или зависит от внешних факторов, которые могут измениться в будущем.

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

🟠Отложенная инициализация:
Свойство с модификатором lazy инициализируется только при первом доступе к нему. До этого момента память для свойства не выделяется и код инициализатора не выполняется.

🟠Требования:
Ленивое свойство всегда должно быть объявлено как переменная (var), так как его значение может измениться после инициализации.

🟠Использование:
Ленивые свойства полезны, когда инициализация значения свойства требует больших затрат ресурсов или сложных вычислений, и нет необходимости выполнять их до первого обращения к свойству.
class DataProcessor {
lazy var expensiveData: [String] = {
// Симуляция затратной операции
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()

init() {
print("DataProcessor инициализирован")
}
}

let processor = DataProcessor()
// expensiveData еще не инициализировано

print("Перед доступом к expensiveData")
print(processor.expensiveData) // Инициализация происходит здесь
print("После доступа к expensiveData")


Вывод
DataProcessor инициализирован
Перед доступом к expensiveData
Инициализация expensiveData
["Data1", "Data2", "Data3"]
После доступа к expensiveData

🚩Плюсы

Оптимизация производительности:
Ленивые свойства помогают избегать ненужных затрат на инициализацию до тех пор, пока свойство не будет действительно использовано.

Отложенные зависимости:
Иногда свойства зависят от внешних данных или состояний, которые могут быть недоступны или неинициализированы на момент создания объекта. Ленивые свойства позволяют отложить инициализацию до тех пор, пока данные или состояния не будут доступны.

Упрощение инициализаторов:
Ленивые свойства могут упростить инициализаторы классов и структур, позволяя избежать сложных вычислений или загрузок данных в момент создания объекта.

🚩Важные моменты

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

🟠Свойства только для чтения:
Если ленивое свойство должно быть только для чтения, его можно объявить как приватную переменную с ленивой инициализацией и предоставить публичное свойство только для чтения.
class DataProcessor {
private lazy var _expensiveData: [String] = {
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()

var expensiveData: [String] {
return _expensiveData
}
}

let processor = DataProcessor()
print(processor.expensiveData) // Инициализация происходит здесь


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

Основное различие между структурами и классами в Swift заключается в том, что структуры являются типами-значениями, а классы — типами-ссылками. Структуры копируются при передаче в функции или при присваивании, а классы передаются по ссылке. Классы поддерживают наследование, в то время как структуры нет. Структуры также автоматически предоставляют конструкторы, и их использование чаще предпочтительно для простых контейнеров данных.

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

Ленивые свойства (использующие модификатор lazy) не являются потокобезопасными по умолчанию. Это означает, что если к ленивому свойству обращаются несколько потоков одновременно, это может привести к состоянию гонки (race condition), когда несколько потоков одновременно пытаются инициализировать одно и то же свойство.

🚩Проблема с потокобезопасностью

Когда несколько потоков одновременно обращаются к ленивому свойству, может возникнуть ситуация, когда несколько из них попытаются инициализировать его одновременно. Это может привести к неопределенному поведению и, в худшем случае, к краху приложения. В этом примере, если два потока одновременно обращаются к expensiveData, может возникнуть состояние гонки при инициализации свойства.
class DataProcessor {
lazy var expensiveData: [String] = {
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()
}

let processor = DataProcessor()

DispatchQueue.global().async {
print(processor.expensiveData)
}

DispatchQueue.global().async {
print(processor.expensiveData)
}


🚩Потокобезопасная инициализация

Чтобы сделать ленивую инициализацию потокобезопасной, можно использовать синхронизацию. Один из способов — использовать последовательную очередь (DispatchQueue) для обеспечения эксклюзивного доступа к ленивому свойству во время его инициализации. В этом примере используется последовательная очередь для синхронизации доступа к ленивому свойству, что гарантирует, что только один поток будет инициализировать свойство.
class DataProcessor {
private var _expensiveData: [String]?
private let queue = DispatchQueue(label: "com.example.dataProcessorQueue")

var expensiveData: [String] {
return queue.sync {
if _expensiveData == nil {
print("Инициализация expensiveData")
_expensiveData = ["Data1", "Data2", "Data3"]
}
return _expensiveData!
}
}
}

let processor = DataProcessor()

DispatchQueue.global().async {
print(processor.expensiveData)
}

DispatchQueue.global().async {
print(processor.expensiveData)
}


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

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

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

🚩Случаи использования

🟠Частое добавление и удаление элементов
Связанный список позволяет эффективно добавлять и удалять элементы в начале или середине списка (\(O(1)\) времени при наличии ссылки на узел), в то время как в массиве это занимает \(O(n)\) времени.
LinkedList<Integer> list = new LinkedList<>();
list.addFirst(1); // O(1)
list.addLast(2); // O(1)
list.add(1, 3); // O(1) при наличии ссылки на узел
list.remove(1); // O(1) при наличии ссылки на узел


🟠Неизвестный размер данных или динамическое изменение размера
Связанный список автоматически расширяется и сжимается, в отличие от массивов.

🟠Итерация по элементам
Часто используется для реализации очередей и стеков, где важны операции добавления и удаления с начала или конца списка.
LinkedList<Integer> queue = new LinkedList<>();
queue.addLast(1); // Enqueue
queue.addLast(2);
queue.removeFirst(); // Dequeue

LinkedList<Integer> stack = new LinkedList<>();
stack.addFirst(1); // Push
stack.addFirst(2);
stack.removeFirst(); // Pop


🟠Использование в качестве базовой структуры данных для других структур
Используется для реализации хеш-таблиц и графов.

🟠Минимизация использования памяти
Эффективен, когда размер элементов или узлов варьируется, так как не требует непрерывного блока памяти.

Реализация LRU-кеша (Least Recently Used cache):
class LRUCache<K, V> {
private final int capacity;
private final LinkedHashMap<K, V> map;

public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
};
}

public V get(K key) {
return map.getOrDefault(key, null);
}

public void put(K key, V value) {
map.put(key, value);
}
}


Реализация двусторонних очередей (Deque):
LinkedList<Integer> deque = new LinkedList<>();
deque.addFirst(1); // Add to front
deque.addLast(2); // Add to back
deque.removeFirst(); // Remove from front
deque.removeLast(); // Remove from back


🚩Плюсы

Эффективное добавление и удаление элементов.
Гибкость размера.
Не требует непрерывного блока памяти.

🚩Минусы

Медленный доступ к элементам по индексу (\(O(n)\)).
Дополнительные накладные расходы на хранение указателей.
Повышенная сложность управления памятью.

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

В Swift память управляется с помощью Automatic Reference Counting (ARC), который автоматически подсчитывает количество ссылок на объекты и освобождает память, когда объект больше не используется. Память делится на две части: стек (stack) и куча (heap). Стек используется для хранения локальных переменных и управления потоком выполнения, а куча — для динамически выделяемых объектов, таких как экземпляры классов. ARC работает с объектами на куче, автоматически освобождая память, когда счётчик ссылок становится равен нулю.

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

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

🟠Использование слабых и неустранимых ссылок
Слабые ссылки (Weak References)
Используйте weak для ссылок, которые могут стать nil в какой-то момент времени. Это предотвращает удержание объектов в памяти, когда они больше не нужны.
Неустранимые ссылки (Unowned References)
Используйте unowned для ссылок, которые должны существовать на протяжении всего времени жизни объекта, к которому они принадлежат.
class Person {
var name: String
weak var friend: Person?

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


🟠Избегание циклических ссылок
Циклические ссылки возникают, когда два объекта имеют сильные ссылки друг на друга, что предотвращает освобождение их из памяти. Использование слабых и неустранимых ссылок помогает избежать этой проблемы.
class A {
var b: B?
}

class B {
weak var a: A?
}


🟠Использование Capture Lists в замыканиях
Замыкания (closures) могут захватывать ссылки на объекты, что может привести к циклическим ссылкам. Используйте списки захвата (capture lists), чтобы указать, как должны захватываться ссылки.
class ViewController: UIViewController {
var name: String = "ViewController"

func setupClosure() {
let closure = { [weak self] in
print(self?.name ?? "No name")
}
closure()
}
}


🚩Инструменты для обнаружения и устранения

🟠Xcode Instruments
Leaks
Инструмент для обнаружения утечек памяти. Позволяет увидеть, какие объекты не освобождаются из памяти.
Allocations
Инструмент для отслеживания распределения памяти и анализа использования памяти.
Использование Leaks в Instruments
1⃣Откройте проект в Xcode.
2⃣Выберите Product > Profile или нажмите Command + I.
2⃣Выберите инструмент Leaks и начните запись.
3⃣Выполните действия в приложении, которые могут привести к утечкам памяти.
4⃣Просмотрите результаты и устраните обнаруженные утечки.

🟠Xcode Memory Graph Debugger
Встроенный инструмент в Xcode, который позволяет визуально анализировать граф объектов и выявлять утечки памяти.
Использование Memory Graph Debugger
1⃣Запустите приложение в Xcode.
2⃣Выберите Debug > View Memory Graph Hierarchy.
3⃣Анализируйте граф объектов для выявления неожиданных ссылок и циклических зависимостей.

🚩Практические советы

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

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

Value types в Swift — это типы данных, которые копируются при передаче в другую переменную или константу. Примеры value types включают структуры (struct), перечисления (enum) и встроенные типы данных, такие как Int и String. Когда вы изменяете копию value type, оригинальная переменная остаётся неизменной. Это поведение помогает избегать неожиданных изменений данных при работе с несколькими переменными.

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

Могут возникать, когда два объекта удерживают сильные ссылки друг на друга, что препятствует их освобождению из памяти. Замыкания (closures) могут захватывать ссылки на объекты, что также может приводить к циклическим ссылкам. Capture list (список захвата) в замыканиях помогает предотвратить такие циклические ссылки, позволяя явно указать, как замыкание должно захватывать ссылки на объекты.

🚩Что такое?

Это механизм, который используется в замыканиях для указания, как должны захватываться ссылки на объекты. Capture list позволяет указать, должны ли захватываемые ссылки быть сильными (strong) или слабыми (weak), или неустранимыми (unowned).

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

Capture list указывается в квадратных скобках в начале замыкания. Каждый элемент списка захвата указывает захватываемую переменную и желаемый семантический тип захвата (например, weak или unowned).

🟠Захват слабых ссылок (weak)
Использование слабых ссылок предотвращает удержание объектов в памяти замыканием, что предотвращает циклические ссылки. Замыкание захватывает self с помощью слабой ссылки (weak self). Слабая ссылка не увеличивает счетчик ссылок на self, поэтому объект MyClass может быть освобожден из памяти, когда на него больше нет других сильных ссылок.
class MyClass {
var name: String = "MyClass"

func setupClosure() {
let closure = { [weak self] in
guard let self = self else { return }
print(self.name)
}
closure()
}
}

var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Объект освобождается, так как замыкание не удерживает его в памяти


🟠Захват неустранимых ссылок (unowned)
Использование неустранимых ссылок предполагает, что захваченный объект будет существовать на протяжении всего времени жизни замыкания. Если объект освобождается раньше времени, доступ к неустранимой ссылке приведет к краху приложения. Замыкание захватывает self с помощью неустранимой ссылки (unowned self). Неустранимая ссылка не увеличивает счетчик ссылок на self, но предполагается, что объект будет существовать на протяжении всего времени жизни замыкания.
class MyClass {
var name: String = "MyClass"

func setupClosure() {
let closure = { [unowned self] in
print(self.name)
}
closure()
}
}

var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Объект освобождается, но доступ к unowned ссылке после этого приведет к ошибке


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

Reference types в Swift — это типы данных, которые передаются по ссылке, а не по значению, что означает, что несколько переменных могут указывать на один и тот же объект. Класс (class) является примером reference type. Изменения, внесённые через одну ссылку на объект, отражаются во всех других ссылках на этот объект. Это поведение делает reference types полезными для объектов, которые должны быть изменяемыми и доступными из нескольких мест в коде.

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

🟠Слабая ссылка (weak)
Не удерживает объект в памяти и автоматически обнуляется, когда объект освобождается. Если попытаться обратиться к объекту через слабую ссылку после его освобождения, ссылка будет иметь значение nil.
class MyClass {
var name: String

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

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

var obj: MyClass? = MyClass(name: "Example")
weak var weakRef = obj

print(weakRef?.name) // Output: Optional("Example")

obj = nil // Объект освобождается, weakRef становится nil

print(weakRef?.name) // Output: nil


🟠Неустранимая ссылка (unowned)
Также не удерживает объект в памяти, но не обнуляется автоматически. Если попытаться обратиться к объекту через неустранимую ссылку после его освобождения, это приведет к аварийному завершению программы (runtime crash), так как ссылка будет указывать на несуществующий объект.
class MyClass {
var name: String

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

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

class AnotherClass {
unowned var myObject: MyClass

init(myObject: MyClass) {
self.myObject = myObject
}
}

var obj: MyClass? = MyClass(name: "Example")
var anotherObj: AnotherClass? = AnotherClass(myObject: obj!)

print(anotherObj?.myObject.name) // Output: Example

obj = nil // Объект освобождается, но anotherObj.myObject не обнуляется

// Попытка доступа к myObject через unowned ссылку после освобождения объекта вызовет runtime crash
print(anotherObj?.myObject.name) // Runtime crash


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Есть ли отличия value тайпа от референса тайпа?

Главное отличие между value types и reference types заключается в том, что value types копируются при передаче, а reference types передаются по ссылке. Это значит, что изменение value types в одной переменной не затрагивает другие переменные, тогда как изменение reference types влияет на все ссылки на объект. Value types обеспечивают большую безопасность и предсказуемость при работе с неизменяемыми данными. Reference types позволяют легче работать с общими изменяемыми данными, но могут привести к ошибкам синхронизации.

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

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

🚩Асинхронность

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

🚩Многопоточность

Предполагает выполнение нескольких потоков параллельно. Поток (thread) — это наименьшая единица обработки, которую операционная система может управлять независимо. Многопоточность используется для выполнения нескольких операций одновременно, что может улучшить производительность на многоядерных процессорах.

🚩Асинхронность без многопоточности

Операции могут быть реализованы без создания дополнительных потоков. Вместо этого, они могут использовать механизм, называемый кооперативной многозадачностью, где выполнение задач управляется посредством событийного цикла (event loop).

🟠JavaScript и Node.js
JavaScript в браузере и Node.js используют одно поточный событийный цикл для управления асинхронными операциями. Операции, такие как сетевые запросы или таймеры, выполняются асинхронно, но они не требуют создания дополнительных потоков.
console.log("Start");

setTimeout(() => {
console.log("Timeout callback");
}, 1000);

console.log("End");

// Output:
// Start
// End
// Timeout callback (после 1 секунды)


🟠Dispatch Queues в GCD (Grand Central Dispatch)
В iOS, DispatchQueue.main.async позволяет выполнять код асинхронно на главной очереди без создания нового потока. Это часто используется для обновления пользовательского интерфейса.
DispatchQueue.main.async {
// Этот код выполняется асинхронно на главной очереди
print("Async task on main queue")
}


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

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

🚩Плюсы и минусы

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

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

В Swift есть три основных типа коллекций: массивы (Array), множества (Set) и словари (Dictionary). Массивы хранят элементы в порядке вставки и поддерживают доступ по индексу. Множества — это неупорядоченные коллекции уникальных элементов. Словари — это коллекции, которые хранят пары ключ-значение, где каждый ключ уникален, а значения могут быть любыми типами. Все эти коллекции в Swift поддерживают обобщённые типы, что делает их гибкими для работы с любыми данными.

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

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

🚩Асинхронность

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

🚩Многопоточность

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

🚩Взаимосвязь и независимость

🟠Асинхронность без многопоточности
Асинхронные операции могут быть выполнены без создания дополнительных потоков. Например, JavaScript и Node.js используют одно поточный событийный цикл для управления асинхронными операциями. В этом примере setTimeout выполняет асинхронную операцию в одном потоке.
console.log("Start");

setTimeout(() => {
console.log("Timeout callback");
}, 1000);

console.log("End");


🟠Многопоточность без асинхронности
Многопоточность может использоваться для выполнения нескольких задач параллельно без необходимости в асинхронности. Например, выполнение вычислительных задач в нескольких потоках без ожидания завершения других задач. В этом примере два потока выполняются параллельно, но операции не являются асинхронными.
let queue1 = DispatchQueue(label: "queue1", qos: .userInitiated)
let queue2 = DispatchQueue(label: "queue2", qos: .userInitiated)

queue1.sync {
for i in 0..<5 {
print("Task 1 - \(i)")
}
}

queue2.sync {
for i in 0..<5 {
print("Task 2 - \(i)")
}
}


🟠Асинхронность с многопоточностью
Асинхронные операции могут быть выполнены в разных потоках для повышения производительности. Например, асинхронная загрузка данных в фоновом потоке, чтобы не блокировать основной поток пользовательского интерфейса. В этом примере данные загружаются асинхронно в фоновом потоке, а затем результат обновляется в основном потоке.
DispatchQueue.global(qos: .background).async {
// Выполнение фоновой задачи
let data = loadData()
DispatchQueue.main.async {
// Обновление UI после завершения фоновой задачи
updateUI(with: data)
}
}


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

Асинхронная задача (async task) в Swift — это операция, которая выполняется в фоновом режиме, не блокируя основной поток исполнения. Асинхронные задачи позволяют продолжать выполнение программы, пока задача выполняется, и обрабатывать результат позже. В Swift 5.5 и позже используется ключевое слово `async/await` для упрощения работы с асинхронными операциями, что делает код более читаемым и управляемым. Асинхронные задачи полезны для операций, таких как сетевые запросы или работа с файлами.

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

Это архитектурный шаблон для iOS-приложений, который обещает улучшить разбиение кода на модули и упростить тестирование. Однако, несмотря на свои преимущества, VIPER может быть воспринят как "боль" по ряду причин.

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

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

🟠Большой объем шаблонного кода
VIPER требует написания большого количества шаблонного кода, особенно для создания связей между компонентами. Это может привести к усталости разработчика и увеличению времени разработки. Этот шаблонный код нужно будет повторять для каждого модуля, что может быть утомительным и трудоемким.
protocol SomeModuleViewProtocol: AnyObject {
var presenter: SomeModulePresenterProtocol? { get set }
}

protocol SomeModuleInteractorProtocol: AnyObject {
var presenter: SomeModulePresenterProtocol? { get set }
}

protocol SomeModulePresenterProtocol: AnyObject {
var view: SomeModuleViewProtocol? { get set }
var interactor: SomeModuleInteractorProtocol? { get set }
var router: SomeModuleRouterProtocol? { get set }
}

protocol SomeModuleRouterProtocol: AnyObject {
static func createModule() -> SomeModuleViewProtocol
}


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

🟠Перегруженность проекта
Когда проект растет, количество VIPER-модулей может стать огромным. Это может сделать проект перегруженным и сложным для управления, особенно в крупных командах.

🚩Плюсы

Разделение ответственности
Каждый компонент имеет свою четко определенную роль.
Тестируемость
Модули VIPER легко тестировать из-за их четкой структуры и разделения.
Модульность
Легче переиспользовать и обновлять компоненты.

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

Диспетчеризация (dispatch) в Swift — это механизм, который управляет выполнением задач в многопоточном окружении, распределяя задачи между различными потоками. В Swift используется Grand Central Dispatch (GCD) для асинхронного и синхронного выполнения задач на разных очередях. Диспетчеризация позволяет эффективно управлять ресурсами процессора и улучшать производительность программ. Она также обеспечивает простоту в работе с многопоточностью, минимизируя возможность ошибок при синхронизации.

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

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

🚩Методы

🟠Grand Central Dispatch (GCD)
Предоставляет низкоуровневый интерфейс для управления параллельными задачами, используя глобальные или пользовательские конкурентные очереди.
DispatchQueue.global(qos: .userInitiated).async {
// Первая задача
performFirstTask()
}

DispatchQueue.global(qos: .userInitiated).async {
// Вторая задача
performSecondTask()
}


🟠OperationQueue
Это более высокоуровневый интерфейс для управления параллельными задачами с возможностью задания зависимостей и приоритетов.
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 2 // Устанавливаем количество одновременно выполняемых задач

let operation1 = BlockOperation {
performFirstTask()
}

let operation2 = BlockOperation {
performSecondTask()
}

operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)


🟠Dispatch Group
Позволяет группировать несколько асинхронных задач и уведомлять, когда все задачи в группе завершены.
let dispatchGroup = DispatchGroup()

DispatchQueue.global(qos: .userInitiated).async(group: dispatchGroup) {
// Первая задача
performFirstTask()
}

DispatchQueue.global(qos: .userInitiated).async(group: dispatchGroup) {
// Вторая задача
performSecondTask()
}

dispatchGroup.notify(queue: .main) {
// Все задачи завершены
updateUI()
}


🟠Combine
Это фреймворк для реактивного программирования, который позволяет работать с асинхронными потоками данных и выполнять задачи параллельно.
import Combine

let publisher1 = Just("Task 1").delay(for: 2.0, scheduler: DispatchQueue.global())
let publisher2 = Just("Task 2").delay(for: 2.0, scheduler: DispatchQueue.global())

Publishers.Zip(publisher1, publisher2)
.receive(on: DispatchQueue.main)
.sink { task1, task2 in
print("Both tasks completed: \(task1), \(task2)")
}
.store(in: &cancellables)


🟠URLSession для параллельных сетевых запросов
Автоматически управляет параллельным выполнением сетевых запросов.
let url1 = URL(string: "https://example.com/data1")!
let url2 = URL(string: "https://example.com/data2")!

let task1 = URLSession.shared.dataTask(with: url1) { data, response, error in
// Обработка первого ответа
}

let task2 = URLSession.shared.dataTask(with: url2) { data, response, error in
// Обработка второго ответа
}

task1.resume()
task2.resume()


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