Двусторонние (взаимные) связи позволяют объектам знать друг о друге, что удобно при навигации в обе стороны. Например, если у вас есть 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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В Swift ключи в словаре (
Dictionary) должны быть уникальными и сравниваться между собой. Поэтому, если вы хотите использовать*свою структуру в качестве ключа, она должна соответствовать протоколу Hashable. Структура должна соответствовать
Hashable Должен быть реализован
hash(into:) или использовать Equatable + Hashable автоматически Свойства структуры должны быть
Hashable (если String, Int, Double – все ок) 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:):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
ScenePhase описывает состояние приложения или сцены:
- active — сцена на экране и активно взаимодействует с пользователем.
- inactive — сцена на экране, но неактивна (например, уведомление поверх).
- background — сцена не отображается, приложение в фоне.
Переходы между состояниями позволяют сохранять данные, приостанавливать процессы, управлять ресурсами.
Используется через
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Когда вы открываете несколько экранов (UIViewController) в iOS, они обычно создают стек представлений (View Controller Stack). В зависимости от способа открытия экранов (модально или через навигационный стек), поведение
dismiss будет разным.Если экраны открывались модально (
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)
Если экраны открывались через
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
Сейчас самое подходящее время подключиться, если вы ждали или откладывали:
Все, кто поддержат проект сейчас, до релиза, получат:
🚀 PRO-доступ на 1 год по цене месячной подписки
➕ Бета-доступ к EasyOffer 2.0 (конец мая)
👉 Поддержать: https://planeta.ru/campaigns/easyoffer
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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 }, класс или структура, реализующая протокол, должна предоставить как минимум геттер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.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.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
Когда происходит первый доступ к view, или когда вызывается метод loadView(). Тогда UIViewController инициализирует дерево вью
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Forwarded from easyoffer
🎉 Easyoffer 2.0 — самый успешный краудфандинг в истории рунета в категории "Технологии"!
Мы это сделали! За считанные часы после старта, благодаря вашей поддержке, проект не просто стартовал — он взлетел.
💸 Собрано: 2 276 840 рублей
Это не просто цифра — это ваше доверие, ваша вера в идею, и ваша инвестиция в будущее карьеры сотен (а скоро — тысяч) специалистов.
💼 Благодаря этой сумме мы уже:
— Наняли ещё пару разработчиков и аналитиков
— Запустили активный сбор и разметку новых данных
— Ускорили разработку и подняли планку качества
Спасибо каждому, кто поверил в нас на старте! Дальше — только масштабирование и развитие. Мы строим сервис, который станет must-have для всех, кто ищет работу в IT.
👉 Присоединяйтесь сейчас — это только начало.
Мы это сделали! За считанные часы после старта, благодаря вашей поддержке, проект не просто стартовал — он взлетел.
💸 Собрано: 2 276 840 рублей
Это не просто цифра — это ваше доверие, ваша вера в идею, и ваша инвестиция в будущее карьеры сотен (а скоро — тысяч) специалистов.
💼 Благодаря этой сумме мы уже:
— Наняли ещё пару разработчиков и аналитиков
— Запустили активный сбор и разметку новых данных
— Ускорили разработку и подняли планку качества
Спасибо каждому, кто поверил в нас на старте! Дальше — только масштабирование и развитие. Мы строим сервис, который станет must-have для всех, кто ищет работу в IT.
👉 Присоединяйтесь сейчас — это только начало.
🔥1
В контексте Swift термин Satisfiable (удовлетворяемый) относится к тому, может ли конкретный тип соответствовать (удовлетворять) требованиям протокола.
Протокол считается satisfiable, если его можно реализовать без противоречий. Например, если в протоколе указаны требования, которые могут быть выполнены в классе или структуре.
protocol Animal {
var name: String { get }
func speak()
}
struct Dog: Animal {
var name = "Барсик"
func speak() {
print("Гав-гав")
}
}
let dog = Dog()
dog.speak() // "Гав-гав"Протокол не может быть удовлетворён (то есть, не satisfiable), если он содержит требования, которые невозможно реализовать в конкретном типе.
protocol Impossible {
var value: Self { get } // Self требует, чтобы свойство содержало сам тип
}
struct Example: Impossible {
var value: Example // ОШИБКА: бесконечная рекурсия!
}Если вы видите ошибку Protocol 'X' cannot be satisfied, значит, Swift не может найти способ правильно реализовать все его требования.
Проверьте ассоциированные типы и
Self – они должны корректно ссылаться на реализуемый тип.Проверьте требования протокола – убедитесь, что все свойства и методы могут быть реализованы в классе/структуре.
Попробуйте использовать
any Protocol – если Self создаёт проблемы, возможно, нужно использовать any: func test(value: any Impossible) { } // Позволяет работать с протоколом без строгого SelfСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Используй do-catch для перехвата throw, try? и try! для безопасного вызова функций, которые могут выбросить ошибку. Также можно логировать через os_log, print, или использовать краш-репортинг-сервисы (Sentry, Firebase Crashlytics).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift есть несколько инструментов для работы с многопоточностью и параллельным выполнением кода. Вот основные из них:
GCD – это низкоуровневая технология, позволяющая управлять задачами (тасками) в очередях (
DispatchQueue). DispatchQueue.global(qos: .background).async {
print("Фоновый поток")
DispatchQueue.main.async {
print("Вернулись в главный поток")
}
}OperationQueue – это более гибкая и объектно-ориентированная альтернатива GCD.
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // Ограничение на 2 задачи одновременно
queue.addOperation {
print("Операция 1")
}
queue.addOperation {
print("Операция 2")
}
С
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()) // Потокобезопасный доступ
}С
async/await код становится читаемым и удобным.func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 секунда задержки
return "Данные загружены"
}
Task {
let result = await fetchData()
print(result)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
Что такое PRO-подписка на easyoffer 2.0?
easyoffer PRO — это не просто доступ к базе, а полноценный инструмент для получения оффера.
🧠 База вопросов с собеседований
+ Анализ на основе 4,000 собеседований
+ Вероятность встречи каждого вопроса
+ Фильтрация по грейдам, компаниям, типам интервью
+ Примеры ответов: текстовые и видео
+ Готовьтесь к собеседованию в конкретную компанию
🛠 Тренажер "Проработка вопросов"
+ Флеш-карточки + интервальные повторения
+ Персональная система показа карточек в зависимости от ваших ответов
+ Упор на наиболее частые вопросы
+ Фокус на слабые места и быстрый прогресс
🎭 Тренажер "Реальное собеседование"
+ Сценарии на основе реальных интервью
+ Подготовка к конкретным компаниям
+ Итоговая статистика: прошёл/не прошёл
🧩 База задач с собеседований
+ Live-coding и System Design задачи
+ Оценка вероятности встречи задачи
+ Подготовка к задачам по конкретным компаниям
📋 База тестовых заданий
+ Задания из реальных вакансий
+ Фильтрация по технологиям и грейдам
+ Лучшие решения в доступе
📈 Тренды технологий в вакансиях
+ Топ-100 навыков, которые требуют компании
+ Динамика популярности технологий
+ Фильтрация по грейдам
🎁 Специальная цена до релиза:
3200 руб. за целый год
Сейчас PRO на 1 год стоит как будет стоить 1 месяц после релиза. Покупка также открывает доступ к закрытому бета-тестированию.
+ Вы можете активировать подписку в любой момент, например, когда начнете искать работу.
Предзаказ здесь: https://planeta.ru/campaigns/easyoffer
📌 Цена поднимется сразу после запуска.
Если вы хотите перестать угадывать, что спросят на собеседовании, и начать точечно готовиться на основе реальных данных — easyoffer PRO именно для вас.
Экономьте время. Получайте оффер легко.
easyoffer PRO — это не просто доступ к базе, а полноценный инструмент для получения оффера.
🧠 База вопросов с собеседований
+ Анализ на основе 4,000 собеседований
+ Вероятность встречи каждого вопроса
+ Фильтрация по грейдам, компаниям, типам интервью
+ Примеры ответов: текстовые и видео
+ Готовьтесь к собеседованию в конкретную компанию
🛠 Тренажер "Проработка вопросов"
+ Флеш-карточки + интервальные повторения
+ Персональная система показа карточек в зависимости от ваших ответов
+ Упор на наиболее частые вопросы
+ Фокус на слабые места и быстрый прогресс
🎭 Тренажер "Реальное собеседование"
+ Сценарии на основе реальных интервью
+ Подготовка к конкретным компаниям
+ Итоговая статистика: прошёл/не прошёл
🧩 База задач с собеседований
+ Live-coding и System Design задачи
+ Оценка вероятности встречи задачи
+ Подготовка к задачам по конкретным компаниям
📋 База тестовых заданий
+ Задания из реальных вакансий
+ Фильтрация по технологиям и грейдам
+ Лучшие решения в доступе
📈 Тренды технологий в вакансиях
+ Топ-100 навыков, которые требуют компании
+ Динамика популярности технологий
+ Фильтрация по грейдам
🎁 Специальная цена до релиза:
3200 руб. за целый год
Сейчас PRO на 1 год стоит как будет стоить 1 месяц после релиза. Покупка также открывает доступ к закрытому бета-тестированию.
+ Вы можете активировать подписку в любой момент, например, когда начнете искать работу.
Предзаказ здесь: https://planeta.ru/campaigns/easyoffer
📌 Цена поднимется сразу после запуска.
Если вы хотите перестать угадывать, что спросят на собеседовании, и начать точечно готовиться на основе реальных данных — easyoffer PRO именно для вас.
Экономьте время. Получайте оффер легко.
- AutoLayout — это система ограничений (constraints), определяющих расположение элементов вне зависимости от размера экрана. Используется при адаптивной, универсальной верстке.
- Frame-верстка — это явное указание координат и размеров каждого элемента. Быстро, но не гибко — плохо масштабируется под разные устройства и ориентации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Перед вызовом
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() элементы ещё не отрисованы, поэтому 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
Процесс выглядит так:
1. Пользователь нажимает кнопку.
2. Запускается сетевой запрос по URL изображения.
3. После завершения загрузки изображение преобразуется в нужный формат.
4. Полученное изображение устанавливается в элемент ImageView.
Важно, чтобы сетевой запрос выполнялся в фоновом потоке, а обновление интерфейса — в основном UI-потоке.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1💊1