uz
Feedback
Swift | Вопросы собесов

Swift | Вопросы собесов

Kanalga Telegram’da o‘tish

Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @easyoffer_adv

Ko'proq ko'rsatish
2 205
Obunachilar
-424 soatlar
-57 kunlar
-930 kunlar
Postlar arxiv
🤔 Какие есть способы внедрения зависимостей? Через инициализацию (constructor injection), сеттеры, property injection, либо через DI-контейнер (например, Swinject, Resolver или Hilt в Android). В iOS чаще используют первый и третий способ. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Что такое runLoop? RunLoop — это фундаментальный механизм в iOS и macOS, который управляет циклом обработки событий в приложении. Он отслеживает и обрабатывает входящие события, такие как нажатия клавиш, касания экрана, таймеры и сетевые запросы, и поддерживает приложение в активном состоянии, пока оно не завершится. 🚩Основные аспекты `RunLoop` 🟠Цикл обработки событий RunLoop постоянно выполняет цикл, ожидая входящие события и обрабатывая их по мере поступления. Этот цикл состоит из нескольких этапов: ожидание события, обработка события и повтор цикла. 🟠Режимы (Modes) RunLoop может работать в разных режимах, которые определяют, какие источники событий будут отслеживаться и обрабатываться. Основные режимы включают default и tracking (для событий отслеживания, таких как прокрутка). В каждой итерации RunLoop обрабатывает события только для текущего режима.
RunLoop.current.run(mode: .default, before: Date.distantFuture)     
🟠Источники событий (Event Sources) RunLoop может отслеживать различные источники событий, такие как таймеры (Timer), порты (Port), ввод пользователей (такие как касания экрана и клики мыши), а также пользовательские источники (Input Source). 🟠Таймеры RunLoop может управлять таймерами, которые выполняют задачи через определенные интервалы времени.
     let timer = Timer(timeInterval: 1.0, repeats: true) { _ in
         print("Timer fired!")
     }
     RunLoop.current.add(timer, forMode: .default)
🟠Обработка событий RunLoop используется для обработки событий в основном потоке (main thread) приложения. Это особенно важно для поддержания отзывчивости пользовательского интерфейса, поскольку все взаимодействия с UI происходят в основном потоке. 🚩Пример использования `RunLoop`
import Foundation

class Example {
    var timer: Timer?

    func startRunLoop() {
        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
        RunLoop.current.run()
    }

    @objc func timerFired() {
        print("Timer fired!")
    }
}

let example = Example()
example.startRunLoop()
Ставь 👍 и забирай 📚 Базу знаний

🤔 В чем разница между синхронными и асинхронными запросами? Синхронные запросы блокируют выполнение программы до получения ответа, что может замедлить работу приложения, особенно если запрос занимает длительное время. Асинхронные запросы, напротив, не блокируют основной поток и позволяют программе продолжать выполнение других задач, пока ожидается ответ от сервера или другой операции. Асинхронные запросы часто используются для работы с сетью или файловой системой, чтобы улучшить отзывчивость и производительность программ. В Swift асинхронные операции реализуются через GCD или `async/await`. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Как заставить оператор поддерживать функцию типа 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
Ставь 👍 и забирай 📚 Базу знаний

🤔 А если сделать свою структуру, то она будет поддерживать Copy-On-Write? Нет, по умолчанию пользовательские структуры не поддерживают COW. Чтобы реализовать это, нужно использовать обёртку с reference type и isKnownUniquelyReferenced для контроля над копированием. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Как бы реализовал словарь в 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
Ставь 👍 и забирай 📚 Базу знаний

🤔 В чём проблема, если все переменные будут делаться в weak? Если все переменные будут weak, объекты будут сразу уничтожаться, как только ни одна сильная ссылка на них не останется. Это приведёт к непредсказуемому поведению, потере данных и крашам, потому что объекты будут nil при обращении. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Структуры (value-типы) хранятся? на стеке, если используются как локальные переменные. в куче, если они: — вложены в класс, — захвачены замыканием, — передаются как inout, — возвращаются из функции и живут дольше текущего контекста. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Как можно итерироваться по коллекции? По коллекции можно итерироваться: - С помощью цикла for. - Через итераторы. - Используя методы коллекций (например, map, filter, forEach). - Через генераторы или ленивые последовательности (в функциональных языках). Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Какие есть инструменты для работы с потоками? В Swift есть несколько инструментов для работы с многопоточностью и параллельным выполнением кода. Вот основные из них: 🟠GCD (Grand Central Dispatch) – главный инструмент для потоков GCD – это низкоуровневая технология, позволяющая управлять задачами (тасками) в очередях (DispatchQueue).
DispatchQueue.global(qos: .background).async {
    print("Фоновый поток")
    
    DispatchQueue.main.async {
        print("Вернулись в главный поток")
    }
}
🟠OperationQueue – более удобный API для задач OperationQueue – это более гибкая и объектно-ориентированная альтернатива GCD.
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // Ограничение на 2 задачи одновременно

queue.addOperation {
    print("Операция 1")
}

queue.addOperation {
    print("Операция 2")
}
🟠Actors – безопасная работа с потоками в Swift 5.5+ С actor можно работать с потоками без гонок данных, потому что все его свойства защищены от одновременного доступа.
actor Counter {
    private var value = 0
    
    func increment() {
        value += 1
    }

    func getValue() -> Int {
        return value
    }
}

let counter = Counter()

Task {
    await counter.increment()
    print(await counter.getValue()) // Потокобезопасный доступ
}
🟠Task & Async/Await (Swift 5.5+) – современный подход к асинхронности С async/await код становится читаемым и удобным.
func fetchData() async -> String {
    try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 секунда задержки
    return "Данные загружены"
}

Task {
    let result = await fetchData()
    print(result)
}
Ставь 👍 и забирай 📚 Базу знаний

🤔 Где фреймы — наиболее яркий пример использования? Фреймы логично использовать в анимациях, при рисовании на Canvas, и в простых элементах интерфейса, где точное позиционирование важнее гибкости. Также актуальны при создании кастомных вью или в играх. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Почему Apple предпочитает использовать value type по умолчанию? Apple предпочитает value types (структуры struct) по умолчанию в Swift по нескольким причинам 🟠Безопасность многопоточного кода struct копируется при передаче, а не передается по ссылке, как class. Это снижает вероятность гонки данных (data race), когда один поток изменяет объект, а другой читает его одновременно. В многопоточной среде это делает код более безопасным.
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 1, y: 2)
var p2 = p1  // p2 - это копия, изменения в p2 не затронут p1

p2.x = 10

print(p1.x) // 1
print(p2.x) // 10
🟠Производительность struct хранятся в стеке, а не в куче, что делает их создание и удаление быстрее. Куча (heap) требует управления памятью (ARC – Automatic Reference Counting), а struct — нет.
class MyClass { var value = 0 }  // В куче (heap), управляется ARC
struct MyStruct { var value = 0 }  // В стеке (stack), копируется при передаче
🟠Предсказуемость и неизменяемость struct ведут себя как примитивные типы (Int, Double), что делает код предсказуемым. Их изменение происходит локально, без неожиданных эффектов в других частях программы.
class Car {
    var speed: Int
    init(speed: Int) { self.speed = speed }
}

var car1 = Car(speed: 60)
var car2 = car1  // car2 - это ссылка на тот же объект

car2.speed = 100  // Изменение затрагивает car1!

print(car1.speed) // 100 (хотя мы меняли car2!)
🟠Использование в стандартной библиотеке Swift изначально построен на struct: Int, Double, Bool, Array, Dictionary, String — это структуры. Это делает язык более безопасным и производительным.
var arr1 = [1, 2, 3]
var arr2 = arr1  // Копия массива, а не ссылка!

arr2.append(4)

print(arr1) // [1, 2, 3]  (не изменился!)
print(arr2) // [1, 2, 3, 4] (новый массив)
🚩Когда использовать `class`? Хотя struct — предпочтительный выбор, class нужен, когда: Нужна ссылочная семантика (например, объект должен изменяться в разных местах кода). Есть сложные иерархии наследования. Требуется работа с Objective-C (NSObject). Ставь 👍 и забирай 📚 Базу знаний

🤔 Какие есть ключевые различия между Objective-C и Swift? Swift — это современный, безопасный, типизированный язык с лаконичным синтаксисом. Objective-C основан на динамической диспетчеризации и C-подобной нотации. В Swift управление памятью автоматизировано и безопаснее. Swift также поддерживает опциональные типы, value semantics, протокол-ориентированное программирование, тогда как Objective-C ближе к классической ООП-модели. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

Осталось 3 часа до конца акции: «Пожизненный PRO тариф — по цене 1 года» Поиск работы отнимает силы, время и веру в себя, но
Осталось 3 часа до конца акции: «Пожизненный PRO тариф — по цене 1 года» Поиск работы отнимает силы, время и веру в себя, но не у тех кто использует easyoffer PRO. Успей сделать самую выгодную инвестицию в развитие своей карьеры. Акция закончится уже сегодня 23 июня 23:59 по мск: 👉 https://easyoffer.ru/pro

🤔 Что означает принцип open closed? Принцип открытости/закрытости (Open/Closed Principle) гласит, что классы должны быть открыты для расширения, но закрыты для модификации. Это позволяет добавлять новую функциональность без изменения существующего кода, что снижает риск внесения ошибок. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

Последний день акции: «Пожизненный PRO тариф — по цене 1 года» 🚀 PRO включает: – Полный доступ ко всем грейдам и профессиям
Последний день акции: «Пожизненный PRO тариф — по цене 1 года» 🚀 PRO включает: – Полный доступ ко всем грейдам и профессиям – База live-coding задач и вопросов из технических собеседований с вероятностью их встречи – Примеры лучших ответов от Senior разработчиков – 1100+ записи реальных собеседований, в том числе в топовые компании (Сбер, Авито, Яндекс, WB, OZON, МТС и др.) – База 400+ тестовых заданий от компаний. – Автоотклики на вакансии в хедхантер – Аналитика ТОП-требований из вакансий для лучшего написания резюме и прохождения ATS систем рекрутеров – Генератор уникального резюме и CV под каждую вакансию – Тренажеры подготовки к собеседованию: «Реальное собеседование» и «Проработка вопросов» по методике интервальных повторений (как Anki) – (скоро) Агрегатор вакансий – (скоро) Сообщество Акция закончится уже сегодня 23 июня 23:59 по мск: 👉 https://easyoffer.ru/pro

🤔 Что наследуется? Класс-наследник может унаследовать: - свойства и методы, - инициализаторы (если они явно разрешены), - доступ к superclass через super. Протоколы могут быть унаследованы другим протоколом или реализованы классом/структурой. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

Пожизненный PRO тариф — по цене 1 года. Покупаешь один раз — пользуешься всю жизнь: 👉 https://easyoffer.ru/pro 🚀 PRO-доступ
Пожизненный PRO тариф — по цене 1 года. Покупаешь один раз — пользуешься всю жизнь: 👉 https://easyoffer.ru/pro 🚀 PRO-доступ закроет 99% проблем на пути к офферу: 1. Полный доступ ко всем грейдам и профессиям. Не важно, Junior вы или Senior, Тестировщик, Разработчик, Проджект — вы получите материалы под ваш текущий уровень и цели, без ограничений. 2. База live-coding задач и вопросов с реальных собесов с уникальной системой вероятности их встречи. Вы будете готовиться не вслепую, а точечно по тем темам, которые спрашивают чаще всего. 3. Эталонные ответы от Senior-разработчиков. Никакой воды и догадок — только четкие, структурированные решения, за которые дают «зеленый свет» к офферу 4. 1100+ записей реальных собеседований (включая топы: Сбер, Авито, Яндекс, WB, OZON, МТС). Вы увидите всё изнутри: как спрашивают, как отвечают сильные кандидаты и на каких ошибках проваливаются 80% проходящих. 5. База 400+ тестовых заданий. Если вы еще студент, то практикуйтесь на решении задач, которые помогут попасть на собес 6. Автоотклики на Хедхантере — пока вы спите, ваше резюме летит к рекрутерам автоматически. Это экономия сотен часов ручного кликанья. 7. Аналитика ТОП-требований из вакансий. Мы парсим рынок и показываем, какие скиллы сейчас в цене. Это позволит вам точечно апгрейдить резюме и проходить суровые ATS-фильтры (которые отсеивают до 75% резюме еще до просмотра рекрутером). 8. Генератор уникального резюме и CV под каждую вакансию. Забудьте про «универсальное» резюме — нейросеть адаптирует ваш опыт под конкретную позицию за минуту, повышая шансы на приглашение в разы. 9. Тренажеры подготовки к собеседованию: «Реальное собеседование» — сценарий вопросов из реальных интервью «Проработка вопросов» — флеш карточки с вопросами/ответами по методике интервальных повторений (как Anki) 10. (Скоро) Агрегатор вакансий — все вакансии из HH, Telegram, LinkedIn и других площадок в одной ленте. 11. (Скоро) Закрытое комьюнити — нетворкинг и помощь в сложных вопросах от таких же целеустремленных айтишников. Завтра последний день акции: 👉 https://easyoffer.ru/pro

🤔 Что происходит с вьюшками, которые лежат в Scroll View? - Эти вьюшки не двигаются физически — они находятся в contentView, которая движется вместе с contentOffset. - Относительно ScrollView они могут визуально выходить за пределы видимости, но физически остаются на месте внутри большого contentSize. Если вью ушла за пределы видимой области, она не рендерится до тех пор, пока не попадёт в видимую область (оптимизация). Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний

🤔 Как отлавливать, где допущена ошибка в свифте? В 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("Метод ещё не реализован")
}
Ставь 👍 и забирай 📚 Базу знаний