Это ситуация, когда несколько потоков одновременно обращаются к одним и тем же данным, и итог зависит от порядка их выполнения. Может привести к непредсказуемому поведению и ошибкам.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
🎉 easyoffer 2.0 — релиз уже в этом месяце!
Вас ждут новые фичи, о которых мы ранее даже не упоминали. Они сделают путь к офферам ещё быстрее и эффективнее. Расскажу о них чуть позже 👀
В честь запуска мы готовим ограниченную акцию:
Первые 500 покупателей получат:
🚀 PRO тариф на 1 год с 50% скидкой
Что нужно сделать:
🔔 Подпишитесь на этот Telegram-канал, чтобы первыми узнать о старте релиза. Сообщение появится в нем раньше, чем где-либо еще — вы успеете попасть в число первых 500 и получить максимальную выгоду. 🎁 А еще только для подписчиков канала ценный бонус в подарок к PRO тарифу.
📅 Официальный запуск — уже совсем скоро.
Следите за новостями и не пропустите старт!
Вас ждут новые фичи, о которых мы ранее даже не упоминали. Они сделают путь к офферам ещё быстрее и эффективнее. Расскажу о них чуть позже 👀
В честь запуска мы готовим ограниченную акцию:
Первые 500 покупателей получат:
🚀 PRO тариф на 1 год с 50% скидкой
Что нужно сделать:
🔔 Подпишитесь на этот Telegram-канал, чтобы первыми узнать о старте релиза. Сообщение появится в нем раньше, чем где-либо еще — вы успеете попасть в число первых 500 и получить максимальную выгоду. 🎁 А еще только для подписчиков канала ценный бонус в подарок к PRO тарифу.
📅 Официальный запуск — уже совсем скоро.
Следите за новостями и не пропустите старт!
Garbage Collector (GC) и Automatic Reference Counting (ARC) – это два разных подхода к управлению памятью в программировании. Они решают одну задачу: автоматическое освобождение неиспользуемой памяти, но делают это по-разному.
Java, Kotlin, C#, Python, JavaScript
- GC периодически просматривает всю память приложения и ищет объекты, на которые больше нет ссылок.
- Когда такие объекты находятся, они удаляются, а память освобождается.
- Это автоматический процесс, который запускается по мере необходимости.
Где используется: Swift, Objective-C
- Каждый объект имеет счетчик ссылок (
reference count). - Когда переменная создает ссылку на объект, счетчик увеличивается.
- Когда переменная перестает ссылаться на объект, счетчик уменьшается.
- Когда счетчик достигает нуля, объект удаляется из памяти сразу же.
class Person {
var pet: Pet?
}
class Pet {
var owner: Person?
}
let person = Person()
let pet = Pet()
person.pet = pet
pet.owner = person // Теперь оба объекта держат друг друга, и ARC их не удалитРешение – использовать
weak: class Pet {
weak var owner: Person? // Теперь утечки памяти не будет
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
– Value types — struct, enum, tuple.
– Reference types — class, function, NSObject-производные.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Если все переменные в коде сделать
weak, объекты могут мгновенно удаляться из памяти, потому что никто не будет владеть ими (strong reference). -
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 уничтоженНапример, между
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
- private — ограничивает доступ к свойству или методу в пределах текущей области, например, внутри одного класса или расширения.
- fileprivate — расширяет доступ до всего файла, в котором объявлен элемент. Позволяет разным типам в одном файле работать с приватными членами друг друга.
- public private(set) — свойство можно читать откуда угодно, но изменять только внутри текущего модуля. Это позволяет внешнему коду обращаться к значению, но не менять его напрямую.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Перед вызовом
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
Ключевое слово inout указывает, что переменная будет передана в функцию по ссылке, а не по значению. Это значит, что функция сможет изменить значение переменной за пределами своей области видимости. Чтобы передать значение в inout-параметр, перед именем переменной ставится &.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В 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
Расширение ячейки (UITableViewCell, UICollectionViewCell) делается через добавление вью, анимаций, динамических constraints или swipe-действий. Также можно использовать кастомные layout'ы или auto-sizing.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Половина массива — это подмножество элементов массива, которое составляет примерно 50% его длины. В зависимости от контекста, это может быть:
Первая половина – элементы от начала массива до середины.
Вторая половина – элементы от середины до конца массива.
Любая подгруппа, близкая к 50% – например, при нечетном количестве элементов можно взять либо на один элемент больше, либо меньше.
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let middleIndex = array.count / 2 // Делим пополам
let firstHalf = Array(array[..<middleIndex]) // Первая половина
let secondHalf = Array(array[middleIndex...]) // Вторая половина
print(firstHalf) // [1, 2, 3, 4, 5]
print(secondHalf) // [6, 7, 8, 9, 10]
Разбиение данных для обработки (например, сортировка "разделяй и властвуй").Разделение элементов для параллельной обработки.
Разбиение коллекций при пагинации.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊6
Начиная с Swift 4, стандартный Dictionary сохраняет порядок вставки ключей. Даже при изменении значений или добавлении новых элементов порядок сохраняется, пока явным образом не удаляются ключи или не происходит массовая перераспределённая перестройка.
Это стало возможным благодаря обновлённой реализации хеш-таблицы внутри Dictionary.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
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
👍4
Проблема возникает, когда задача с более низким приоритетом получает доступ к ресурсу раньше задачи с высоким приоритетом. Это может происходить из-за отсутствия синхронизации или блокировок — пример: priority inversion.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Мьютекс (от англ. "mutex" - mutual exclusion, взаимное исключение) — это механизм синхронизации, используемый в многопоточном программировании для предотвращения одновременного доступа нескольких потоков к общим ресурсам, таким как переменные, структуры данных или файлы. Он помогает избежать состояния гонки (race condition), когда результат выполнения программы зависит от неопределённого порядка доступа потоков к ресурсу.
Мьютекс обеспечивает доступ к общему ресурсу только одному потоку в каждый момент времени. Когда один поток захватывает мьютекс, другие потоки должны ждать, пока мьютекс не будет освобождён.
Поток захватывает мьютекс перед доступом к общему ресурсу и освобождает его после завершения работы с этим ресурсом. Если мьютекс уже захвачен другим потоком, текущий поток будет блокирован до тех пор, пока мьютекс не будет освобождён.
import Foundation
class SafeCounter {
private var value = 0
private let lock = NSLock()
func increment() {
lock.lock() // Захват мьютекса
value += 1
lock.unlock() // Освобождение мьютекса
}
func getValue() -> Int {
lock.lock() // Захват мьютекса
let currentValue = value
lock.unlock() // Освобождение мьютекса
return currentValue
}
}
let counter = SafeCounter()
DispatchQueue.global().async {
for _ in 0..<1000 {
counter.increment()
}
}
DispatchQueue.global().async {
for _ in 0..<1000 {
counter.increment()
}
}
// Подождём немного, чтобы дать потокам закончить работу
Thread.sleep(forTimeInterval: 1)
print("Final counter value: \(counter.getValue())")
Мьютексы защищают общие ресурсы от одновременного доступа, предотвращая повреждение данных.
Код становится более предсказуемым и стабильным, так как исключаются состояния гонки.
Если мьютексы захватываются в неправильном порядке, это может привести к ситуации, когда два или более потока блокируют друг друга, ожидая освобождения мьютексов.
Чрезмерное использование мьютексов может привести к снижению производительности из-за увеличения времени ожидания потоков.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Обычно используются:
- pushViewController (в UINavigationController);
- present(_:animated:) — для модального показа;
- В SwiftUI — NavigationStack, .sheet, .fullScreenCover.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В
Operation (ранее NSOperation) в Foundation есть встроенный механизм отмены, который позволяет корректно завершить операцию, если она больше не нужна. Однако отмена не прерывает выполнение автоматически – код внутри операции должен сам проверять флаг отмены и корректно завершаться. Вызывается
cancel() – операция помечается как отмененная. Флаг
isCancelled становится true, но операция продолжает выполняться, если не проверяет этот флаг. Операция должна самостоятельно проверять
isCancelled и прерываться. class MyOperation: Operation {
override func main() {
for i in 1...10 {
if isCancelled { return } // Проверяем, отменена ли операция
print("Выполняется шаг \(i)")
sleep(1) // Симуляция работы
}
}
}
let queue = OperationQueue()
let operation = MyOperation()
queue.addOperation(operation)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
operation.cancel() // Отменяем через 3 секунды
}Если операция асинхронная (
isAsynchronous = true), просто проверять isCancelled недостаточно. Надо корректно управлять состояниями (isExecuting, isFinished).class AsyncOperation: Operation {
private var _executing = false
private var _finished = false
override var isAsynchronous: Bool { true }
override var isExecuting: Bool {
get { return _executing }
set {
willChangeValue(for: \.isExecuting)
_executing = newValue
didChangeValue(for: \.isExecuting)
}
}
override var isFinished: Bool {
get { return _finished }
set {
willChangeValue(for: \.isFinished)
_finished = newValue
didChangeValue(for: \.isFinished)
}
}
override func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
executeTask()
}
private func executeTask() {
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
if self.isCancelled {
self.complete()
return
}
print("Асинхронная операция завершена")
self.complete()
}
}
private func complete() {
isExecuting = false
isFinished = true
}
}
let queue = OperationQueue()
let asyncOp = AsyncOperation()
queue.addOperation(asyncOp)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
asyncOp.cancel() // Отменяем через 1 секунду
}Если у вас есть зависимости между операциями, отмена одной может автоматически отменить все последующие:
let op1 = MyOperation()
let op2 = MyOperation()
op2.addDependency(op1)
let queue = OperationQueue()
queue.addOperations([op1, op2], waitUntilFinished: false)
// Отменяем первую операцию, вторая тоже не выполнится
op1.cancel()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Plain cases: case success, failure
- Associated values: case data(String)
- Raw values: case error = 404
- Также поддерживаются indirect и вложенные enum.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊2
Классы — это ссылочные типы в Swift, которые позволяют создавать объекты с общими свойствами и методами. Они поддерживают наследование, работают с ARC (Automatic Reference Counting) и хранятся в куче (heap).
Когда вы присваиваете класс другой переменной, копируется не сам объект, а ссылка на него. Изменения, сделанные через одну ссылку, затронут и другие.
В отличие от структур, которые хранятся в стеке, классы создают объекты в куче. Это позволяет передавать объекты между функциями без копирования.
Swift использует ARC для управления памятью. Объект удаляется из памяти, когда на него больше нет ссылок.
В отличие от структур, классы могут наследовать свойства и методы от других классов. Поддерживают переопределение методов (
override). Два объекта класса можно сравнивать по ссылке (
===), а не только по значению (==). class Animal {
var name: String
init(name: String) {
self.name = name
}
func speak() {
print("Some sound")
}
}
class Dog: Animal {
override func speak() {
print("Woof!")
}
}
let dog1 = Dog(name: "Buddy")
let dog2 = dog1 // Копируется ссылка, а не объект
dog2.name = "Charlie"
print(dog1.name) // "Charlie", так как dog1 и dog2 указывают на один объектСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Когда пользователь касается экрана, происходит следующий процесс:
1. Аппаратный слой (дисплей) фиксирует касание и передаёт его в iOS.
2. Система генерирует объект события (UIEvent, UITouch).
3. Событие передаётся в главное окно (UIWindow) и далее — вью-контроллерам и иерархии UIView.
4. Система запускает hit-testing — определяет, какая вьюшка находится под точкой касания.
5. Вызываются методы:
- hitTest(_:with:) — определяет целевую вью.
- touchesBegan(_:with:), touchesMoved, touchesEnded — передают событие в конкретную вью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4