Паттерн стратегия позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми. Таким образом, поведение объекта может изменяться во время выполнения без изменения его класса.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
Добавление задачи в concurrent queue через .sync означает:
- Задача выполняется синхронно — вызывающий поток ждёт завершения.
- Даже если очередь параллельная, .sync блокирует текущий поток до завершения блока.
Это может привести к deadlock'у, если .sync вызван изнутри той же очереди.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift все типы данных делятся на два вида по семантике хранения и передачи:
Структуры (
struct), перечисления (enum) и кортежи (tuple) передаются по значению, то есть каждое присваивание создаёт копию объекта. struct User {
var name: String
}
var user1 = User(name: "Alice")
var user2 = user1 // Копия!
user2.name = "Bob"
print(user1.name) // "Alice" (НЕ изменилось)
print(user2.name) // "Bob"Классы (
class), акторы (actor) и замыкания (closure) передаются по ссылке, то есть несколько переменных могут ссылаться на один и тот же объект. class User {
var name: String
init(name: String) {
self.name = name
}
}
var user1 = User(name: "Alice")
var user2 = user1 // Передача ссылки!
user2.name = "Bob"
print(user1.name) // "Bob" (ИЗМЕНИЛОСЬ!)
print(user2.name) // "Bob"Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Points (pt) – абстрактные единицы измерения в iOS, независимые от физического разрешения экрана.
- Pixels (px) – физические точки дисплея, отображающие контент.
На Retina-дисплеях 1 точка (pt) может состоять из 2×2 или 3×3 пикселей (px) (@2x, @3x). Это позволяет поддерживать четкость интерфейса на разных устройствах.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Это ключевой механизм ООП, позволяющий классам наследовать свойства, методы и другие характеристики от других классов. Это позволяет создавать новые классы на основе существующих, расширяя их функциональность или изменяя её.
Базовый класс определяет общие свойства и методы, которые могут быть унаследованы подклассами.
Подкласс наследует (или "расширяет") базовый класс. Он может переопределять унаследованные методы и свойства, добавлять новые методы и свойства, а также добавлять инициализаторы или изменять существующие.
Подклассы могут переопределять методы, свойства и индексаторы базового класса для изменения или расширения их поведения.
Можно предотвратить переопределение методов, свойств или индексаторов с помощью ключевого слова
final. Если метод, свойство или индексатор объявлен как final, то он не может быть переопределён в подклассе.class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// Этот метод будет переопределен в подклассах, если необходимо
}
}
class Bicycle: Vehicle {
var hasBasket = false
}
class Car: Vehicle {
var gear = 1
final func drive() {
print("Car is moving")
}
override func makeNoise() {
print("Vroom!")
}
}Подклассы могут вызывать методы своего суперкласса с помощью ключевого слова
super. Это позволяет подклассам расширять, а не заменять поведение суперкласса.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Барьер (Barrier) – это механизм синхронизации потоков, который позволяет контролировать порядок выполнения операций** в многопоточной среде.
Используются в многопоточном программировании для управления порядком операций с памятью.
В многопоточной среде процессоры и компиляторы могут оптимизировать порядок команд, что может привести к непредсказуемому поведению. Барьеры памяти предотвращают это.
#include <stdatomic.h>
atomic_int sharedValue = 0;
void updateValue() {
atomic_store_explicit(&sharedValue, 10, memory_order_release);
}
void readValue() {
int value = atomic_load_explicit(&sharedValue, memory_order_acquire);
}
Используются в Grand Central Dispatch (GCD)** для контроля порядка выполнения задач в
DispatchQueue. - Позволяет блокировать очередь на время выполнения задачи.
- Все задачи до барьера выполняются параллельно.
- Барьер ждёт завершения всех предыдущих задач, выполняется эксклюзивно, затем разрешает следующие задачи.
let queue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
queue.async {
print("Задача 1")
}
queue.async {
print("Задача 2")
}
// БАРЬЕРНАЯ ОПЕРАЦИЯ
queue.async(flags: .barrier) {
print("🔴 Барьер: все предыдущие задачи завершены, выполняюсь один!")
}
queue.async {
print("Задача 3")
}
В OpenMP (
#pragma omp barrier) ждёт завершения всех потоков перед выполнением следующего кода. #pragma omp parallel
{
printf("До барьера\n");
#pragma omp barrier
printf("После барьера\n");
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В Domain-Driven Design (DDD) управление зависимостями важно для создания четко разделенных и легко управляемых компонентов. Это достигается следующими методами:
Инъекция зависимостей позволяет передавать зависимости через конструкторы или сеттеры, снижая связанность и упрощая тестирование.
interface UserRepository {
findById(id: string): Promise<User | null>;
}
class UserService {
private userRepository: UserRepository;
constructor(userRepository: UserRepository) {
this.userRepository = userRepository;
}
public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}Сервисы домена содержат бизнес-логику, не принадлежащую конкретным сущностям или значимым объектам, и могут взаимодействовать с несколькими объектами или внешними сервисами.
class PaymentService {
private paymentGateway: PaymentGateway;
constructor(paymentGateway: PaymentGateway) {
this.paymentGateway = paymentGateway;
}
public async processPayment(order: Order): Promise<boolean> {
return await this.paymentGateway.charge(order.totalAmount);
}
}Анти-коррупционные слои защищают доменную модель от внешних влияний и преобразуют данные в понятные форматы.
class ExternalUserService {
public getUserData(id: string): ExternalUser {
// Получение данных от внешнего сервиса
}
}
class UserService {
private externalUserService: ExternalUserService;
constructor(externalUserService: ExternalUserService) {
this.externalUserService = externalUserService;
}
public getUser(id: string): User {
const externalUser = this.externalUserService.getUserData(id);
return new User(externalUser.id, externalUser.name, externalUser.email);
}
}Контейнеры управляют созданием и жизненным циклом зависимостей, упрощая конфигурацию и разрешение зависимостей.
import "reflect-metadata";
import { Container, injectable, inject } from "inversify";
@injectable()
class UserRepositoryImpl implements UserRepository {
public async findById(id: string): Promise<User | null> {
// Реализация метода
}
}
@injectable()
class UserService {
private userRepository: UserRepository;
constructor(@inject("UserRepository") userRepository: UserRepository) {
this.userRepository = userRepository;
}
public async getUserById(id: string): Promise<User | null> {
return await this.userRepository.findById(id);
}
}
// Конфигурация контейнера
const container = new Container();
container.bind<UserRepository>("UserRepository").to(UserRepositoryImpl);
container.bind<UserService>(UserService).toSelf();
// Разрешение зависимостей
const userService = container.get<UserService>(UserService);
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1💊1
Семафор — это примитив синхронизации, который ограничивает количество потоков, получающих доступ к ресурсу. Может использоваться для ожидания, блокировки и регулирования параллельных задач.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это абстрактная структура данных, работающая по принципу 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
Да, если использовать Notification Service Extension в iOS, можно изменить содержимое уведомления до показа. Однако это работает только для remote notification с mutable-content: 1.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Каждый способ создания макетов (layout'а) в iOS-разработке имеет свои преимущества и недостатки.
Удобный графический интерфейс для быстрой настройки и визуального просмотра изменений.
Простая настройка ограничений (constraints) для адаптивного интерфейса.
Возможность быстрого создания и изменения макетов без написания кода.
Ограниченные возможности для создания сложных и динамических макетов.
Трудности при слиянии изменений в storyboard или xib файлах в больших командах.
Более медленное время загрузки по сравнению с программными подходами.
Поддержка различных устройств и ориентаций экрана.
Возможность создания сложных и адаптивных макетов с помощью ограничений.
Упрощенная настройка ограничений через визуальный интерфейс.
Может быть сложно освоить, особенно для новичков.
Требует тщательного планирования и может стать сложным при работе с большим количеством ограничений.
Возможность точной настройки макета с помощью кода.
Легкость создания динамических и условных макетов.
Легче управлять изменениями в коде по сравнению с визуальными файлами.
Требует больше времени и усилий для настройки, особенно для сложных макетов.
Отсутствие визуального редактора может затруднить представление конечного результата.
Легкость создания и управления сложными макетами с минимальными усилиями.
Автоматическое управление ограничениями для вложенных элементов.
Поддержка различных ориентаций и размеров экранов.
Менее гибкие по сравнению с чистым Auto Layout или программными подходами.
Не всегда подходят для всех типов макетов, особенно для более сложных компоновок.
Простота и понятность кода благодаря декларативному подходу.
Мгновенное обновление интерфейса при изменении кода.
Современные возможности языка и тесная интеграция с экосистемой Apple.
Поддержка различных платформ (iOS, macOS, watchOS, tvOS).
Поддержка только iOS 13 и выше, что может ограничить использование на старых устройствах.
Некоторые функции еще находятся в стадии разработки, и могут быть ограничены возможности по сравнению с UIKit.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Когда объект запрашивается из базы, он сначала создаётся как fault — пустая обёртка, содержащая только идентификатор. Данные подгружаются только при обращении к свойствам.
Это снижает расход памяти и повышает производительность при работе с большими выборками.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
В Swift словарь (`Dictionary<Key, Value>`) – это неупорядоченная коллекция пар `ключ → значение`, где ключи уникальны.
Swift позволяет сравнивать два словаря по их ключам и значениям, если и ключи, и значения соответствуют протоколу
Equatable. let dict1 = ["name": "Alice", "age": "25"]
let dict2 = ["name": "Alice", "age": "25"]
let dict3 = ["name": "Bob", "age": "30"]
print(dict1 == dict2) // ✅ true (значения одинаковые)
print(dict1 == dict3) // ❌ false (разные значения)
Ошибка при сравнении
Dictionary без EquatableЕсли значения не соответствуют `Equatable`, сравнение не сработает:
struct User {
let name: String
}
let dict1 = ["user": User(name: "Alice")]
let dict2 = ["user": User(name: "Alice")]
// Ошибка: Type 'User' does not conform to protocol 'Equatable'
// print(dict1 == dict2)Решение: Сделать
User Equatable:struct User: Equatable {
let name: String
}
print(dict1 == dict2) // Теперь работает!Можно сравнивать отдельные элементы по ключу.
let dict = ["name": "Alice", "age": "25"]
if dict["name"] == "Alice" {
print("Имя совпадает") // ✅
}
Можно сравнить только ключи или только значения.
let dict1 = ["name": "Alice", "age": "25"]
let dict2 = ["age": "30", "name": "Alice"]
print(dict1.keys == dict2.keys) // ✅ true (ключи одинаковые)
Сравнение значений
print(Set(dict1.values) == Set(dict2.values)) // ✅ true (если порядок неважен)
Если словарь хранит
Any, то прямое сравнение не сработает, и нужно сравнивать элементы вручную.let dict1: [String: Any] = ["name": "Alice", "age": 25]
let dict2: [String: Any] = ["name": "Alice", "age": 25]
// Функция сравнения словарей с `Any`
func areDictionariesEqual(_ dict1: [String: Any], _ dict2: [String: Any]) -> Bool {
return NSDictionary(dictionary: dict1).isEqual(to: dict2)
}
print(areDictionariesEqual(dict1, dict2)) // ✅ true
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
UIView всегда имеет CALayer, так как UIView — это обёртка над CALayer в UIKit. CALayer может существовать без UIView, потому что это низкоуровневый элемент Core Animation, который не зависит от UIKit. Каждый
UIView внутри себя содержит CALayer, который отвечает за отрисовку. let view = UIView()
print(view.layer) // Всегда существует!
CALayer можно создать и добавить в иерархию без UIView. let layer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
layer.backgroundColor = UIColor.red.cgColor
if let window = UIApplication.shared.windows.first {
window.layer.addSublayer(layer) // Добавляем без UIView!
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
По коллекции можно итерироваться:
- С помощью цикла for.
- Через итераторы.
- Используя методы коллекций (например, map, filter, forEach).
- Через генераторы или ленивые последовательности (в функциональных языках).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Когда пользователь нажимает на иконку приложения на домашнем экране, iOS проходит несколько этапов перед тем, как приложение становится активным.
iOS-устройства управляются системой SpringBoard – это оболочка, отвечающая за домашний экран, иконки, фоновые процессы.
Когда пользователь тапает на иконку приложения, SpringBoard отправляет событие
UIApplicationLaunchOptionsKey в систему. Если приложение не запущено:
- iOS создаёт новый процесс и выделяет память.
- Загружаются зависимости (библиотеки, фреймворки).
- Создаётся объект
UIApplication. Здесь приложение инициализируется и загружается основной UI.
Метод в
AppDelegate: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("Приложение запущено")
return true
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Чтобы отложить создание ресурсоёмкого объекта до момента, когда он действительно нужен. Также позволяет избежать лишних вычислений, если свойство не будет использовано.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6