Swift | Вопросы собесов
2.15K subscribers
29 photos
960 links
Download Telegram
🤔 Какие паттерны знаешь?

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

🚩Порождающие паттерны (Creational)

Помогают упростить создание объектов и сделать его гибким.
Определяет интерфейс для создания объекта, но поручает подклассам выбрать его тип.
protocol Button {
func press()
}

class iOSButton: Button {
func press() { print("iOS button pressed") }
}

class AndroidButton: Button {
func press() { print("Android button pressed") }
}

class ButtonFactory {
static func createButton(for os: String) -> Button {
return os == "iOS" ? iOSButton() : AndroidButton()
}
}

let button = ButtonFactory.createButton(for: "iOS")
button.press() // "iOS button pressed"


🟠Одиночка (Singleton)
Гарантирует, что у класса будет только один экземпляр.
class Database {
static let shared = Database() // Единственный экземпляр
private init() { }

func query() { print("Запрос в базу данных") }
}

Database.shared.query()


🟠Строитель (Builder)
Позволяет пошагово создавать сложные объекты.
class Burger {
var cheese = false
var bacon = false
}

class BurgerBuilder {
private var burger = Burger()

func addCheese() -> Self {
burger.cheese = true
return self
}

func addBacon() -> Self {
burger.bacon = true
return self
}

func build() -> Burger {
return burger
}
}

let myBurger = BurgerBuilder().addCheese().addBacon().build()
print(myBurger.cheese) // true
print(myBurger.bacon) // true


🚩Структурные паттерны (Structural)

Определяют удобные способы связи между объектами.

🟠Адаптер (Adapter)
Позволяет совместить несовместимые интерфейсы.
protocol EuropeanSocket {
func provide220V()
}

class EuropeanPlug: EuropeanSocket {
func provide220V() { print("220V подано") }
}

class USPlug {
func provide110V() { print("110V подано") }
}

// Адаптер для американской вилки
class USAdapter: EuropeanSocket {
private let usPlug: USPlug

init(usPlug: USPlug) { self.usPlug = usPlug }

func provide220V() {
usPlug.provide110V()
print("Адаптация до 220V")
}
}

let adapter = USAdapter(usPlug: USPlug())
adapter.provide220V()
// "110V подано"
// "Адаптация до 220V"


🟠Декоратор (Decorator)
Динамически добавляет объекту новое поведение.
protocol Coffee {
func cost() -> Int
}

class SimpleCoffee: Coffee {
func cost() -> Int { return 100 }
}

// Декоратор "Молоко"
class MilkDecorator: Coffee {
private let coffee: Coffee

init(_ coffee: Coffee) { self.coffee = coffee }

func cost() -> Int {
return coffee.cost() + 30
}
}

let coffee = MilkDecorator(SimpleCoffee())
print(coffee.cost()) // 130


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

- Не создаёт сильной ссылки, не удерживает объект в памяти.
- Избегает retain cycle, как и weak.
- В отличие от weak, не является опциональным, но при обращении к уничтоженному объекту приводит к крашу — требует уверенности, что объект жив.


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

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

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

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

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

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

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

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


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

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

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


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

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


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

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

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

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

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

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


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

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

class ViewModel {
private let networkService: NetworkServiceProtocol

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

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

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


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

fallthrough используется в switch, чтобы намеренно перейти к следующему case, даже если тот не подходит по условию.
По умолчанию в Swift после выполнения одного case switch завершается, и fallthrough — это явное указание продолжить дальше.


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

В iOS для выполнения фоновых задач существуют несколько ключевых механизмов:

🚩Основные механизмы

🟠Grand Central Dispatch (GCD): Используется для асинхронного выполнения задач на глобальных или пользовательских очередях.
DispatchQueue.global(qos: .background).async {
// Фоновая задача
}


🟠OperationQueue: Высокоуровневый API для управления очередями операций с возможностью указания зависимостей.
let queue = OperationQueue()
queue.addOperation {
// Фоновая операция
}


🟠Background Fetch: Позволяет приложению периодически загружать новые данные в фоновом режиме.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Фоновое обновление данных
completionHandler(.newData)
}


🟠BGTaskScheduler: Новый фреймворк для планирования и выполнения фоновых задач.
import BackgroundTasks

func scheduleBackgroundTask() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
try? BGTaskScheduler.shared.submit(request)
}


🟠URLSession Background Transfers: Выполнение загрузки и выгрузки данных в фоновом режиме.
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let session = URLSession(configuration: configuration)
let url = URL(string: "https://example.com/largefile")!
let task = session.downloadTask(with: url)
task.resume()


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

Опциональные функции (
@objc optional) нарушают принцип подстановки Лисков (LSP), потому что не гарантируют выполнение всех требований контракта протокола. Это ослабляет надёжность системы типов.

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

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

🚩Content Hugging Priority

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

🚩Content Compression Resistance Priority

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

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

iOS использует ARC (Automatic Reference Counting) — механизм, который автоматически отслеживает количество ссылок на объект. Когда счётчик становится равным нулю, объект удаляется. ARC работает на этапе компиляции и вставляет retain/release/assign автоматически.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Какие ситуации могут быть с user antaraction enabled false, когда vue не может обработать тач?

Если isUserInteractionEnabled = false в iOS-приложении (например, в UIView), это может повлиять на обработку тач-жестов и событий. Если ваш фронтенд на Vue.js работает внутри WebView в iOS-приложении, то Vue может не получать события касаний (touch и click).

🚩Почему это может происходить?

🟠`isUserInteractionEnabled = false` на WebView или родительском UIView
Если WebView (WKWebView или UIWebView) или его родительский UIView имеет isUserInteractionEnabled = false, то система не будет передавать события в WebView.
Решение: Убедитесь, что webView.isUserInteractionEnabled = true.

🟠Перекрывающий UIView с `isUserInteractionEnabled = false`
Если другой UIView (например, UIView-модальное окно или слой затемнения) перекрывает WebView, то iOS не передает события в нижележащие элементы. Даже если у этого UIView стоит isUserInteractionEnabled = false, он все равно блокирует тапы.
Решение: Убедитесь, что нет невидимых вьюх, блокирующих касания.

🟠WebView находится в UIScrollView с отключенным взаимодействием
Если WKWebView вложен в UIScrollView, у которого isUserInteractionEnabled = false, то жесты могут не передаваться в WebView.
Решение: Проверьте настройки UIScrollView, в который встроен WebView.

🟠Проблема с `gestureRecognizers`
Если в UIView или WKWebView добавлены кастомные UIGestureRecognizer, они могут перехватывать события, мешая Vue.
Решение: Отключите ненужные gestureRecognizers.

🟠JS блокирует события (например, `pointer-events: none`)
В Vue.js или в CSS может стоять pointer-events: none, что делает элементы некликабельными.
Решение: Проверьте стили в DevTools.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊5
🤔 Как загрузить что-то из интернета?

Чтобы загрузить данные (файл, JSON и т. д.), нужно:
1. Указать URL-адрес источника.
2. Отправить сетевой запрос.
3. Обработать полученный ответ.
4. Сохранить или использовать полученные данные.
В мобильной разработке обычно это делается асинхронно, чтобы не блокировать интерфейс.


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

При компиляции в Swift класс проходит несколько стадий обработки:
Анализ синтаксиса и семантики – компилятор проверяет код на ошибки.
Генерация промежуточного представления (IR) – создаётся код на уровне LLVM IR.
Оптимизация – Swift применяет различные оптимизации, например, inlining, dead code elimination и другие.
Генерация машинного кода – итоговый код превращается в исполняемый машинный код, специфичный для платформы.

🚩Что конкретно происходит с классом?

В отличие от структур, классы в Swift являются ссылочными типами и хранятся в куче (heap). Это означает, что:
- При создании объекта выделяется память в куче.
- Swift автоматически использует ARC (Automatic Reference Counting) для управления памятью.
- Методы класса могут вызываться через виртуальную таблицу (vtable), если класс использует динамическую диспетчеризацию.

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

- Если класс final, компилятор может оптимизировать вызовы методов, убрав динамическую диспетчеризацию.
- Наследование делает вызовы методов менее предсказуемыми (они идут через vtable).
- В отличие от структур, классы не копируются при передаче в функцию, а передаётся ссылка.

class Animal {
var name: String

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

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

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

let myDog = Dog(name: "Buddy")
myDog.speak()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 С помощью какого типа реализован Optional в Swift?

Optional реализован как перечисление (enum) с двумя случаями: .some(value) и .none. Это позволяет безопасно работать с отсутствием значений.


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

Если функция ожидает параметр типа Double, но вместо этого передаем саму функцию, произойдет ошибка компиляции, так как тип функции и Double — это разные вещи

func printDouble(value: Double) {
print("Значение: \(value)")
}

printDouble(value: printDouble) // Ошибка: Несовместимые типы


🚩Как исправить?

Если вы хотите передавать функцию в качестве аргумента, ее нужно объявить в параметрах как (Double) -> Void:
func processDouble(_ value: Double, action: (Double) -> Void) {
action(value)
}

processDouble(42.0, action: printDouble) // Выведет: "Значение: 42.0"


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

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

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

Начиная с iOS 17, Apple представила новый способ сделать класс наблюдаемым с помощью атрибута @Observable и свойства @Published. Этот подход упрощает создание наблюдаемых объектов и улучшает интеграцию с SwiftUI.

🚩Создание наблюдаемого класса с использованием @Observable

1⃣Импортируем необходимые модули
import SwiftUI


2⃣Создаем класс и добавляем атрибут @Observable
@Observable
class ViewModel {
@Published var message: String = "Привет, мир!"
}


3⃣Используем наблюдаемый объект в SwiftUI
Теперь мы можем использовать наш наблюдаемый объект в SwiftUI, и представление будет автоматически обновляться при изменении свойств, помеченных как @Published.
struct ContentView: View {
@StateObject private var viewModel = ViewModel()

var body: some View {
VStack {
Text(viewModel.message)
Button("Изменить сообщение") {
viewModel.message = "Привет, SwiftUI!"
}
}
}
}


🚩Пример с пояснением

Полный пример использования наблюдаемого класса в SwiftUI
import SwiftUI

@Observable
class ViewModel {
@Published var message: String = "Привет, мир!"
}

struct ContentView: View {
@StateObject private var viewModel = ViewModel()

var body: some View {
VStack {
Text(viewModel.message)
Button("Изменить сообщение") {
viewModel.message = "Привет, SwiftUI!"
}
}
}
}


Пример использования @EnvironmentObject
import SwiftUI

@Observable
class ViewModel {
@Published var message: String = "Привет, мир!"
}

struct ParentView: View {
@StateObject private var viewModel = ViewModel()

var body: some View {
ChildView()
.environmentObject(viewModel)
}
}

struct ChildView: View {
@EnvironmentObject var viewModel: ViewModel

var body: some View {
VStack {
Text(viewModel.message)
Button("Изменить сообщение") {
viewModel.message = "Привет, SwiftUI!"
}
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊6👍1
🤔 Почему self обязателен в escaping?

Потому что такие замыкания сохраняются в памяти, и указание self явно делает захват осознанным. Это предупреждает retain cycle и улучшает читаемость.

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

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

🟠Что значит "функция типа T"?
Функция в Swift — это тоже тип данных.
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}


Тип этой функции
(Int, Int) -> Int


🟠Пример: оператор `+` для функций
Допустим, у нас есть две функции, и мы хотим, чтобы оператор + создавал новую функцию, объединяя их поведение.
import Foundation

// Функция типа (Int) -> Int
func double(_ x: Int) -> Int {
return x * 2
}

func increment(_ x: Int) -> Int {
return x + 1
}

// Перегружаем оператор + для функций (Int) -> Int
func + (lhs: @escaping (Int) -> Int, rhs: @escaping (Int) -> Int) -> (Int) -> Int {
return { x in rhs(lhs(x)) } // Сначала вызываем первую, затем вторую
}

// Используем оператор
let combinedFunction = double + increment

print(combinedFunction(3)) // (3 * 2) + 1 = 7


🟠Пример: оператор `*` для функций
Можно сделать оператор *, который применяет функцию несколько раз.
// Перегружаем оператор * для дублирования применения функции
func * (lhs: @escaping (Int) -> Int, rhs: Int) -> (Int) -> Int {
return { x in
var result = x
for _ in 0..<rhs {
result = lhs(result)
}
return result
}
}

// Используем оператор
let tripleDouble = double * 3

print(tripleDouble(2)) // (2 * 2) * 2 * 2 = 16


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

Value-типы хранятся в стеке, создаются копии при передаче. Reference-типы хранятся в куче, передаются по ссылке. Управление памятью для reference-типа осуществляется через ARC. Value-типы быстрее в простых задачах, reference — гибче при сложных связях.
Вот подробные ответы на все вопросы, которые касаются управления памятью, навигации, работы потоков и асинхронности в iOS, а также концепций ARC и утечек:


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

Сайт UITableView (Table View) в iOS используется для отображения списков данных в виде таблицы. Это один из наиболее часто используемых компонентов пользовательского интерфейса в приложениях iOS, так как позволяет организовывать и представлять большие объемы информации в удобном для пользователя формате.

🚩Плюсы

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

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

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

Разделение данных на секции:
UITableView поддерживает разделение данных на секции с заголовками и подзаголовками, что упрощает организацию и навигацию по спискам данных.

🚩Основные компоненты `UITableView`

🟠Ячейки (Cells):
Основной элемент, используемый для отображения строки данных. Ячейки могут быть стандартными или кастомными.

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

🟠Перезагрузка данных (Reloading Data):
UITableView предоставляет методы для обновления данных, такие как reloadData(), которые перезагружают всю таблицу, и методы для обновления отдельных строк или секций с анимацией.

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

- Использует реиспользуемые ячейки (dequeueReusableCell) для оптимизации памяти.
- Работает с делегатами (UITableViewDelegate) и источниками данных (UITableViewDataSource) для динамического обновления контента.
- Поддерживает разделы, перетаскивание, редактирование.
- Может быть обновлен с анимацией (reloadData, performBatchUpdates).


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