Swift | Вопросы собесов
2.13K subscribers
28 photos
941 links
Download Telegram
Какие есть паттерны в объектном проектировании ?
Спросят с вероятностью 27%

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

Порождающие паттерны
Эти паттерны связаны с процессами создания объектов, делая систему независимой от способа создания, компоновки и представления объектов.

Одиночка (Singleton): Обеспечивает создание только одного экземпляра класса и предоставляет глобальную точку доступа к этому экземпляру.
Фабричный метод (Factory Method): Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
Абстрактная фабрика (Abstract Factory): Предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов без указания их конкретных классов.
Прототип (Prototype): Позволяет копировать существующие объекты без делания кода зависимым от их классов.
Строитель (Builder): Позволяет создавать сложные объекты пошагово, используя один и тот же процесс строительства для получения разных представлений.

Структурные паттерны
Описывают, как объединять объекты и классы в более крупные структуры.

Адаптер (Adapter): Позволяет объектам с несовместимыми интерфейсами работать вместе.
Мост (Bridge): Разделяет абстракцию и реализацию так, чтобы они могли изменяться независимо.
Композит (Composite): Комбинирует объекты в древовидные структуры для представления иерархий "часть-целое".
Декоратор (Decorator): Динамически добавляет объектам новые обязанности без изменения их реализации.
Фасад (Facade): Предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.
Прокси (Proxy): Предоставляет заместителя или заполнитель для другого объекта для контроля доступа к нему.

Поведенческие паттерны
Регулируют эффективное взаимодействие и распределение обязанностей между объектами.

Наблюдатель (Observer): Создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах.
Стратегия (Strategy): Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.
Состояние (State): Позволяет объекту изменять своё поведение в зависимости от своего состояния.
Команда (Command): Превращает запросы в объекты, позволяя передавать их как аргументы при вызове методов, ставить их в очередь, логировать и т.д.
Цепочка обязанностей (Chain of Responsibility): Позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и следует ли передать запрос дальше по цепочке.
Посредник (Mediator): Позволяет уменьшить взаимосвязь между классами, вынося межклассовые взаимодействия в класс-посредник.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент
👍3
Для каких сущностей работает copy on write ?
Спросят с вероятностью 27%

Механизм Copy-on-Write (CoW) используется для оптимизации производительности и использования памяти при копировании объектов. Этот механизм особенно полезен для неизменяемых (immutable) структур данных. CoW часто ассоциируется со стандартными коллекциями и собственными типами данных, реализованными как структуры (value types), такие как Array, String, Dictionary, и Set.

Принцип работы Copy-on-Write

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

Как это работает в Swift

Автоматически применяет механизм CoW к своим стандартным коллекциям, таким как Array, String, Dictionary, и Set. Это означает, что при передаче этих объектов в функции или при их копировании реальное дублирование данных происходит только в случае модификации одной из копий. Таким образом, если вы создаёте копию массива и не изменяете его, обе переменные будут указывать на одни и те же данные в памяти. Как только вы модифицируете одну из копий, Swift создаст реальную копию данных для этой копии, обеспечивая независимость данных между оригиналом и копией.

Пример:
var originalArray = [1, 2, 3]
var copiedArray = originalArray // На этом этапе данные не дублируются

copiedArray.append(4) // Теперь данные копируются, потому что copiedArray модифицируется


В этом примере до вызова append обе переменные, originalArray и copiedArray, ссылаются на один и тот же набор данных. Модификация copiedArray активирует механизм CoW, и Swift создаёт реальную копию данных для copiedArray.

Реализация CoW для собственных типов данных

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

Copy-on-Write — это механизм оптимизации, который позволяет отложить копирование данных до момента их реальной модификации. Это улучшает производительность и эффективность использования памяти, особенно при работе с большими структурами данных. Swift автоматически применяет CoW к своим стандартным коллекциям, таким как Array, String, DictionarySet.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент
👍2
Что известно о MVVM ?
Спросят с вероятностью 27%

MVVM (Model-View-ViewModel) — это архитектурный паттерн, разработанный для упрощения создания пользовательского интерфейса, который был впервые представлен Microsoft для использования с Windows Presentation Foundation (WPF) и Silverlight. С тех пор он нашёл широкое применение в различных технологиях разработки программного обеспечения, включая разработку под iOS и Android. MVVM помогает разделить логику представления интерфейса (UI) от бизнес-логики и логики приложения, упрощая тестирование и поддержку.

Основные компоненты:

Model (Модель): Содержит бизнес-логику и данные приложения. Модель отвечает за доступ к данным, их хранение, валидацию, обработку и т.д.
View (Представление): Отображает данные (модель) пользователю и передаёт пользовательский ввод (например, нажатие кнопки) в ViewModel. В контексте iOS это могут быть UIViews и UIViewControllerы.
ViewModel (Модель представления): Служит посредником между View и Model, предоставляя поток данных между ними. ViewModel обрабатывает всю логику представления, включая преобразование данных из Model для отображения в View. Она также обрабатывает все действия пользователей, переданные из View, и может вызывать изменения в Model.

Особенности и преимущества:

Разделение ответственности: Чётко разделяет логику представления от бизнес-логики, что облегчает тестирование и поддержку кода.
Упрощение тестирования: Благодаря изоляции бизнес-логики и логики представления, ViewModel можно тестировать независимо от пользовательского интерфейса и логики работы с данными.
Привязка данных (Data Binding): Часто использует механизмы привязки данных, чтобы обеспечить автоматическое обновление View при изменении данных в ViewModel и наоборот. Это уменьшает количество шаблонного кода для обновления интерфейса.
Улучшение поддерживаемости: Изменения в пользовательском интерфейсе или бизнес-логике оказывают меньшее влияние на другие аспекты приложения, что упрощает внесение изменений.

MVVM может быть реализован с использованием различных фреймворков и технологий, таких как RxSwift или Combine, которые предоставляют мощные инструменты для реализации привязки данных и управления потоками данных. Это позволяет разработчикам создавать реактивные приложения, где View автоматически обновляется в ответ на изменения данных, обеспечивая более плавный и интуитивно понятный пользовательский опыт.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент
👍2
Каков жизненный цикл приложения ?
Спросят с вероятностью 27%

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

1️⃣Не запущено (Not Running)
Приложение ещё не было запущено или было завершено.

2️⃣Неактивное состояние (Inactive)
Приложение запущено, но не получает события. Это может произойти в моменты временного перерыва, например, во время перехода из одного состояния в другое, или когда система ожидает ответа от пользователя или приложения.

3️⃣Активное состояние (Active)
Приложение активно и получает события. Это основное рабочее состояние приложения, когда пользователь взаимодействует с его интерфейсом.

4️⃣Фоновое состояние (Background)
Приложение находится в фоновом режиме и может выполнять код. Приложение переходит в это состояние из активного состояния, когда пользователь переключается на другое приложение или на главный экран. Приложения могут запросить дополнительное время для завершения задач в фоновом режиме.

5️⃣Приостановлено (Suspended)
Приложение находится в фоновом режиме, но не выполняет код. Операционная система может автоматически перевести приложение из фонового состояния в приостановленное для освобождения ресурсов. Приложение остается в памяти, но любые активные задачи, потоки или таймеры останавливаются.

Методы AppDelegate для управления жизненным циклом

Разработчики могут реагировать на изменения в жизненном цикле, используя методы делегата UIApplicationDelegate, такие как:

application(_:didFinishLaunchingWithOptions:) — вызывается, когда приложение завершает запуск.
applicationDidBecomeActive(_:) — вызывается, когда приложение становится активным.
applicationWillResignActive(_:) — вызывается, когда приложение переходит из активного состояния в неактивное.
applicationDidEnterBackground(_:) — вызывается, когда приложение переходит в фоновый режим.
applicationWillEnterForeground(_:) — вызывается перед переходом приложения из фонового режима в активный.
applicationWillTerminate(_:) — вызывается перед тем, как приложение будет завершено.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍2
Что такое "Опционалы" ?
Спросят с вероятностью 45%

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

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

Пример:
var optionalInt: Int? = nil


В этом случае переменная optionalInt объявлена как опциональная Int. Изначально она не содержит значения, что обозначается как nil.

Предлагается несколько способов работы с опционалами, включая:

1️⃣Принудительное развертывание: Использование восклицательного знака (!) для доступа к значению опционала. Этот метод опасен, так как может привести к ошибке времени выполнения, если опционал содержит nil.
let someValue: Int? = 5
let unwrappedValue: Int = someValue! // Принудительное развертывание


2️⃣Опциональное связывание: Позволяет безопасно проверить и извлечь значение опционала, если оно существует.
if let unwrappedValue = someValue {
print(unwrappedValue) // Безопасное использование unwrappedValue
}


3️⃣Оператор объединения с nil (nil-coalescing operator): Позволяет предоставить значение по умолчанию для опционала в случае, если в нем содержится nil.
let defaultValue = someValue ?? 0 // Использует 0, если someValue содержит nil


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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍3
В чем разница между MVC и MVVM ?
Спросят с вероятностью 27%

MVC (Model-View-Controller) и MVVM (Model-View-ViewModel) — это два популярных архитектурных паттерна, используемых в разработке ПО для организации кода и разделения ответственности между компонентами системы. Оба паттерна направлены на упрощение разработки и поддержки приложений, но они делают это по-разному.

MVC (Model-View-Controller)

Компоненты:
Model: Отвечает за данные и бизнес-логику приложения. Модель не знает о контроллерах и видах, обращаясь к ним через делегаты или нотификации.
View: Отображает данные (модель) пользователю и отправляет действия пользователя контроллеру. View не содержит бизнес-логику обработки данных.
Controller: Служит посредником между моделью и видом, обрабатывая пользовательский ввод и обновляя модель или вид.

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

MVVM (Model-View-ViewModel)

Компоненты:
Model: Содержит данные и бизнес-логику.
View: Отображает визуальные элементы и генерирует события пользовательского ввода. View также может непосредственно обращаться к ViewModel через привязку данных (data binding).
ViewModel: Содержит логику представления, которая преобразует данные модели в значения, удобные для отображения. ViewModel реагирует на команды View, выполняет необходимую бизнес-логику и обновляет модель, избавляя View от необходимости содержать логику, отличную от логики отображения.

Особенности:
Слабая связь между View и ViewModel, что упрощает тестирование и поддержку.
Использование привязки данных сокращает объём шаблонного кода для обновления интерфейса.

Основные различия

Связность: В MVVM меньше связности между отображением и логикой представления, благодаря использованию ViewModel и механизмов привязки данных. В MVC виды и контроллеры тесно связаны.
Разделение ответственности: MVVM позволяет лучше разделить логику отображения от бизнес-логики, переместив большую часть логики отображения из View в ViewModel. В MVC контроллеры могут стать перегруженными, поскольку в них содержится и логика управления данными, и логика управления представлениями.
Тестирование: MVVM облегчает тестирование логики представления благодаря слабой связности и отделению ViewModel от View. В MVC тестирование может быть усложнено из-за сильной связности между контроллерами и видами.

Выбор между MVC и MVVM зависит от конкретного проекта, предпочтений команды и требований к архитектуре приложения. MVVM часто предпочтителен для приложений с сложным пользовательским интерфейсом и динамическими данными из-за его гибкости и упрощения тестирования.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
2
Что такое Retain Cycle ?
Спросят с вероятностью 27%

Цикл удержания (Retain Cycle) — это проблема управления памятью, которая возникает в языках программирования с автоматическим подсчётом ссылок (например, в Swift и Objective-C), когда два объекта хранят сильные (strong) ссылки друг на друга. Это приводит к тому, что объекты не освобождаются автоматически после того, как становятся ненужными, потому что счетчик ссылок на каждый из них остаётся больше нуля. В результате память, которую занимают эти объекты, утекает, что может привести к избыточному расходу памяти и в конечном итоге к снижению производительности или даже к аварийному завершению работы приложения.

Как оно возникает

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

Как избежать этого

1️⃣Использование слабых (weak) или некрепких (unowned) ссылок: Одним из решений является использование слабых или некрепких ссылок для одного из взаимосвязанных объектов.weak и unowned ссылки не увеличивают счетчик ссылок объекта, на который они указывают, что предотвращает возникновение цикла удержания.

Слабые ссылки (`weak`): Используются, когда ссылка может быть nil. Swift автоматически обнуляет слабую ссылку, когда объект, на который она указывает, освобождается.

Некрепкие ссылки (`unowned`): Используются, когда гарантировано, что ссылка всегда будет указывать на объект, и вы хотите избежать цикла удержания. В отличие от слабой ссылки, некрепкая ссылка не обнуляется автоматически и может привести к ошибке времени выполнения, если объект будет освобождён.

2️⃣Изменение архитектуры приложения: Иногда стоит пересмотреть взаимодействие между объектами в вашем приложении, чтобы избежать необходимости создания взаимосильных ссылок.

Пример:
class Parent {
var child: Child?
}

class Child {
weak var parent: Parent?
}

var parent: Parent? = Parent()
var child: Child? = Child()

parent?.child = child
child?.parent = parent

// Разрыв ссылок
parent = nil
child = nil


В этом примере parent имеет сильную ссылку на child, но child имеет слабую ссылку на parent. Это предотвращает возникновение цикла удержания, и объекты могут быть корректно освобождены из памяти.

Цикл удержания — это ситуация, при которой два объекта ссылаются друг на друга сильными ссылками, предотвращая их освобождение и вызывая утечку памяти. Использование слабых (weak) или некрепких (unowned) ссылок помогает избежать этих циклов и обеспечивает корректное управление памятью в приложениях на Swift и Objective-C.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍4🔥1
Что такое многопоточность ?
Спросят с вероятностью 45%

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

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

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

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

Важным аспектом многопоточного программирования является синхронизация доступа к общим ресурсам, чтобы предотвратить "гонки данных" и другие виды конфликтов. Для этого используются различные механизмы синхронизации, такие как блокировки (locks), семафоры (semaphores) и барьеры (barriers).

Предлагается несколько инструментов для многопоточного программирования, в том числе Grand Central Dispatch (GCD) и Operation Queues, которые упрощают выполнение асинхронных операций и управление потоками.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍3
Как устроен responder chain ?
Спросят с вероятностью 27%

Responder chain (цепочка обработчиков событий) — это концепция, используемая для обработки событий, таких как касания, жесты, нажатия клавиш и другие пользовательские действия. Она представляет собой последовательность объектов (responders), которые могут реагировать на эти события. Цепочка обработчиков позволяет событию быть переданным от одного объекта к другому, пока не будет найден подходящий обработчик события или пока цепочка не будет завершена.

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

1️⃣Начало цепочки: В контексте iOS приложения, цепочка обработчиков событий начинается с объекта, который первым получает событие. Например, в случае касания экрана это может быть конкретный элемент управления UI (например, кнопка), который распознает касание.

2️⃣Передача события: Если этот объект (responder) не обрабатывает событие, оно передается дальше по цепочке. Обычно следующим в цепочке является родительский элемент в иерархии представлений, и так далее, вплоть до корневого представления.

3️⃣ViewController и дальше: Если событие достигает корневого представления и оно не обрабатывается, оно передается объекту ViewController этого представления. Затем ViewController может обработать событие или передать его дальше по цепочке.

4️⃣UIApplication и AppDelegate: Если ни один из ViewControllers не обработал событие, оно может быть передано объекту приложения (UIApplication) и, в конечном счёте, его делегату (AppDelegate), где оно может быть обработано или проигнорировано.

Пример

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

Зачем это нужно

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

Responder chain — это система для передачи и обработки событий, которая обеспечивает последовательную передачу событий от одного объекта к другому в пределах иерархии приложения, пока событие не будет обработано или не достигнет конца цепочки. Это ключевой механизм для обработки пользовательского ввода и других событий в приложениях для платформ Apple.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🔥4
Что такое жизненный цикл объекта ?
Спросят с вероятностью 45%

Жизненный цикл объекта — это период с момента его создания до момента уничтожения. Управление жизненным циклом объекта важно для эффективного использования памяти и предотвращения утечек памяти, особенно в языках программирования, где управление памятью выполняется автоматически через механизм подсчёта ссылок (reference counting).

Создание объекта

1️⃣Инициализация (Initialization): Объект создаётся в памяти. Это происходит при вызове инициализатора, который устанавливает начальное состояние всех свойств объекта. Инициализаторы могут быть определены с различными параметрами, позволяя создавать объекты с разными начальными значениями.

Использование объекта

2️⃣Доступ и изменение состояния: После создания объекта его методы могут быть вызваны, а свойства — читаться или изменяться. Это активный период в жизненном цикле объекта, когда он выполняет полезную работу.

Уничтожение объекта

3️⃣Деинициализация (Deinitialization): Непосредственно перед тем, как объект будет уничтожен, вызывается деинициализатор (если он определён). Это дает возможность освободить ресурсы или выполнить другие операции по очистке. Деинициализаторы не принимают никаких параметров и указываются с помощью ключевого слова deinit.

4️⃣Освобождение памяти (Deallocation): Когда на объект больше нет активных ссылок (когда счётчик ссылок достигает нуля), память, занимаемая объектом, освобождается. Это означает, что объект удаляется из памяти, и ресурсы, которые он использовал, становятся доступными для других объектов или программ.

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

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍21
Как решить проблему race condition ?
Спросят с вероятностью 36%

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

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

1️⃣Использование блокировок (Locks)

Блокировки позволяют "заблокировать" данные, чтобы только один поток мог их изменять в данный момент времени. В Swift для этого можно использовать NSLock, pthread_mutex_t в более низкоуровневом коде или даже DispatchSemaphore с семафором, установленным в 1, для управления доступом к ресурсу.
let lock = NSLock()

func threadSafeMethod() {
lock.lock()
// безопасный доступ к данным
lock.unlock()
}


2️⃣Сериализация операций с помощью очередей

Система Grand Central Dispatch (GCD) в Swift позволяет создавать сериализованные (последовательные) очереди, гарантируя, что операции в очереди выполняются одна за другой, что исключает возможность "race condition".
let queue = DispatchQueue(label: "com.example.myQueue")

func threadSafeMethod() {
queue.async {
// безопасный доступ к данным
}
}


3️⃣Использование атомарных операций

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

4️⃣Использование транзакций

В некоторых системах, например, в базах данных, можно использовать транзакции для управления доступом к данным. Транзакции гарантируют, что серия операций либо выполняется полностью, либо не выполняется вовсе, что может помочь предотвратить "race condition" при одновременном доступе к данным.

5️⃣Избегание общего состояния

Ещё одним подходом является стараться избегать общего состояния между потоками там, где это возможно, и использовать локальное состояние, передаваемое между потоками, или неизменяемые (immutable) данные, которые безопасно читать из нескольких потоков одновременно.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍3
Что такое dispatch группа ?
Спросят с вероятностью 18%

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

Как они работают

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

Вот пример того, как можно использовать dispatch группы с помощью GCD:
let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
DispatchQueue.global().async {
// Задача 1
print("Задача 1 началась")
// Представим, что тут какая-то асинхронная операция
sleep(2) // Имитация длительной операции
print("Задача 1 завершена")
dispatchGroup.leave()
}

dispatchGroup.enter()
DispatchQueue.global().async {
// Задача 2
print("Задача 2 началась")
sleep(1) // Имитация длительной операции
print("Задача 2 завершена")
dispatchGroup.leave()
}

dispatchGroup.notify(queue: DispatchQueue.main) {
// Этот блок кода будет выполнен, когда все задачи в группе завершены
print("Все задачи в группе выполнены")
}


Основные Моменты

`dispatchGroup.enter()`: Этот метод увеличивает счётчик задач в группе и сообщает системе, что началась новая задача, которая должна быть учтена.
`dispatchGroup.leave()`: Этот метод уменьшает счётчик задач в группе и сообщает системе, что задача завершена.
`dispatchGroup.notify(queue:completion:)`: Этот метод позволяет задать блок кода, который будет выполнен после завершения всех задач в группе. Вы указываете очередь, в которой должен выполняться этот блок, что позволяет гарантировать, что все последующие действия будут выполнены в нужном контексте (например, в основной очереди для обновления пользовательского интерфейса).

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍41
Что такое RC ?
Спросят с вероятностью 36%

RC, или подсчёт ссылок (Reference Counting) — это метод управления памятью, используемый в программировании для автоматического освобождения неиспользуемой памяти. Суть метода заключается в отслеживании количества ссылок на объект в памяти; когда число этих ссылок достигает нуля, объект считается больше не нужным, и его память может быть освобождена.

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

Как работает ARC:

Увеличение счётчика ссылок: Когда вы создаёте ссылку на объект, ARC автоматически увеличивает количество ссылок на этот объект. Это происходит, например, когда вы присваиваете объект переменной или добавляете его в коллекцию.
Уменьшение счётчика ссылок: Когда ссылка на объект удаляется или заменяется, ARC уменьшает счётчик ссылок. Если счётчик достигает нуля, ARC автоматически освобождает память, занимаемую объектом.

ARC умно управляет всеми ссылками в рамках приложения, включая сильные, слабые (weak) и несильные (unowned) ссылки, чтобы минимизировать риск утечек памяти. Однако разработчикам всё ещё нужно быть осторожными с циклическими ссылками, когда два объекта ссылаются друг на друга сильными ссылками, что может помешать ARC корректно освободить память. В таких случаях рекомендуется использовать слабые или несильные ссылки для разрыва цикла.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍2
Что такое Stack и куча ?
Спросят с вероятностью 36%

Stack (стек) и куча (heap) — это две области памяти, используемые в компьютерных программах для хранения переменных и объектов, но они управляются по-разному и служат разным целям.

Стек

Стек — это область памяти, где хранятся локальные переменные и информация для управления вызовами функций и методов. Данные в стеке организованы в виде стека (LIFO — последний пришёл, первый ушёл), что означает, что последний добавленный блок данных будет первым удалённым. Стек характеризуется тем, что работает очень быстро и автоматически управляет выделением и освобождением памяти. Однако он имеет ограниченный размер, что ограничивает объём данных, которые могут быть в нём хранены.

Пример использования стека — вызов функций: когда функция вызывается, запись о ней (включая параметры и локальные переменные) помещается в стек. Когда функция завершает работу, её запись удаляется из стека.

Куча

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

Работа с кучей медленнее, чем со стеком, поскольку требует явного управления памятью: программист должен явно запрашивать выделение памяти при создании нового объекта и освобождать её, когда объект больше не нужен. Это увеличивает риск утечек памяти и фрагментации.

Ключевые отличия

Управление памятью: В стеке управление памятью осуществляется автоматически, а в куче — вручную или с помощью сборщика мусора в языках программирования, которые его поддерживают.
Производительность: Доступ к стеку быстрее из-за его LIFO-структуры и автоматического управления памятью, в то время как куча предоставляет большую гибкость за счёт производительности.
Размер: Стек обычно имеет ограниченный размер, заданный при запуске программы, в то время как куча может расширяться в зависимости от доступной системной памяти.
Хранение данных: Стек используется для хранения локальных переменных и управления вызовами функций, куча — для динамически выделяемых объектов.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
3
В чем разница между синхронными и асинхронными запросами ?
Спросят с вероятностью 36%

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

Синхронные запросы

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

Пример:
let data = try? Data(contentsOf: url)
// Следующая строка кода не выполнится, пока данные не будут загружены

Асинхронные запросы

При асинхронном выполнении программа инициирует запрос и сразу же продолжает выполнение следующих операций, не дожидаясь его завершения. Завершение асинхронного запроса обрабатывается с помощью колбэков, промисов, фьючерсов, async/await или других механизмов, позволяющих уведомить программу о завершении операции. Таким образом, асинхронное выполнение позволяет избежать блокировки пользовательского интерфейса и делает приложение более отзывчивым.

Пример:
func fetchData() async {
let data = try? await Data(contentsOf: url)
// Этот код выполнится сразу после завершения загрузки данных, но не заблокирует остальную программу
}


Ключевые отличия

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

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍41
Что такое Dependency Injection ?
Спросят с вероятностью 36%

Dependency Injection (DI) — это техника в программировании, используемая для управления зависимостями между объектами. Это один из методов инверсии управления (Inversion of Control, IoC), который помогает сделать программный код более модульным и упрощает тестирование. Его суть заключается в том, что вместо того чтобы объект сам создавал или искал необходимые ему зависимости (другие объекты, от которых он зависит для выполнения своих функций), эти зависимости предоставляются ему извне.

Преимущества:

1️⃣Упрощение тестирования: Поскольку зависимости инжектируются извне, для тестирования можно легко подменить реальные зависимости моками или фейками.
2️⃣Улучшение модульности и гибкости: DI позволяет легко заменять компоненты системы без изменения кода, использующего эти компоненты.
3️⃣Уменьшение связности: Так как объекты не создают свои зависимости напрямую, связь между компонентами системы ослабевает, что упрощает управление кодом.

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

Можно реализовать разными способами, включая:

Конструктор (Constructor Injection): Зависимости передаются объекту через его конструктор.
Сеттер (Setter Injection): Зависимости передаются через сеттеры или другие методы после создания объекта.
Интерфейс (Interface Injection): Объект предоставляет интерфейс, через который в него можно инжектировать зависимости.

Пример:
Без использования DI, объект может сам создавать свою зависимость:
class Service {}

class Client {
var service: Service

init() {
self.service = Service()
}
}

С использованием Constructor Injection:
class Service {}

class Client {
var service: Service

init(service: Service) {
self.service = service
}
}

// Использование
let service = Service()
let client = Client(service: service)


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

Dependency Injection делает код более чистым, тестируемым и соответствующим принципам SOLID, особенно принципу инверсии зависимостей (Dependency Inversion Principle).

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🔥41
Ребят, напоминаю, что все вопросы, которые здесь публикуются можно посмотреть списком вместе с видео-ответами на моем сайте easyoffer.ru
4🤯1
Что лучше NSOperationQueue или GCD ?
Спросят с вероятностью 36%

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

Grand Central Dispatch (GCD)

Это низкоуровневый API для многопоточного программирования, основанный на очередях. Он позволяет выполнять задачи асинхронно или синхронно, управляя потоками за вас на основе доступности системных ресурсов. GCD оптимизирован для максимальной производительности и простоты использования.

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

NSOperationQueue

Это высокоуровневый API, построенный поверх GCD, который предоставляет больше контроля над операциями. Он работает с объектами NSOperation, которые представляют собой одну выполнимую задачу. NSOperation позволяет добавлять зависимости между операциями, приостанавливать и возобновлять выполнение очереди, а также наблюдать за завершением операции.

Преимущества:
Возможность добавления зависимостей между операциями.
Возможность отмены операций.
Поддержка KVO (Key-Value Observing) для наблюдения за изменениями свойств операций.
Больше контроля над выполнением операций по сравнению с GCD.

Что выбрать?

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

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

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍5
Чем архитектура помогает ?
Спросят с вероятностью 18%

Архитектура ПО играет критическую роль в процессе разработки, обеспечивая основу для создания систематизированных и управляемых IT-проектов. Вот основные аспекты, в которых архитектура оказывает существенное влияние:

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

2️⃣Упрощение сопровождения и развертывания
Чёткая и структурированная архитектура упрощает процессы обслуживания, тестирования и развертывания программного обеспечения. Модульная структура помогает локализовать ошибки и устранять их, не затрагивая работу всей системы. Также она позволяет независимо разворачивать отдельные компоненты.

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

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

5️⃣Облегчение межкомандной работы
Когда она чётко определена, команды разработчиков могут работать над разными частями системы независимо друг от друга. Это стандартизирует технические подходы и упрощает интеграцию разработанных модулей.

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

7️⃣Повышение надёжности и доступности
Разрабатывая архитектуру, можно предусмотреть механизмы для обеспечения высокой доступности и надёжности системы, включая резервирование, балансировку нагрузки и восстановление после сбоев.

8️⃣Удовлетворение бизнес-требований
Эффективная архитектура позволяет точнее соответствовать бизнес-требованиям и целям, обеспечивая гибкость в изменении функциональности в соответствии с изменяющимися условиями рынка или требованиями законодательства.

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

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍42
Чем отличается frame от bounce ?
Спросят с вероятностью 36%

Понятия frame и bounds относятся к свойствам представлений (views), но имеют разные значения и используются в разных целях.

Frame

Представляет собой прямоугольник, который определяет положение и размер представления относительно его родительского представления (superview). Координаты frame указываются в системе координат родительского представления. Это означает, что frame показывает, где именно находится представление в родительском представлении и какого оно размера.

Пример: Если frame представления A равен (10, 20, 100, 200), это означает, что верхний левый угол A находится на 10 пикселей вправо и 20 пикселей вниз относительно верхнего левого угла его родительского представления, а размер A составляет 100 пикселей в ширину и 200 пикселей в высоту.

Bounds

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

Пример: Если bounds представления B равен (0, 0, 300, 400), это означает, что видимая область содержимого B начинается с верхнего левого угла и имеет размер 300 пикселей в ширину и 400 пикселей в высоту. Если вы измените bounds так, чтобы они начинались, например, с (50, 50, 300, 400), это сдвинет видимую область содержимого так, что верхняя левая точка видимого содержимого будет находиться на 50 пикселей вправо и 50 пикселей вниз от исходной верхней левой точки содержимого B.

Основные отличия

Система координат: Frame использует систему координат родительского представления, в то время как bounds использует собственную систему координат представления.
Использование: Frame чаще всего используется для определения положения и размера представления относительно его родительского представления, а bounds — для работы с содержимым внутри представления, например, когда необходимо управлять прокруткой.

Понимание различий между frame и bounds важно для правильной работы с макетами интерфейса и управлением представлениями в iOS и macOS разработке.

👉 Можно посмотреть примеры как отвечают люди на этот вопрос, или перейти к списку 823 вопросов на IOS разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
👍2
👾 Ребят, напоминаю, у нас есть приватные группы где мы делимся реальными собеседованиями и тестовыми заданиями. Чтобы попасть в эти в группы воспользуйтесь ботами:
🤖 Доступ к базе собесов
🤖 Доступ к базе тестовых заданий