Swift | Вопросы собесов
2.15K subscribers
29 photos
959 links
Download Telegram
Forwarded from easyoffer
Я боялся, что провалю собеседование. Так появился easyoffer

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

Типа… ты потратил месяцы на то, чтобы учиться, писал pet-проекты, собирал резюме, рассылаешь отклики — и всё может закончиться на одном-единственном вопросе, на который ты не знаешь ответ.

Я реально боялся.
Я смотрел видео mock-собеседований на YouTube, останавливал каждое, выписывал вопросы в Notion. Потом вручную писал к ним ответы. И потом ещё по нескольку раз перечитывал. Такой вот "тренажёр" на коленке.

📎 (там на картинке — один из моих реальных списков в Notion, ставь 🔥 если тоже так делал)

В какой-то момент я посчитал — у меня уже было выписано больше 500 вопросов. Я почувствовал ужас.
Потому что невозможно всё это зазубрить. А что, если спросят как раз тот, к которому я не успел подготовиться?..

Тогда и пришла идея

А что если понять, какие из вопросов встречаются чаще всего? Чтобы не учить всё подряд, а сфокусироваться на главном.

Так родился easyoffer.

Сначала — просто как пет-проект, чтобы показать в резюме и подготовиться к собесам. А потом оказалось, что он реально помогает людям. За первые месяцы его посетили сотни тысяч человек. И я понял: это больше, чем просто пет-проект.

Сейчас я делаю EasyOffer 2.0
И уже не один, а вместе с вами.

В новой версии будут:
– вопросы из реальных собесов, с фильтрацией по грейду, компании, типу интервью
– тренажёр с карточками (по принципу интервальных повторений — как в Anki)
– база задач с интервью
– тренажёр «реальное собеседование», чтобы отрепетировать как в жизни

Каждая фича упрощает и сокращает время на подготовку. Все эти штуки я бы мечтал иметь, когда сам готовился к собеседованиям.

Я делаю всё на свои деньги. Никаких инвесторов. Только вы и я.

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

Все, кто поддержат проект до релиза, получат:

🚀 1 год PRO-доступа по цене месячной подписки. Его можно активировать в любое время, например когда начнете готовится к собесам.
Доступ к закрытому бета-тесту

Поддержать 👉 https://planeta.ru/campaigns/easyoffer

Спасибо, что верите в этот проект 🙌
🤔 Что такое property wrapper?

Это механизм Swift, позволяющий добавить логику при доступе к свойству (при чтении, записи, обновлении).
Они позволяют:
- Автоматически управлять значением.
- Реализовывать реактивность, кэширование, валидацию и т.п.
- Быть читаемыми и декларативными.


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

Stack (стек) – это структура данных, работающая по принципу LIFO (Last In, First Out – "последним пришел, первым ушел").

🚩Пример реализации стека в Swift

В Swift нет встроенного стека (кроме Array), но можно создать свой:
struct Stack<T> {
private var elements: [T] = []

mutating func push(_ item: T) {
elements.append(item)
}

mutating func pop() -> T? {
return elements.popLast() // Удаляет и возвращает верхний элемент
}

func peek() -> T? {
return elements.last // Возвращает верхний элемент без удаления
}

func isEmpty() -> Bool {
return elements.isEmpty
}
}

// Пример использования:
var stack = Stack<Int>()
stack.push(10)
stack.push(20)
stack.push(30)

print(stack.pop()!) // 30
print(stack.peek()!) // 20
print(stack.isEmpty()) // false


🚩Где используется стек?

Обратный порядок выполнения (рекурсия) – стек вызовов функций.
Алгоритмы (обратная польская нотация, DFS – поиск в глубину)
История действий (назад-вперед в браузере, отмена в редакторе).

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

1. GCD (Grand Central Dispatch): для управления потоками и выполнения задач.
2. OperationQueue: более высокоуровневая абстракция над GCD.
3. URLSession: для выполнения сетевых запросов в фоне.
4. BackgroundTasks: для выполнения долгосрочных задач в фоне.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
🤯1
🤔 Какие можешь выделить главные принципы ООП?

Основные принципы объектно-ориентированного программирования (ООП) включают:
1. Инкапсуляция: сокрытие внутренних деталей реализации объекта и объединение данных и методов, работающих с этими данными, в один объект.
2. Наследование: создание нового класса на основе существующего.
3. Полиморфизм: возможность обращаться с объектами, производными от одного базового класса, для выполнения методов, определенных в базовом классе, но переопределенных в производных.
4. Абстракция: определение интерфейса взаимодействия с объектом, отделяющего его функциональное поведение от конкретной реализации.


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

Структуры (struct) – это один из основных типов данных в Swift. Они значимые (value type), то есть при передаче копируются, а не передаются по ссылке.

🚩Главные особенности `struct`

Value type – при передаче копируется (в отличие от классов)
Нет ARC (автоматического подсчета ссылок) – производительность выше
Могут иметь методы, свойства, инициализаторы
Работают с протоколами (protocol-oriented programming)
Не поддерживают наследование

🚩Пример структуры

struct Car {
var model: String
var year: Int

func description() -> String {
return "\(model) – \(year)"
}
}

let car1 = Car(model: "Tesla", year: 2023)
var car2 = car1 // Создается копия

print(car1.description()) // Tesla – 2023
print(car2.description()) // Tesla – 2023

car2.year = 2024 // Изменяем car2, но car1 остается прежним
print(car1.year) // 2023
print(car2.year) // 2024


🚩Мутируемые структуры (`mutating`)

По умолчанию методы не могут изменять свойства структуры. Чтобы изменить их внутри метода, нужно использовать mutating
struct Counter {
var count = 0

mutating func increment() {
count += 1
}
}

var counter = Counter()
counter.increment()
print(counter.count) // 1


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

Двусторонние (взаимные) связи позволяют объектам знать друг о друге, что удобно при навигации в обе стороны. Например, если у вас есть User и Post, вы можете получить все посты пользователя (user.posts), а из поста — автора (post.user). Это упрощает работу с данными, повышает связность и облегчает доступ к связанным объектам. Также это удобно для поддержки целостности и каскадных операций (например, удаления).


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

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

🟠Массивы
Один из самых распространённых способов реализации стека — это использование массива. В такой реализации элементы стека хранятся в массиве, и индекс последнего элемента (вершина стека) отслеживается отдельной переменной.
struct Stack<Element> {
private var storage: [Element] = []

mutating func push(_ element: Element) {
storage.append(element)
}

mutating func pop() -> Element? {
return storage.popLast()
}

func peek() -> Element? {
return storage.last
}

var isEmpty: Bool {
return storage.isEmpty
}
}


🟠Связные списки
Стек можно реализовать с использованием связных списков, где каждый элемент списка содержит данные и ссылку на следующий элемент в стеке. Вершина стека в такой реализации — это начало связного списка.
class Node<Element> {
var value: Element
var next: Node?

init(value: Element) {
self.value = value
}
}

struct Stack<Element> {
private var head: Node<Element>?

mutating func push(_ element: Element) {
let node = Node(value: element)
node.next = head
head = node
}

mutating func pop() -> Element? {
let node = head
head = head?.next
return node?.value
}

func peek() -> Element? {
return head?.value
}

var isEmpty: Bool {
return head == nil
}
}


🟠Стек вызовов
Это системный стек, который используется во время выполнения программы для хранения информации о вызовах функций/методов. Он хранит адреса возврата, параметры функций, локальные переменные и другие данные, необходимые для управления вызовами функций и их возврата.

🚩Зачем он нужен?

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

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 "Weak" и "Unowned" в чем разница?

Weak и Unowned в Swift используются для предотвращения циклов сильных ссылок в случае управления памятью через ARC. Weak ссылки могут становиться nil, если объект, на который они ссылаются, освобождается, и используются, когда возможна утечка памяти. Unowned ссылки, напротив, не могут становиться nil, и если объект освобожден, доступ к unowned ссылке приведет к ошибке. Weak обычно используется для объектов, которые могут быть удалены, а Unowned — когда объект должен существовать всегда.

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

В Swift ключи в словаре (Dictionary) должны быть уникальными и сравниваться между собой. Поэтому, если вы хотите использовать*свою структуру в качестве ключа, она должна соответствовать протоколу Hashable.

🚩Что нужно?

Структура должна соответствовать Hashable
Должен быть реализован hash(into:) или использовать Equatable + Hashable автоматически
Свойства структуры должны быть Hashable (если String, Int, Double – все ок)

🚩Пример кастомной структуры в `Dictionary`

struct Person: Hashable {
let id: Int
let name: String
}

// Теперь можно использовать `Person` как ключ
var peopleAges: [Person: Int] = [
Person(id: 1, name: "Alice"): 25,
Person(id: 2, name: "Bob"): 30
]

// Доступ по ключу
let alice = Person(id: 1, name: "Alice")
print(peopleAges[alice] ?? "Не найдено") // 25


🚩Ручная реализация `hash(into:)`

Если нужно кастомное хеширование, можно реализовать hash(into:):
struct Person: Hashable {
let id: Int
let name: String

func hash(into hasher: inout Hasher) {
hasher.combine(id) // Используем только id для хеша
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Состояние сцены и переходы (Scene phases and transitions)?

ScenePhase описывает состояние приложения или сцены:
- active — сцена на экране и активно взаимодействует с пользователем.
- inactive — сцена на экране, но неактивна (например, уведомление поверх).
- background — сцена не отображается, приложение в фоне.
Переходы между состояниями позволяют сохранять данные, приостанавливать процессы, управлять ресурсами.
Используется через
@Environment(.scenePhase) и onChange.

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

Когда вы открываете несколько экранов (UIViewController) в iOS, они обычно создают стек представлений (View Controller Stack). В зависимости от способа открытия экранов (модально или через навигационный стек), поведение dismiss будет разным.

🚩Модальное представление (`present`)

Если экраны открывались модально (present), то dismiss на последнем экране просто закроет только этот экран, и управление вернётся к предыдущему.
let newVC = UIViewController()
present(newVC, animated: true)


Позже вызываем:
dismiss(animated: true)


Если вызвать dismiss на первом модально представленном контроллере, все последующие модальные контроллеры закроются сразу.
// Открываем два экрана последовательно
let vc1 = UIViewController()
let vc2 = UIViewController()
present(vc1, animated: true)
vc1.present(vc2, animated: true)


Если вызвать dismiss на vc2:
vc2.dismiss(animated: true)


Если вызвать dismiss на vc1:
vc1.dismiss(animated: true)


🚩Навигационный стек (`push`)

Если экраны открывались через UINavigationController (pushViewController), то dismiss не работает для удаления последнего экрана. Нужно использовать popViewController.
let vc1 = UIViewController()
let vc2 = UIViewController()
navigationController?.pushViewController(vc1, animated: true)
navigationController?.pushViewController(vc2, animated: true)


Теперь если мы вызовем:
vc2.dismiss(animated: true)


Правильный способ закрытия последнего экрана в UINavigationController:
navigationController?.popViewController(animated: true)


Если вы хотите закрыть весь стек экранов, используйте:
navigationController?.popToRootViewController(animated: true)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Forwarded from easyoffer
Осталось всего 14 дней до завершения краудфандинга

Сейчас самое подходящее время подключиться, если вы ждали или откладывали:

Все, кто поддержат проект сейчас, до релиза, получат:
🚀 PRO-доступ на 1 год по цене месячной подписки
Бета-доступ к EasyOffer 2.0 (конец мая)

👉 Поддержать: https://planeta.ru/campaigns/easyoffer
🤔 Что такое retail release?

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

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

В Swift нет множественного наследования классов, но можно использовать множественное наследование через протоколы.

🚩Почему нет множественного наследования классов?

Swift запрещает множественное наследование классов, потому что оно может привести к конфликтам и алмазной проблеме (diamond problem).
Допустим, в языке с поддержкой множественного наследования у нас есть два родительских класса с одинаковым методом:
class A {
public:
void greet() { cout << "Hello from A"; }
};

class B {
public:
void greet() { cout << "Hello from B"; }
};

// C наследуется от A и B
class C : public A, public B {};

C obj;
obj.greet(); // Какой метод вызвать? A или B?


🚩Как обойти ограничение? Используем протоколы!

В Swift можно реализовать множественное наследование через протоколы, поскольку класс может соответствовать нескольким протоколам одновременно.
protocol Flyable {
func fly()
}

protocol Swimmable {
func swim()
}

class Animal {}

class Duck: Animal, Flyable, Swimmable {
func fly() {
print("Утка летит")
}

func swim() {
print("Утка плывёт")
}
}

let duck = Duck()
duck.fly() // Утка летит
duck.swim() // Утка плывёт


🚩Что делать, если нужен код по умолчанию?

Если хочется, чтобы протокол предоставлял реализацию по умолчанию (почти как родительский класс), можно использовать extension:
protocol Walker {
func walk()
}

extension Walker {
func walk() {
print("Иду вперёд")
}
}

class Person: Walker {}

let human = Person()
human.walk() // "Иду вперёд" (метод взят из extension)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Перечислите и расскажи о счётчиках ссылок

Каждый объект имеет счётчик ссылок, отражающий, сколько активных ссылок на него существует:
- retain — увеличивает счётчик.
- release — уменьшает.
- dealloc вызывается, когда счётчик становится 0.
В ARC всё это происходит не вручную, а автоматически. Цель — не допустить утечек памяти и преждевременного освобождения объектов.


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

В 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