Синхронные и асинхронные соединения имеют свои уникальные преимущества и недостатки. Понимание этих различий важно для правильного выбора подхода в зависимости от контекста и требований приложения.
Плюсы
Синхронные операции проще понимать и реализовывать, так как они выполняются последовательно.
Отладка синхронного кода проще, так как последовательность выполнения предсказуема.
Синхронный код выполняется по порядку, что делает логику более прозрачной.
Минусы
Синхронные операции могут блокировать основной поток выполнения, что приводит к замедлению работы интерфейса или приложения в целом.
Приложения с синхронными операциями могут становиться менее отзывчивыми, так как долгие операции могут замораживать пользовательский интерфейс.
Синхронный подход может ограничивать возможности масштабирования, так как каждая операция ожидает завершения предыдущей.
Плюсы
Асинхронные операции позволяют выполнять другие задачи, не дожидаясь завершения долгих операций, что увеличивает общую производительность приложения.
Асинхронный подход позволяет поддерживать отзывчивость пользовательского интерфейса, так как долгие операции выполняются в фоновом режиме.
Асинхронный код лучше масштабируется, так как позволяет обрабатывать множество запросов одновременно.
Минусы
Асинхронные операции требуют более сложной реализации и понимания концепций, таких как колбэки, промисы или async/await.
Отладка асинхронного кода может быть сложнее из-за непредсказуемой последовательности выполнения.
Асинхронные операции могут приводить к неопределенным состояниям, если не обрабатывать их правильно, что может привести к трудноуловимым ошибкам.
Синхронный пример
func fetchDataSynchronously() {
let url = URL(string: "https://api.example.com/data")!
let data = try? Data(contentsOf: url)
if let data = data {
print("Данные загружены: \(data)")
} else {
print("Ошибка загрузки данных")
}
}Асинхронный пример
func fetchDataAsynchronously() {
let url = URL(string: "https://api.example.com/data")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print("Данные загружены: \(data)")
} else if let error = error {
print("Ошибка загрузки данных: \(error)")
}
}.resume()
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это модификатор, который предотвращает изменения:
- Класс: нельзя наследовать.
- Метод: нельзя переопределять.
- Переменная: значение нельзя изменить после инициализации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Не поддерживает множественное наследование для классов, что является общим выбором в современных объектно-ориентированных языках программирования из-за сложностей и проблем, которые множественное наследование может создать (например, проблема алмаза смерти). Однако предлагаются альтернативные механизмы, позволяющие имитировать некоторые аспекты множественного наследования.
Похожи на интерфейсы в других языках. Они позволяют классам, структурам и перечислениям принимать "контракты", которые определяют методы и свойства, которые должны быть реализованы. Протоколы могут наследоваться и расширяться, предоставляя гибкий способ добавления функциональности к типам.
protocol Movable {
func move()
}
protocol Drawable {
func draw()
}
class Character: Movable, Drawable {
func move() {
print("Персонаж двигается")
}
func draw() {
print("Персонаж рисуется")
}
}
Позволяют добавлять новую функциональность к существующим типам, включая те, которые определены в стандартной библиотеке или внешних библиотеках. Хотя расширения не могут добавлять хранимые свойства, они могут добавлять новые вычисляемые свойства, методы и протоколы.
extension Character: Drawable {
func draw() {
print("Персонаж рисуется через расширение")
}
}Это альтернативный подход к наследованию, при котором один тип включает другой как часть своей реализации, вместо того чтобы наследовать от него. Это предпочтительный метод для достижения повторного использования кода, позволяющий избежать ограничений и сложностей наследования.
class Engine {
func start() {
print("Двигатель запущен")
}
}
class Car {
let engine = Engine()
func startEngine() {
engine.start()
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
- Layer: низкоуровневый компонент (из Core Animation), который отвечает за рендеринг содержимого и может использоваться для сложных анимаций или оптимизаций.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Используется в замыканиях (closures) для управления тем, как замыкание захватывает переменные и константы из окружающего контекста. Могут захватывать и хранить ссылки на любые константы и переменные из контекста, в котором они определены. Это удобно, но может привести к сильным ссылочным циклам и утечкам памяти, если замыкание захватывает
self или другие экземпляры класса.Предоставляет способ определить правила захвата переменных в замыкании. Она задаётся в начале замыкания и позволяет избежать нежелательных сильных ссылок, особенно при работе с
self в методах класса, что очень важно для предотвращения утечек памяти в приложениях.Синтаксис Capture List
{ [capture rule] (parameters) -> return type in
// Код замыкания
}Пример с простым замыканием
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure() // Выведет "0 10"
Использование Capture List для предотвращения сильных ссылочных циклов
class MyClass {
var property = "Property"
func doSomething() {
let closure = { [weak self] in
print(self?.property ?? "нет self")
}
closure()
}
deinit {
print("MyClass экземпляр был деинициализирован")
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Термин "диспетчеризация" часто используется для описания механизма, посредством которого вызывается метод или функция в ответ на вызов. В контексте ООП, особенно в языках с поддержкой полиморфизма, диспетчеризация может быть статической (ранней) или динамической (поздней).
Использует компиляцию времени компиляции для определения того, какой метод будет вызван. Это означает, что компилятор определяет адрес вызываемого метода на этапе компиляции, и этот адрес не изменяется во время выполнения программы.
В языках, таких как C и Swift (при использовании
final классов или структур), методы, известные во время компиляции, могут быть вызваны без дополнительной нагрузки, связанной с поиском в таблице виртуальных функций.Используется в языках программирования, поддерживающих полиморфизм и наследование, и означает, что метод, который будет вызван, определяется во время выполнения. Это позволяет объектам различных классов отвечать на одни и те же сообщения (вызовы методов), каждый по-своему.
Таблица виртуальных методов (VMT): В объектно-ориентированных языках, таких как C++ или Java, каждый класс с виртуальными методами имеет таблицу виртуальных методов. Эта таблица содержит адреса всех виртуальных методов, которые могут быть вызваны для объекта данного класса. Поиск по таблице: Во время выполнения, когда вызывается метод, производится поиск соответствующего метода в таблице VMT, и используется адрес, найденный в таблице, что вносит задержку по сравнению со статической диспетчеризацией.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
- Слабые ссылки (weak): не увеличивают счётчик ссылок, используются для предотвращения циклических зависимостей.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Это ключевой механизм ООП, позволяющий классам наследовать свойства, методы и другие характеристики от других классов. Это позволяет создавать новые классы на основе существующих, расширяя их функциональность или изменяя её.
Базовый класс определяет общие свойства и методы, которые могут быть унаследованы подклассами.
Подкласс наследует (или "расширяет") базовый класс. Он может переопределять унаследованные методы и свойства, добавлять новые методы и свойства, а также добавлять инициализаторы или изменять существующие.
Подклассы могут переопределять методы, свойства и индексаторы базового класса для изменения или расширения их поведения.
Можно предотвратить переопределение методов, свойств или индексаторов с помощью ключевого слова
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
👍3
Обычно имеют в виду различия между использованием стека вызовов (память стека) и кучи (heap) в контексте управления памятью в программировании. Эти два типа памяти имеют разные способы выделения и освобождения памяти, каждый со своими преимуществами и недостатками.
Память в стеке выделяется и освобождается по очень простому и быстрому принципу: LIFO (Last In, First Out). Это означает, что для выделения памяти достаточно переместить указатель стека вверх (при добавлении данных) или вниз (при удалении данных). Этот процесс почти мгновенен и не требует дополнительных вычислений.
В куче память выделяется динамически, что требует управления доступными блоками памяти. Аллокатор памяти должен найти достаточно большой свободный блок, что может занять значительное время, особенно если память фрагментирована. Также освобождение памяти в куче требует более сложной обработки, включая возможную дефрагментацию.
Доступ к данным в стеке очень быстрый, потому что данные всегда добавляются и удаляются с "вершины" стека, где находится указатель стека. Это делает доступ к текущим локальным переменным очень быстрым и предсказуемым. Доступ к данным в куче может быть менее эффективным, поскольку данные могут быть разбросаны по разным частям памяти. Кроме того, дополнительное время требуется для поиска и управления блоками памяти.
Его память автоматически очищается при выходе из области видимости, что упрощает управление памятью и снижает риск утечек памяти. Память, выделенная в куче, остаётся занятой до тех пор, пока явно не будет освобождена. Это увеличивает риск утечек памяти, если разработчик забудет освободить память.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
- Аллокацию памяти.
- Освобождение через сборщик мусора (в Java, Swift) или вручную (в C++).
- Компактирование для предотвращения фрагментации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2🤯2👍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
❤4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В iOS-приложениях можно создавать анимации несколькими способами.
Самый простой способ анимации представлений (views) в iOS - это использование метода
UIView.animate. Вот пример кода, который изменяет положение и прозрачность представления за 1 секунду:UIView.animate(withDuration: 1.0) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}Для более сложных анимаций можно использовать Core Animation, например,
CABasicAnimation. Вот пример анимации изменения позиции слоя (layer):let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: CGPoint(x: 50, y: 50))
animation.toValue = NSValue(cgPoint: CGPoint(x: 150, y: 150))
animation.duration = 1.0
myView.layer.add(animation, forKey: "positionAnimation")
Этот класс предоставляет более детальный контроль над анимациями. Его можно использовать для создания и управления анимациями в реальном времени:
let animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}
animator.startAnimation()Для создания реалистичных анимаций с пружинным эффектом можно использовать метод
UIView.animate с параметрами пружинного демпфирования:UIView.animate(withDuration: 1.0,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: [],
animations: {
myView.frame.origin.y += 100
myView.alpha = 0.5
}, completion: nil)
Для анимации переходов между экранами используется
UIViewControllerAnimatedTransitioning. Это требует реализации методов протокола UIViewControllerAnimatedTransitioning:class CustomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to) else { return }
transitionContext.containerView.addSubview(toView)
toView.alpha = 0.0
UIView.animate(withDuration: 0.5, animations: {
toView.alpha = 1.0
}, completion: { finished in
transitionContext.completeTransition(finished)
})
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это ключевое слово, используемое для объявления слабой ссылки (weak reference) на объект, которая не увеличивает счетчик ссылок этого объекта. В отличие от
weak, unowned ссылки никогда не становятся nil, поэтому они используются в тех случаях, когда можно гарантировать, что объект, на который ссылаются, будет существовать так же долго, как и сама ссылка.Например, если один объект никогда не будет существовать дольше другого объекта, и тем самым вы уверены, что ссылка всегда будет действительной.
Чтобы предотвратить циклы сильных ссылок, которые могут привести к утечкам памяти.
``` swift
class Person {
let name: String
var creditCard: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: String
unowned let owner: Person
init(number: String, owner: Person) {
self.number = number
self.owner = owner
}
deinit {
print("CreditCard #\(number) is being deinitialized")
}
}
// Пример использования
var john: Person? = Person(name: "John Appleseed")
john?.creditCard = CreditCard(number: "1234 5678 9012 3456", owner: john!)
john = nil
```
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это метод, который вызывается перед удалением объекта из памяти. В Swift используется метод deinit. Он позволяет выполнять действия, такие как освобождение ресурсов или закрытие соединений, когда объект больше не нужен.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это структура данных, которая работает по принципу "первым пришел - первым ушел" (FIFO, First In, First Out). Это значит, что элементы добавляются в конец очереди и извлекаются из начала очереди.
Вставка элемента в конец очереди.
Удаление элемента из начала очереди.
Просмотр элемента в начале очереди без его удаления.
Проверка, содержит ли очередь элементы.
Очереди часто используются в задачах, где порядок обработки элементов важен.
Управление задачами в системах реального времени.
Обработка запросов в сетевых приложениях.
Имплементация алгоритмов обхода графов (например, поиск в ширину).
struct Queue<T> {
private var elements: [T] = []
// Добавление элемента в конец очереди
mutating func enqueue(_ element: T) {
elements.append(element)
}
// Удаление элемента из начала очереди
mutating func dequeue() -> T? {
return isEmpty ? nil : elements.removeFirst()
}
// Просмотр элемента в начале очереди
func peek() -> T? {
return elements.first
}
// Проверка на пустоту
var isEmpty: Bool {
return elements.isEmpty
}
}
// Пример использования
var queue = Queue<Int>()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
print(queue.dequeue() ?? "Queue is empty") // Вывод: 1
print(queue.peek() ?? "Queue is empty") // Вывод: 2GCD (Grand Central Dispatch) использует очереди для управления многозадачностью. Например,
DispatchQueue.main.async добавляет задачи в очередь для выполнения на главном потоке:DispatchQueue.main.async {
// Код выполняется на главном потоке
}Используются для управления и упорядочивания выполнения множества
Operation объектов:let queue = OperationQueue()
queue.addOperation {
print("Operation 1")
}
queue.addOperation {
print("Operation 2")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1. Retain: увеличивает счетчик ссылок объекта, указывая, что объект используется.
2. Release: уменьшает счетчик ссылок. Когда счетчик достигает нуля, объект освобождается из памяти.
Эта модель широко использовалась в Objective-C до появления ARC (Automatic Reference Counting).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1