Swift | Вопросы собесов
2.14K subscribers
28 photos
934 links
Download Telegram
Когда использовать Set вместо Array ?
Спросят с вероятностью 18%

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

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

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

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

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

1️⃣Уникальность: Все элементы в множестве уникальны.
2️⃣Неупорядоченность: Элементы в множестве не хранятся в каком-либо определенном порядке.
3️⃣Эффективность операций: Операции добавления, удаления и проверки на вхождение обычно более эффективны для множеств по сравнению с массивами из-за использования хеш-таблиц.

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

Примеры:

Массив:
var visitors = ["Alice", "Bob", "Alice", "Joe"]
print(visitors[1]) // Выводит "Bob"
// Используется для учёта всех посещений, включая повторные


Множество:
var uniqueVisitors: Set = ["Alice", "Bob", "Alice", "Joe"]
print(uniqueVisitors) // Выводит ["Alice", "Bob", "Joe"]
// Используется для учёта уникальных посетителей


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

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

🔐 База собесов | 🔐 База тестовых
🔥9👍31
Как в Swift устроена инкапсуляция ?
Спросят с вероятностью 27%

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

Уровни доступа:

1️⃣Public: Члены класса доступны из любого кода в том же модуле и вне его. Используется для определения интерфейса публичного API.
2️⃣Open: Такой же уровень доступа, как и public, но в дополнение позволяет классам и членам класса быть наследованными и переопределенными вне модуля, в котором они объявлены.
3️⃣Internal: Члены класса доступны в любом месте кода в их собственном модуле или приложении, но недоступны за его пределами. Это уровень доступа по умолчанию.
4️⃣Fileprivate: Ограничивает доступ к членам класса в пределах того же файла исходного кода.
5️⃣Private: Самый строгий уровень доступа, ограничивающий область видимости членов класса контекстом, в котором они объявлены. Начиная с Swift 4, private также означает доступность в расширениях класса в том же файле.

Примеры:
class SomeClass {
private var privateVariable: Int = 0 // Доступна только внутри `SomeClass`
fileprivate func fileprivateMethod() {} // Доступен в любом месте внутри того же файла исходного кода
internal func internalMethod() {} // Доступен в любом месте внутри того же модуля (по умолчанию)
public var publicVariable: Int = 0 // Доступна из любого места в том же модуле и из внешних модулей
open func openMethod() {} // Может быть переопределен во внешних модулях
}

private class PrivateClass {} // Доступен только внутри текущего файла


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

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

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

Протокол Hashable позволяет объекту быть использованным в качестве ключа в словаре (Dictionary) или элемента в множестве (Set). Чтобы объект мог выполнять эту роль, он должен быть уникально идентифицируемым, чтобы каждый экземпляр объекта мог быть сравним с другими экземплярами и иметь уникальное хэш-значение. Это требование обеспечивается реализацией двух требований протокола: Equatable и hash(into:).

Equatable

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

hash(into:)

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

Пример:
struct Person: Hashable {
var name: String
var age: Int

static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name && lhs.age == rhs.age
}

func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(age)
}
}


В этом примере Person структура реализует Hashable, предоставляя реализации для == и hash(into:). Это позволяет использовать Person в качестве ключей в Dictionary или элементов в Set.

Данный протокол необходим для:

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

Hashable делает возможным эффективное хранение и поиск данных, используя хэш-таблицы, что является основой для Dictionary и Set.

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

🔐 База собесов | 🔐 База тестовых
👍81
Для чего нужен optional ?
Спросят с вероятностью 27%

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

Основные цели использования:

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

2️⃣Упрощение кода: Вместо того чтобы использовать специальные значения (например, -1 или 0 для числовых значений) для обозначения отсутствия реального значения, опционалы позволяют использовать nil. Это делает код чище и его намерения — более ясными.

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

Пример:
var maybeString: String? = "Hello"
print(maybeString) // Выведет "Optional("Hello")"

// Для безопасного извлечения значения из опционала можно использовать условное связывание
if let actualString = maybeString {
print(actualString) // Выведет "Hello"
} else {
print("maybeString не содержит значения")
}

// Или использовать оператор разворачивания опционала
print(maybeString!) // Выведет "Hello", но приведёт к ошибке выполнения, если maybeString == nil

// Использование оператора объединения с nil для предоставления значения по умолчанию
print(maybeString ?? "Значение отсутствует")


Механизмы работы:

Принудительное извлечение: Использование ! после имени опционала. Это рискованный подход, так как при попытке извлечения значения из nil произойдёт ошибка выполнения.
Опциональное связывание: Позволяет безопасно проверить и извлечь значение из опционала.
Опциональные цепочки: Позволяют запрашивать свойства, методы и индексы на опциональном значении и возвращают опционал.
Оператор объединения с nil (`??`): Позволяет задать значение по умолчанию, которое будет использовано в случае, если опционал содержит nil.

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

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

🔐 База собесов | 🔐 База тестовых
👍3
Зачем нужны свойства "Content Hugging Priority" ?
Спросят с вероятностью 18%
ID 7449

Свойства "Content Hugging Priority" и "Content Compression Resistance Priority" играют ключевую роль в системе Auto Layout. Эти свойства помогают определить, как вьюшки (views) должны быть отформатированы и как они реагируют на изменения в доступном пространстве в интерфейсе пользователя. Рассмотрим подробнее, что означает каждое из этих свойств и как они используются в разработке интерфейсов.

Content Hugging Priority

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

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

Если у вас есть метка (label) с текстом, которая должна расширяться или сжиматься в зависимости от количества текста, установка высокого Content Hugging Priority для этой метки гарантирует, что она будет расти и уменьшаться как можно точнее по размеру текста.

Content Compression Resistance Priority

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

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

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

Взаимодействие свойств

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

Понимание и правильное использование Content Hugging Priority и Content Compression Resistance Priority позволяют разработчикам создавать гибкие и адаптивные интерфейсы, которые хорошо выглядят на разных устройствах и при различных условиях содержимого.

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

🔐 База собесов | 🔐 База тестовых
👍5
🤔 Какая конструкция в Swift позволяет безопасно развернуть опционал?
Anonymous Quiz
67%
if let
27%
guard else
4%
unwrap
2%
force unwrap
👍2
Какие методы жизненного цикла у view controller'а известны ?
Спросят с вероятностью 18%

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

1️⃣loadView()
Вызывается, когда контроллеру вью нужно создать свою view. Обычно вы не должны вызывать этот метод напрямую или его переопределять, если только вы не загружаете view не из storyboard, а создаёте её программно.

2️⃣viewDidLoad()
Вызывается после загрузки view контроллера в память. Этот метод вызывается один раз за время существования контроллера и является местом для выполнения всех начальных настроек, создания визуальных элементов, которые не изменяются, и первоначальной конфигурации данных.

3️⃣viewWillAppear(_:)
Вызывается перед тем, как view будет добавлена к иерархии view. Используется для выполнения задач, связанных с появлением view, таких как обновление пользовательского интерфейса, запуск анимаций или мониторинг изменений в данных или состоянии.

4️⃣viewDidAppear(_:)
Вызывается после того, как view добавлена к иерархии view и представлена на экране. Это хорошее место для начала анимаций, загрузки данных, которые необходимо обновлять каждый раз при появлении view, или для начала таких операций, как отслеживание местоположения или внешние запросы данных, которые должны запускаться только когда view активна.

5️⃣viewWillDisappear(_:)
Вызывается перед тем, как view будет удалена из иерархии view. Это время для остановки анимаций, сохранения измененных данных и отмены любых задач, начатых при появлении view.

6️⃣viewDidDisappear(_:)
Вызывается после удаления view из иерархии view. Здесь можно освободить ресурсы, связанные с view, остановить службы, такие как таймеры, или отписаться от уведомлений, которые не нужны, когда view не видно.

7️⃣viewWillLayoutSubviews()
Вызывается перед тем, как контроллер вью будет располагать свои подвиды. Этот метод хорош для изменения размеров или изменения расположения подвидов вручную.

8️⃣viewDidLayoutSubviews()
Вызывается после того, как контроллер вью расположил свои подвиды. Это полезно, если вам нужно сделать дополнительные настройки после того, как система расположения завершила свою работу.

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

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

🔐 База собесов | 🔐 База тестовых
🔥73
Какие есть способы развертывания optional ?
Спросят с вероятностью 27%

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

1️⃣Принудительное развертывание (forced unwrapping)

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


2️⃣Опциональное связывание (optional binding)

Позволяет проверить, содержит ли опционал значение, и, если да, сразу же присвоить это значение переменной или константе. Это безопасный способ работы с опционалами.
if let number = optionalNumber {
print("У нас есть значение: \(number)")
} else {
print("Значение отсутствует")
}


3️⃣Опциональное связывание с guard

Так же, как и предыдущий метод, но позволяет рано выйти из блока кода, если значение отсутствует. Это удобно для уменьшения вложенности и улучшения читаемости кода.
guard let number = optionalNumber else {
print("Значение отсутствует")
return
}
print("У нас есть значение: \(number)")


4️⃣Оператор объединения (nil coalescing operator)

Позволяет задать значение по умолчанию, которое будет использоваться в случае, если опционал равен nil.
let number = optionalNumber ?? 0 // Возвращает 0, если optionalNumber равен nil


5️⃣Опциональные цепочки (optional chaining)

Позволяют запрашивать свойства, методы и индексы на опционале в манере, которая не вызовет ошибки, если опционал равен nil. Вместо этого выражение вернет nil.
let count: Int? = optionalString?.count


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

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

🔐 База собесов | 🔐 База тестовых
👍17
Что такое автоматический подсчет ссылок ?
Спросят с вероятностью 18%

Автоматический подсчёт ссылок (Automatic Reference Counting, ARC) — это технология управления памятью, которая автоматически отслеживает и управляет памятью объектов в языках программирования, таких как Objective-C и Swift. ARC используется для облегчения задачи разработчиков, автоматизируя процесс управления памятью, который ранее требовал явного выделения и освобождения памяти.

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

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

Когда счетчик ссылок объекта достигает нуля, это означает, что на объект больше нет активных ссылок, и память, занимаемая объектом, может быть освобождена. ARC тогда автоматически вызывает деструктор объекта (если он есть) и освобождает память.

Преимущества:
Уменьшение утечек памяти: Значительно уменьшает риск утечек памяти, так как автоматически освобождает объекты, которые больше не используются.
Простота использования: Поскольку он автоматизирует управление памятью, разработчикам не нужно явно вызывать методы для выделения и освобождения памяти, что упрощает написание кода и уменьшает вероятность ошибок.
Безопасность и чистота кода: Управление памятью с ARC более безопасно, чем традиционное ручное управление памятью, и способствует написанию более чистого и легко поддерживаемого кода.

Недостатки:
Циклические ссылки: Одна из основных проблем ARC — это управление циклическими ссылками, когда два объекта взаимно ссылаются друг на друга, что приводит к тому, что счетчики ссылок никогда не достигают нуля. Разработчики должны явно управлять такими ситуациями с помощью слабых (weak) и неуправляемых (unowned) ссылок.
Контроль над временем жизни объекта: В некоторых случаях разработчикам может потребоваться более тонкий контроль над временем жизни объекта, чем тот, который предоставляет ARC.

Использование ссылок

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

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

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

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

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

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

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

Пример использования MemoryLayout в Swift:
struct ExampleStruct {
var a: Int
var b: Bool
}

print("Size of ExampleStruct: \(MemoryLayout<ExampleStruct>.size)")
print("Stride of ExampleStruct: \(MemoryLayout<ExampleStruct>.stride)")
print("Alignment of ExampleStruct: \(MemoryLayout<ExampleStruct>.alignment)")


Размер протокола

Может быть запутанным, так как протоколы сами по себе не занимают пространство; они определяют интерфейс, которому должны соответствовать типы данных. Протоколы в Swift не имеют прямого размера в памяти, так как они не являются конкретными данными, а скорее определяют "шаблон", который должны следовать объекты или структуры.

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

Пример определения размера типа, соответствующего протоколу:
protocol SomeProtocol {
var num: Int { get set }
}

struct SomeStruct: SomeProtocol {
var num: Int
}

print("Size of SomeStruct: \(MemoryLayout<SomeStruct>.size)")


В этом примере, размер SomeStruct, которая соответствует SomeProtocol, будет равен размеру его единственного свойства num типа Int.

Размеры типов данных, соответствующих протоколу, зависят от их конкретной реализации, а сам протокол не имеет непосредственного "размера" в традиционном понимании.

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

🔐 База собесов | 🔐 База тестовых
🔥41
🤔 Какой модификатор доступа в Swift является наиболее ограничивающим?
Anonymous Quiz
1%
public
14%
internal
84%
fileprivate
1%
open
Что может быть ключом и значением для dictionary ?
Спросят с вероятностью 27%

Словарь (Dictionary) представляет собой коллекцию пар ключ-значение, где каждый ключ должен быть уникальным. Чтобы использовать какой-либо тип в качестве ключа словаря, этот тип должен соответствовать протоколу Hashable. Это требование обусловлено тем, что Swift использует хеш-таблицу для хранения элементов словаря, что обеспечивает быстрый доступ к его элементам.

Ключи

Должны быть уникальными: Каждый ключ в словаре должен быть уникальным. При попытке добавить в словарь элемент с ключом, который уже существует в словаре, старое значение будет заменено на новое.
Должны соответствовать протоколу `Hashable`: Это означает, что тип ключа должен иметь способность быть правильно хешированным. Большинство базовых типов Swift (например, String, Int, Double и др.) уже соответствуют Hashable, поэтому их можно использовать в качестве ключей без дополнительных усилий.

Значения

Могут быть любого типа: Значения в словаре могут быть любого типа, и они не обязаны соответствовать протоколу Hashable.
Могут повторяться: Разные ключи могут иметь одинаковые значения.

Пример:
var personAge: [String: Int] = ["John": 30, "Sara": 25]


В этом примере ключами являются строки (String), представляющие имена людей, а значениями — целые числа (Int), представляющие их возраст. Тип String соответствует Hashable, что делает его подходящим в качестве ключа для Dictionary.

Собственные типы в качестве ключей

Вы также можете использовать собственные пользовательские типы в качестве ключей словаря, но для этого ваш тип должен соответствовать протоколу Hashable. Это включает в себя реализацию требуемых методов для сравнения на равенство (==) и хеширования (hash(into:)).
struct Person: Hashable {
var name: String
var id: Int
}

var peopleDictionary: [Person: String] = [Person(name: "John", id: 1): "Engineer"]


Здесь Person — это пользовательский тип, соответствующий Hashable, что позволяет использовать его в качестве ключа. Значения в этом словаре — строки, описывающие профессию человека.

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

🔐 База собесов | 🔐 База тестовых
1
🤔 Какой метод для работы с массивами не существует в Swift?
Anonymous Quiz
4%
map()
2%
filter()
9%
reduce()
86%
collect()
Что такое дженерики ?
Спросят с вероятностью 27%

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

Ключевые аспекты:

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

Функции
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

var int1 = 1
var int2 = 2
swapTwoValues(&int1, &int2) // int1 становится 2, int2 становится 1

var string1 = "Hello"
var string2 = "World"
swapTwoValues(&string1, &string2) // string1 становится "World", string2 становится "Hello"


Дженерик типы

Могут хранить элементы любого типа.
var numbers: Array<Int> = [1, 2, 3]
var strings: Dictionary<String, String> = ["Key": "Value"]


Дженерик протоколы

Также можно использовать с протоколами с помощью ассоциированных типов.
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}


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

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

🔐 База собесов | 🔐 База тестовых
👍51
🤔 Какое ключевое слово используется для объявления констант в Swift?
Anonymous Quiz
2%
var
94%
let
2%
const
2%
static
Что используется для многопоточности ?
Спросят с вероятностью 27%

В разработке под платформы Apple, такие как iOS, macOS, watchOS и tvOS, для реализации многопоточности и асинхронного выполнения задач чаще всего используются следующие технологии и фреймворки:

1️⃣Grand Central Dispatch (GCD)

Это низкоуровневый API для управления параллельными задачами, основанный на очередях. GCD позволяет легко выполнять задачи асинхронно на фоновых потоках, не заботясь о создании и управлении потоками напрямую.
DispatchQueue.global(qos: .background).async {
// Выполнить фоновую задачу
DispatchQueue.main.async {
// Вернуться в главный поток для обновления UI
}
}


2️⃣Operation и OperationQueue

Представляют собой более высокоуровневую абстракцию над GCD, позволяя группировать связанные задачи и управлять их выполнением. Operation добавляет возможности, такие как установление приоритетов задач, управление зависимостями между задачами и отмена задач.
let operationQueue = OperationQueue()

let operation1 = BlockOperation {
// Задача 1
}

let operation2 = BlockOperation {
// Задача 2
}

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


3️⃣Swift Concurrency (Async/Await)

Представляет собой современный механизм для работы с асинхронным кодом с использованием async/await, который делает асинхронный код более читаемым и легким для понимания, сокращая необходимость во вложенных колбэках.
func fetchData() async -> Data? {
let url = URL(string: "https://example.com")!
do {
let (data, _) = try await URLSession.shared.data(from: url)
return data
} catch {
return nil
}
}

Task {
let data = await fetchData()
// Обновить UI
}


4️⃣Threads

Хотя прямое управление потоками обычно не рекомендуется из-за сложности и высокого риска ошибок, Swift и Objective-C позволяют работать с потоками напрямую через класс Thread.
let thread = Thread {
// Код для выполнения в новом потоке
}
thread.start()


Выбор между GCD, Operation/OperationQueue и Swift Concurrency зависит от конкретных требований приложения и предпочтений. GCD подходит для простых асинхронных задач и параллельного выполнения кода. Operation и OperationQueue предлагают больше контроля и гибкости для сложных задач с зависимостями. Swift Concurrency предоставляет современный и безопасный способ работы с асинхронным кодом, делая его более читабельным и легким в обслуживании.

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

🔐 База собесов | 🔐 База тестовых
👍51
В чем разница между garbage collector и ARC ?
Спросят с вероятностью 18%

Управление памятью в программировании — это ключевой аспект, влияющий на производительность, надежность и эффективность приложений. Два популярных подхода к управлению памятью — это сборка мусора (Garbage Collection, GC) и автоматическое подсчетное управление ссылками (Automatic Reference Counting, ARC). Эти методы имеют различные принципы работы и используются в разных языках программирования.

Garbage Collection (GC)

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

Как работает:
GC ведет себя как фоновый процесс, который сканирует кучу (heap) для поиска "недостижимых" объектов.
Основные стратегии GC включают маркировку и очистку, подсчет ссылок и сжатие.
Примеры языков, использующих GC: Java, C#, Python и JavaScript.

Преимущества:
Уменьшает вероятность утечек памяти.
Разработчикам не нужно вручную управлять освобождением памяти.

Недостатки:
Непредсказуемость времени запуска GC может привести к задержкам в выполнении программы (паузы для сборки мусора).
Потребление ресурсов CPU для работы GC.

Automatic Reference Counting (ARC)

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

Как работает:
Каждый объект имеет счетчик ссылок, который отслеживает, сколько раз на объект ссылаются.
Когда счетчик достигает нуля (на объект больше нет ссылок), объект автоматически уничтожается и память освобождается.

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

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

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

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

🔐 База собесов | 🔐 База тестовых
2
🤔 Какой механизм в Swift позволяет классам передавать или модифицировать поведение и свойства других классов?
Anonymous Quiz
74%
Наследование
9%
Протоколы
14%
Расширения
3%
Замыкания
Какие есть известные проблемы с многопоточностью ?
Спросят с вероятностью 64%

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

1️⃣Состояние гонки (Race Conditions): Это происходит, когда два или более потока пытаются одновременно изменить общие данные. Результат выполнения таких операций может зависеть от того, в каком порядке выполняются потоки, что может привести к непредсказуемому поведению программы.
var sharedResource = [String]()

DispatchQueue.global().async {
for _ in 0..<1000 {
sharedResource.append("A")
}
}

DispatchQueue.global().async {
for _ in 0..<1000 {
sharedResource.append("B")
}
}
// Предполагаемый результат может быть непредсказуемым, так как оба потока работают с одним и тем же ресурсом одновременно.


2️⃣Взаимная блокировка (Deadlocks): Взаимная блокировка может произойти, когда два или более потока блокируются, ожидая, пока другие потоки освободят ресурсы, которые они уже удерживают, в результате чего ни один из потоков не может продолжить выполнение.
let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")

queue1.async {
queue2.sync {
// Делаем что-то
}
}

queue2.async {
queue1.sync {
// Делаем что-то
}
}
// Здесь потоки будут ждать друг друга бесконечно, создавая взаимную блокировку.


3️⃣Условия гонки при работе с памятью (Memory Races): Похоже на условия гонки, но здесь конфликт возникает при доступе к памяти. Это может привести к повреждению данных, когда несколько потоков пытаются одновременно читать и записывать данные в одно и то же место в памяти без должной синхронизации.

4️⃣Голодание (Starvation): Происходит, когда поток никогда не получает доступ к ресурсу или исполнителю из-за постоянного захвата этих ресурсов другими потоками.

5️⃣Чрезмерная синхронизация (Over-Synchronization): Происходит, когда для предотвращения проблем с многопоточностью добавляется слишком много блокировок, что в свою очередь может серьёзно снизить производительность программы, так как потоки часто ожидают возможности доступа к ресурсам.

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

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

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

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

Основные характеристики:
1️⃣Фиксированный размер: Во многих языках программирования, таких как Java и C, размер массива устанавливается при его создании и не может быть изменен после этого. Это означает, что вы должны знать максимальное количество элементов, которое вам понадобится, заранее.
2️⃣Однородность типов данных: Массивы обычно хранят элементы одного типа. Например, массив целых чисел может хранить только целые числа.
3️⃣Прямой доступ к элементам: Массивы позволяют получать доступ к любому элементу по его индексу за константное время, что делает их идеальными для быстрого доступа к данным.

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

1️⃣Динамический размер: Многие коллекции, такие как ArrayList в Java или list в Python, могут динамически изменять свой размер в ответ на добавление или удаление элементов. Это позволяет использовать столько памяти, сколько необходимо, и избегать заранее заданного ограничения размера.
2️⃣Хранение разнотипных объектов: Некоторые типы коллекций могут хранить элементы различных типов, хотя это и не всегда считается лучшей практикой из-за потери типобезопасности. В динамически типизированных языках, таких как Python, это особенно распространено.
3️⃣Оптимизация под конкретные операции: Например, LinkedList обеспечивает более быструю вставку и удаление элементов по сравнению с массивами, если вам нужно часто вставлять или удалять элементы не только в конце коллекции. С другой стороны, стеки и очереди предоставляют методы для работы с элементами в порядке LIFO или FIFO соответственно.
4️⃣Функциональные возможности: Коллекции в высокоуровневых языках обычно сопровождаются богатым набором встроенных методов для обработки элементов, таких как поиск, сортировка, фильтрация и другие.

Примеры:

Java:
Массивы: int[] numbers = new int[10];
Коллекции: ArrayList<String> strings = new ArrayList<>();

Python:
Массивы (в смысле фиксированных списков): numbers = [1, 2, 3, 4, 5]
Коллекции: strings = ["apple", "banana", "cherry"]

C#:
Массивы: int[] numbers = new int[5];
Коллекции: `List
<string> strings = new List<string>();`

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

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

🔐 База собесов | 🔐 База тестовых
👍1