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

В Swift словарь (Dictionary) представляет собой структуру данных, которая хранит пары ключ-значение и обеспечивает быстрый доступ к значениям по ключу (O(1) в среднем). Но если бы мы хотели реализовать словарь на основе массива, то пришлось бы использовать линейный поиск, что делает операции менее эффективными (O(n)).

🚩Реализация словаря через массив кортежей

Один из простых способов создать словарь на базе массива — использовать массив кортежей (ключ, значение).
struct ArrayDictionary<Key: Equatable, Value> {
private var items: [(Key, Value)] = [] // Храним пары ключ-значение

// Получение значения по ключу
func value(for key: Key) -> Value? {
for (k, v) in items {
if k == key {
return v
}
}
return nil
}

// Добавление или обновление значения
mutating func insert(value: Value, for key: Key) {
for i in 0..<items.count {
if items[i].0 == key {
items[i].1 = value // Обновляем значение, если ключ уже есть
return
}
}
items.append((key, value)) // Добавляем новую пару
}

// Удаление элемента по ключу
mutating func remove(for key: Key) {
items.removeAll { $0.0 == key }
}
}

// Пример использования
var myDict = ArrayDictionary<String, Int>()
myDict.insert(value: 42, for: "age")
myDict.insert(value: 30, for: "height")

print(myDict.value(for: "age")!) // 42
myDict.remove(for: "age")
print(myDict.value(for: "age")) // nil


🚩Оптимизация: Использование бинарного поиска

Если мы отсортируем массив по ключам, можно использовать бинарный поиск (O(log n)) для ускорения поиска ключа.
struct SortedArrayDictionary<Key: Comparable, Value> {
private var items: [(Key, Value)] = []

// Бинарный поиск индекса ключа
private func index(of key: Key) -> Int? {
var left = 0
var right = items.count - 1
while left <= right {
let mid = (left + right) / 2
if items[mid].0 == key {
return mid
} else if items[mid].0 < key {
left = mid + 1
} else {
right = mid - 1
}
}
return nil
}

// Получение значения по ключу
func value(for key: Key) -> Value? {
if let index = index(of: key) {
return items[index].1
}
return nil
}

// Вставка с сохранением сортировки
mutating func insert(value: Value, for key: Key) {
if let index = index(of: key) {
items[index].1 = value
} else {
items.append((key, value))
items.sort { $0.0 < $1.0 } // Сортируем после вставки
}
}
}

// Использование
var sortedDict = SortedArrayDictionary<String, Int>()
sortedDict.insert(value: 50, for: "b")
sortedDict.insert(value: 20, for: "a")
sortedDict.insert(value: 70, for: "c")

print(sortedDict.value(for: "b")!) // 50


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

Массив может содержать только один тип данных (например, [String], [Int]).
Однако, можно хранить разные типы данных, если использовать:
- Any – массив [Any] может содержать String, Int, Double, но требует явного приведения типов.
- protocol – если все элементы реализуют общий протокол ([CustomProtocol]).
- enum с ассоциативными значениями – позволяет хранить разные типы в одном контейнере (enum DataType { case string(String), int(Int) }).


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

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

🟠Статическая связка Swift Runtime
Swift — это относительно новый язык, и его стандартная библиотека не встроена в iOS (как, например, Objective-C runtime). Это значит, что при компиляции приложения в его бинарь включаются стандартные Swift-библиотеки.

🟠Bitcode и архитектуры (Fat Binary)
При публикации в App Store, Xcode компилирует приложение для нескольких архитектур (arm64, armv7, x86_64). Это называется Fat Binary — один исполняемый файл включает версии для разных процессоров.

🟠Swift ABI Stability – улучшение, но не панацея
С версии Swift 5.0 ABI (Application Binary Interface) стабилизирован. Это означает, что в iOS 12.2+ уже есть встроенные Swift-библиотеки.

🟠Использование SwiftUI и Combine
SwiftUI и Combine — новые технологии, они не так оптимизированы, как UIKit. При их использовании код разрастается за счёт декларативного подхода и дополнительной логики от Apple.

🟠Дополнительные ресурсы и Assets
Если приложение использует локализацию, изображения, шрифты, CoreML, ARKit, это тоже увеличивает размер .ipa.

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

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


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

NSManagedObjectID – это уникальный идентификатор объекта в Core Data, который остаётся неизменным на протяжении всего жизненного цикла объекта.

🚩Как сохранить `NSManagedObjectID`?

Используем uriRepresentation() – это строка (URL), которая уникально идентифицирует объект.
func saveObjectID(_ object: NSManagedObject) {
let objectID = object.objectID.uriRepresentation().absoluteString
UserDefaults.standard.set(objectID, forKey: "savedObjectID")
}


🚩Как восстановить объект по `NSManagedObjectID`?

1. Получаем URL из UserDefaults.
2. Преобразуем URL в NSManagedObjectID.
3. Загружаем объект из Core Data.
func fetchSavedObjectID(context: NSManagedObjectContext) -> NSManagedObject? {
guard let objectIDString = UserDefaults.standard.string(forKey: "savedObjectID"),
let objectURL = URL(string: objectIDString) else { return nil }

let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: objectURL)

if let objectID = objectID {
return context.object(with: objectID)
}
return nil
}


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

Реляционные БД (например, PostgreSQL, MySQL) используют таблицы, строгую схему и SQL. Нереляционные БД (например, MongoDB, Redis) хранят данные в формате документов, графов или пар "ключ-значение" и лучше подходят для гибких или масштабируемых структур.


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

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

🚩Порождающие паттерны (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
1👍3
🤔 Какие есть инструменты для работы с потоками?

В зависимости от платформы:
- GCD (Grand Central Dispatch) — диспетчер задач в iOS.
- Operation и OperationQueue.
- NSThread (устаревшее).
- DispatchQueue.async / sync.
- Thread, RunLoop.
- coroutine-подходы в других языках (например, Kotlin, Python).


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

В Swift есть несколько способов отлавливать и диагностировать ошибки в коде:

🚩Обработка ошибок через `do-catch`

Используется, если функция генерирует ошибку (throws).
enum LoginError: Error {
case wrongPassword
case userNotFound
}

func login(user: String, password: String) throws {
if user != "admin" { throw LoginError.userNotFound }
if password != "1234" { throw LoginError.wrongPassword }
}

do {
try login(user: "admin", password: "wrong")
} catch LoginError.wrongPassword {
print("Ошибка: Неверный пароль")
} catch {
print("Ошибка: \(error)")
}


🚩`assert()`, `precondition()`, `fatalError()` (для отладки)

Эти функции прерывают выполнение программы, если что-то пошло не так.
assert() (только в Debug)
let age = -5
assert(age >= 0, "Возраст не может быть отрицательным")


precondition() (работает в Release)
precondition(age >= 0, "Возраст не может быть отрицательным")


fatalError() (прерывает программу)
func getData() -> String {
fatalError("Метод ещё не реализован")
}


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

Это термины из управления памятью в Objective-C, но понимание их полезно и в Swift:
- assign — простое присваивание без увеличения счётчика ссылок. Используется для значимых типов (int, float, struct) или для слабых ссылок.
- retain — увеличивает счётчик ссылок (reference count), предотвращая уничтожение объекта. Используется для ссылочных типов (классов).
В Swift аналогом являются сильные (strong), слабые (weak) и некопируемые (unowned) ссылки.


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

Фреймы (frames) в iOS-разработке используются для задания размеров и расположения элементов интерфейса (UIView) вручную.

🟠Кастомные анимации
Когда нужно анимировать движение элемента, проще всего работать с его frame, так как он напрямую управляет origin (координаты) и size (размеры).
UIView.animate(withDuration: 0.5) {
self.button.frame.origin.x += 100
}


🟠Ручная верстка (без Auto Layout)
Если вы не используете Auto Layout или хотите задать положение элементов программно, frame позволяет точно указать размеры и координаты.
let button = UIButton(type: .system)
button.frame = CGRect(x: 50, y: 100, width: 200, height: 50)
button.setTitle("Нажми меня", for: .normal)
view.addSubview(button)


🟠Оптимизация производительности
При динамической подгрузке ячеек в UITableView или UICollectionView можно вручную вычислять frame для ускорения работы, вместо использования Auto Layout, который может замедлить скроллинг.

🟠Работа с Core Graphics и CALayer
При рисовании или настройке слоев CALayer используется frame, чтобы точно определить размеры слоя.
let borderLayer = CALayer()
borderLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
borderLayer.borderWidth = 2
borderLayer.borderColor = UIColor.red.cgColor
view.layer.addSublayer(borderLayer)


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

Принцип открытости/закрытости (Open/Closed Principle) гласит, что классы должны быть открыты для расширения, но закрыты для модификации. Это позволяет добавлять новую функциональность без изменения существующего кода, что снижает риск внесения ошибок.

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

Если все переменные в коде сделать weak, объекты могут мгновенно удаляться из памяти, потому что никто не будет владеть ими (strong reference).

🚩Как работает `weak`?

- weak создает слабую ссылку – объект не увеличивает счетчик ссылок (retain count).
- Если нет других (strong) ссылок на объект, он удаляется (ARC освобождает память).
- weak переменные всегда являются Optional, потому что объект может стать nil в любой момент.
class Person {
weak var name: String? // Ошибка! Строки – это value type, weak нельзя
}


Проблема, если weak используется везде
class Car {
weak var model: String? // Ошибка (value type)
weak var owner: Person? // ⚠️ Будет nil, если нет других strong ссылок
}

class Person {
var car: Car?
}

var person: Person? = Person()
person?.car = Car()
person?.car?.owner = person // `owner` - weak, объект сразу удалится

print(person?.car?.owner) // nil, объект Person уничтожен


🚩Когда `weak` полезен?

🟠Избегание циклов ссылок (retain cycle)
Например, между delegate и ViewController
   protocol SomeDelegate: AnyObject {
func doSomething()
}

class ViewController {
weak var delegate: SomeDelegate? // Prevent retain cycle
}


🟠Когда объект не должен владеть другим объектом
Например, ячейки в UITableView не должны владеть ViewController.

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

Stable ABI (Application Binary Interface) означает, что двоичный интерфейс языка стабилен, и можно использовать скомпилированные Swift-библиотеки между разными версиями без перекомпиляции. Это важно для модульности и совместимости на уровне фреймворков.


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

В Swift переменные (свойства), объявленные в протоколе, должны указывать:

🟠Только для чтения (`get`)
Если свойство объявлено как { get }, класс или структура, реализующая протокол, должна предоставить как минимум геттер
protocol Animal {
var name: String { get } // Только чтение
}

struct Dog: Animal {
let name = "Барсик" // Реализуем только get
}

let dog = Dog()
print(dog.name) // "Барсик"


Можно также использовать вычисляемое свойство:
struct Cat: Animal {
var name: String {
return "Мурзик"
}
}


🟠Для чтения и записи (`get set`)
Если свойство { get set }, класс или структура обязательно должны предоставить и get, и set.
protocol Vehicle {
var speed: Int { get set } // Чтение и запись
}

class Car: Vehicle {
var speed: Int = 100 // Реализуем и get, и set
}

let car = Car()
car.speed = 120 // Можно изменить значение
print(car.speed) // 120


Вычисляемое свойство тоже подойдёт, если оно имеет get и set:
class Bike: Vehicle {
private var internalSpeed = 50

var speed: Int {
get { return internalSpeed }
set { internalSpeed = newValue }
}
}


🟠Статические свойства (`static`)
Если свойство должно быть общим для всех экземпляров (не индивидуальным), то оно объявляется static.
protocol Config {
static var appVersion: String { get }
}

struct AppSettings: Config {
static let appVersion = "1.0.0"
}

print(AppSettings.appVersion) // "1.0.0"


Класс может использовать class var, если свойство можно переопределять в подклассах:
class AppInfo: Config {
class var appVersion: String {
return "2.0.0"
}
}


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

Размер становится известен в viewDidLayoutSubviews(). В этот момент система уже рассчитала layout, включая frame, bounds и safeAreaInsets.

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

Garbage Collector (GC) и Automatic Reference Counting (ARC) – это два разных подхода к управлению памятью в программировании. Они решают одну задачу: автоматическое освобождение неиспользуемой памяти, но делают это по-разному.

🚩Garbage Collector (GC)

Java, Kotlin, C#, Python, JavaScript
- GC периодически просматривает всю память приложения и ищет объекты, на которые больше нет ссылок.
- Когда такие объекты находятся, они удаляются, а память освобождается.
- Это автоматический процесс, который запускается по мере необходимости.

🚩Подсчет ссылок (ARC - Automatic Reference Counting)

Где используется: Swift, Objective-C
- Каждый объект имеет счетчик ссылок (reference count).
- Когда переменная создает ссылку на объект, счетчик увеличивается.
- Когда переменная перестает ссылаться на объект, счетчик уменьшается.
- Когда счетчик достигает нуля, объект удаляется из памяти сразу же.
class Person {
var pet: Pet?
}

class Pet {
var owner: Person?
}

let person = Person()
let pet = Pet()

person.pet = pet
pet.owner = person // Теперь оба объекта держат друг друга, и ARC их не удалит


Решение – использовать weak:
class Pet {
weak var owner: Person? // Теперь утечки памяти не будет
}


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

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
Официальный релиз easyoffer 2.0 состоится уже в течение нескольких дней.

Напоминаю, что в честь релиза запускаем акцию.

Первые 500 покупателей получат:

🚀 Скидку 50% на PRO тариф на 1 год
🎁 Подарок ценностью 5000₽ для тех, кто подписан на этот канал

🔔 Подпишитесь на этот канал: https://t.me/+b2fZN17A9OQ3ZmJi
В нем мы опубликуем сообщение о релизе в первую очередь
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие изменения надо сделать в constrait перед вызовом layout?

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

🟠Обновление значения констрейнта
Перед вызовом layoutIfNeeded(), измените свойство констрейнта (например, constant у NSLayoutConstraint).
class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!

override func viewDidLoad() {
super.viewDidLoad()

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.changeButtonHeight()
}
}

func changeButtonHeight() {
heightConstraint.constant = 100 // Меняем значение констрейнта
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded() // Перестраиваем макет
}
}
}


🟠Если меняется набор констрейнтов
Если нужно удалить/добавить констрейнты, используйте `activate()` / `deactivate()`.
var expanded = false

@IBOutlet weak var smallHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var largeHeightConstraint: NSLayoutConstraint!

func toggleHeight() {
expanded.toggle()

if expanded {
NSLayoutConstraint.deactivate([smallHeightConstraint])
NSLayoutConstraint.activate([largeHeightConstraint])
} else {
NSLayoutConstraint.deactivate([largeHeightConstraint])
NSLayoutConstraint.activate([smallHeightConstraint])
}

UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}


🟠Если работа идёт в `viewDidLoad()`
Во viewDidLoad() элементы ещё не отрисованы, поэтому layoutIfNeeded() не сработает. Используйте viewDidAppear() или вызовите layoutIfNeeded() после view.layoutIfNeeded().
override func viewDidLoad() {
super.viewDidLoad()
heightConstraint.constant = 100
view.layoutIfNeeded() // НЕ обновит макет, потому что он ещё не загружен
}


Решение
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

heightConstraint.constant = 100
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}


Разница между layoutIfNeeded() и setNeedsLayout()
heightConstraint.constant = 100
view.setNeedsLayout() // Обновление произойдет на следующем цикле рендера


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