Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Если оставить контекст в замыкании, не принимая во внимание возможные проблемы, это может привести к нескольким серьезным проблемам, особенно в многопоточном и асинхронном программировании.
Одной из самых распространенных проблем является утечка памяти из-за циклов удержания (retain cycles). Это происходит, когда два или более объекта удерживают ссылки друг на друга, препятствуя освобождению памяти. В этом примере
closure захватывает self, что создает цикл удержания: MyClass держит сильную ссылку на closure, а closure держит сильную ссылку на self.class MyClass {
var value: Int = 0
var closure: (() -> Void)?
func setupClosure() {
closure = {
self.value += 1
}
}
}
let instance = MyClass()
instance.setupClosure()Когда замыкания захватывают изменяемый контекст, это может привести к условиям гонки и непредсказуемому поведению, особенно при работе в многопоточном окружении. Если метод
increment вызывается из разных потоков, это может привести к условиям гонки и некорректному изменению значения count.class Counter {
var count = 0
func increment() {
DispatchQueue.global().async {
self.count += 1
}
}
}
let counter = Counter()
counter.increment()Если замыкания захватывают тяжелые ресурсы (например, файлы, сети), это может привести к задержкам в их освобождении, что может негативно сказаться на производительности приложения. Если
FileHandler освобождается, но замыкание все еще захватывает file, это может привести к задержке в освобождении файлового дескриптора.class FileHandler {
var file: File?
func processFile() {
DispatchQueue.global().async {
self.file?.read()
}
}
}Когда используется слабая ссылка (
weak), замыкание может обнаружить, что захваченный объект освобожден, что приводит к тому, что слабая ссылка становится nil. Это требует дополнительных проверок и обработки.class MyClass {
var value: Int = 0
var closure: (() -> Void)?
func setupClosure() {
closure = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.value += 1
}
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В iOS разработке классы, представляющие пользовательский интерфейс, такие как
UIView, реализуют протокол UIViewRepresentable. Однако, если вы имеете в виду протоколы, которые непосредственно реализует UIView, то вот основные протоколы:UIView наследуется от NSObject, поэтому реализует протокол NSObjectProtocol. Этот протокол предоставляет базовые методы, которые должны быть реализованы всеми объектами в Objective-C, такие как управление памятью и динамическое разрешение методов.Протокол
UIAppearance позволяет настраивать внешний вид всех экземпляров определенного типа (например, все кнопки или все метки) в приложении. UIButton.appearance().tintColor = .blue
Протокол
UIAccessibility предоставляет поддержку для обеспечения доступности представлений. Это позволяет экранным ридерам и другим вспомогательным технологиям взаимодействовать с приложением. view.isAccessibilityElement = true
view.accessibilityLabel = "Описание элемента"
Если вы работаете со SwiftUI и хотите использовать представление
UIView в SwiftUI, используется протокол UIViewRepresentable. Вот пример, как это делается: import SwiftUI
import UIKit
struct MyUIView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .red
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
// Обновление состояния представления, если необходимо
}
}
struct ContentView: View {
var body: some View {
MyUIView()
.frame(width: 100, height: 100)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
В контексте объектно-ориентированного программирования инициализация обычно осуществляется с помощью конструктора или инициализатора, который устанавливает начальные значения свойств объекта и выполняет любую необходимую начальную настройку.
Обеспечивают выделение памяти для нового объекта.
Устанавливают начальные значения для всех свойств объекта.
Выполняют любую дополнительную настройку, необходимую для объекта.
Основные инициализаторы, которые обеспечивают полную инициализацию всех свойств объекта.
Вспомогательные инициализаторы, которые вызывают другой инициализатор для выполнения части работы и могут добавлять дополнительную настройку.
Предоставляются автоматически для структур и классов, если все их свойства имеют начальные значения и не определены другие инициализаторы.
Принимают параметры, которые используются для установки начальных значений свойств объекта.
class Car {
var color: String
var model: String
var year: Int
// Назначенный инициализатор
init(color: String, model: String, year: Int) {
self.color = color
self.model = model
self.year = year
}
// Удобный инициализатор
convenience init(model: String) {
self.init(color: "White", model: model, year: 2020)
}
}
// Использование назначенного инициализатора
let myCar = Car(color: "Red", model: "Toyota", year: 2020)
// Использование удобного инициализатора
let defaultCar = Car(model: "Honda")Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Метод
lazy используется для отложенной (ленивой) инициализации свойств. Это означает, что значение ленивого свойства не вычисляется до первого обращения к нему. Ленивые свойства особенно полезны, когда вычисление значения свойства требует значительных ресурсов или зависит от внешних факторов, которые могут измениться в будущем.Свойство с модификатором
lazy инициализируется только при первом доступе к нему. До этого момента память для свойства не выделяется и код инициализатора не выполняется.Ленивое свойство всегда должно быть объявлено как переменная (var), так как его значение может измениться после инициализации.
Ленивые свойства полезны, когда инициализация значения свойства требует больших затрат ресурсов или сложных вычислений, и нет необходимости выполнять их до первого обращения к свойству.
class DataProcessor {
lazy var expensiveData: [String] = {
// Симуляция затратной операции
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()
init() {
print("DataProcessor инициализирован")
}
}
let processor = DataProcessor()
// expensiveData еще не инициализировано
print("Перед доступом к expensiveData")
print(processor.expensiveData) // Инициализация происходит здесь
print("После доступа к expensiveData")Вывод
DataProcessor инициализирован
Перед доступом к expensiveData
Инициализация expensiveData
["Data1", "Data2", "Data3"]
После доступа к expensiveData
Ленивые свойства помогают избегать ненужных затрат на инициализацию до тех пор, пока свойство не будет действительно использовано.
Иногда свойства зависят от внешних данных или состояний, которые могут быть недоступны или неинициализированы на момент создания объекта. Ленивые свойства позволяют отложить инициализацию до тех пор, пока данные или состояния не будут доступны.
Ленивые свойства могут упростить инициализаторы классов и структур, позволяя избежать сложных вычислений или загрузок данных в момент создания объекта.
Будьте осторожны при использовании ленивых свойств в многопоточных приложениях, так как одновременный доступ к ленивому свойству из разных потоков может привести к некорректному поведению или состоянию гонки.
Если ленивое свойство должно быть только для чтения, его можно объявить как приватную переменную с ленивой инициализацией и предоставить публичное свойство только для чтения.
class DataProcessor {
private lazy var _expensiveData: [String] = {
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()
var expensiveData: [String] {
return _expensiveData
}
}
let processor = DataProcessor()
print(processor.expensiveData) // Инициализация происходит здесьСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Ленивые свойства (использующие модификатор
lazy) не являются потокобезопасными по умолчанию. Это означает, что если к ленивому свойству обращаются несколько потоков одновременно, это может привести к состоянию гонки (race condition), когда несколько потоков одновременно пытаются инициализировать одно и то же свойство.Когда несколько потоков одновременно обращаются к ленивому свойству, может возникнуть ситуация, когда несколько из них попытаются инициализировать его одновременно. Это может привести к неопределенному поведению и, в худшем случае, к краху приложения. В этом примере, если два потока одновременно обращаются к
expensiveData, может возникнуть состояние гонки при инициализации свойства.class DataProcessor {
lazy var expensiveData: [String] = {
print("Инициализация expensiveData")
return ["Data1", "Data2", "Data3"]
}()
}
let processor = DataProcessor()
DispatchQueue.global().async {
print(processor.expensiveData)
}
DispatchQueue.global().async {
print(processor.expensiveData)
}Чтобы сделать ленивую инициализацию потокобезопасной, можно использовать синхронизацию. Один из способов — использовать последовательную очередь (
DispatchQueue) для обеспечения эксклюзивного доступа к ленивому свойству во время его инициализации. В этом примере используется последовательная очередь для синхронизации доступа к ленивому свойству, что гарантирует, что только один поток будет инициализировать свойство.class DataProcessor {
private var _expensiveData: [String]?
private let queue = DispatchQueue(label: "com.example.dataProcessorQueue")
var expensiveData: [String] {
return queue.sync {
if _expensiveData == nil {
print("Инициализация expensiveData")
_expensiveData = ["Data1", "Data2", "Data3"]
}
return _expensiveData!
}
}
}
let processor = DataProcessor()
DispatchQueue.global().async {
print(processor.expensiveData)
}
DispatchQueue.global().async {
print(processor.expensiveData)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Связанный список позволяет эффективно добавлять и удалять элементы в начале или середине списка (\(O(1)\) времени при наличии ссылки на узел), в то время как в массиве это занимает \(O(n)\) времени.
LinkedList<Integer> list = new LinkedList<>();
list.addFirst(1); // O(1)
list.addLast(2); // O(1)
list.add(1, 3); // O(1) при наличии ссылки на узел
list.remove(1); // O(1) при наличии ссылки на узел
Связанный список автоматически расширяется и сжимается, в отличие от массивов.
Часто используется для реализации очередей и стеков, где важны операции добавления и удаления с начала или конца списка.
LinkedList<Integer> queue = new LinkedList<>();
queue.addLast(1); // Enqueue
queue.addLast(2);
queue.removeFirst(); // Dequeue
LinkedList<Integer> stack = new LinkedList<>();
stack.addFirst(1); // Push
stack.addFirst(2);
stack.removeFirst(); // Pop
Используется для реализации хеш-таблиц и графов.
Эффективен, когда размер элементов или узлов варьируется, так как не требует непрерывного блока памяти.
Реализация LRU-кеша (Least Recently Used cache):
class LRUCache<K, V> {
private final int capacity;
private final LinkedHashMap<K, V> map;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
};
}
public V get(K key) {
return map.getOrDefault(key, null);
}
public void put(K key, V value) {
map.put(key, value);
}
} Реализация двусторонних очередей (Deque):
LinkedList<Integer> deque = new LinkedList<>();
deque.addFirst(1); // Add to front
deque.addLast(2); // Add to back
deque.removeFirst(); // Remove from front
deque.removeLast(); // Remove from back
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3
Это важная часть разработки iOS приложений, так как утечки памяти могут привести к ухудшению производительности приложения и его аварийным завершениям.
Слабые ссылки (Weak References)
Используйте
weak для ссылок, которые могут стать nil в какой-то момент времени. Это предотвращает удержание объектов в памяти, когда они больше не нужны.Неустранимые ссылки (Unowned References)
Используйте
unowned для ссылок, которые должны существовать на протяжении всего времени жизни объекта, к которому они принадлежат. class Person {
var name: String
weak var friend: Person?
init(name: String) {
self.name = name
}
} Циклические ссылки возникают, когда два объекта имеют сильные ссылки друг на друга, что предотвращает освобождение их из памяти. Использование слабых и неустранимых ссылок помогает избежать этой проблемы.
class A {
var b: B?
}
class B {
weak var a: A?
} Замыкания (closures) могут захватывать ссылки на объекты, что может привести к циклическим ссылкам. Используйте списки захвата (
capture lists), чтобы указать, как должны захватываться ссылки. class ViewController: UIViewController {
var name: String = "ViewController"
func setupClosure() {
let closure = { [weak self] in
print(self?.name ?? "No name")
}
closure()
}
} Leaks
Инструмент для обнаружения утечек памяти. Позволяет увидеть, какие объекты не освобождаются из памяти.
Allocations
Инструмент для отслеживания распределения памяти и анализа использования памяти.
Использование Leaks в Instruments
Product > Profile или нажмите Command + I.Leaks и начните запись.Встроенный инструмент в Xcode, который позволяет визуально анализировать граф объектов и выявлять утечки памяти.
Использование Memory Graph Debugger
Debug > View Memory Graph Hierarchy.Регулярно используйте инструменты профилирования во время разработки, чтобы выявлять и устранять утечки памяти на ранних стадиях.
Всегда проверяйте замыкания на наличие захватов ссылок, особенно в асинхронных задачах и анимациях.
Утечки памяти могут проявляться по-разному на симуляторах и реальных устройствах. Тестируйте приложение на реальных устройствах для более точных результатов.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Могут возникать, когда два объекта удерживают сильные ссылки друг на друга, что препятствует их освобождению из памяти. Замыкания (closures) могут захватывать ссылки на объекты, что также может приводить к циклическим ссылкам. Capture list (список захвата) в замыканиях помогает предотвратить такие циклические ссылки, позволяя явно указать, как замыкание должно захватывать ссылки на объекты.
Это механизм, который используется в замыканиях для указания, как должны захватываться ссылки на объекты. Capture list позволяет указать, должны ли захватываемые ссылки быть сильными (strong) или слабыми (weak), или неустранимыми (unowned).
Capture list указывается в квадратных скобках в начале замыкания. Каждый элемент списка захвата указывает захватываемую переменную и желаемый семантический тип захвата (например,
weak или unowned).Использование слабых ссылок предотвращает удержание объектов в памяти замыканием, что предотвращает циклические ссылки. Замыкание захватывает
self с помощью слабой ссылки (weak self). Слабая ссылка не увеличивает счетчик ссылок на self, поэтому объект MyClass может быть освобожден из памяти, когда на него больше нет других сильных ссылок.class MyClass {
var name: String = "MyClass"
func setupClosure() {
let closure = { [weak self] in
guard let self = self else { return }
print(self.name)
}
closure()
}
}
var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Объект освобождается, так как замыкание не удерживает его в памятиИспользование неустранимых ссылок предполагает, что захваченный объект будет существовать на протяжении всего времени жизни замыкания. Если объект освобождается раньше времени, доступ к неустранимой ссылке приведет к краху приложения. Замыкание захватывает
self с помощью неустранимой ссылки (unowned self). Неустранимая ссылка не увеличивает счетчик ссылок на self, но предполагается, что объект будет существовать на протяжении всего времени жизни замыкания.class MyClass {
var name: String = "MyClass"
func setupClosure() {
let closure = { [unowned self] in
print(self.name)
}
closure()
}
}
var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil // Объект освобождается, но доступ к unowned ссылке после этого приведет к ошибкеСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Не удерживает объект в памяти и автоматически обнуляется, когда объект освобождается. Если попытаться обратиться к объекту через слабую ссылку после его освобождения, ссылка будет иметь значение
nil.class MyClass {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
var obj: MyClass? = MyClass(name: "Example")
weak var weakRef = obj
print(weakRef?.name) // Output: Optional("Example")
obj = nil // Объект освобождается, weakRef становится nil
print(weakRef?.name) // Output: nilТакже не удерживает объект в памяти, но не обнуляется автоматически. Если попытаться обратиться к объекту через неустранимую ссылку после его освобождения, это приведет к аварийному завершению программы (runtime crash), так как ссылка будет указывать на несуществующий объект.
class MyClass {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class AnotherClass {
unowned var myObject: MyClass
init(myObject: MyClass) {
self.myObject = myObject
}
}
var obj: MyClass? = MyClass(name: "Example")
var anotherObj: AnotherClass? = AnotherClass(myObject: obj!)
print(anotherObj?.myObject.name) // Output: Example
obj = nil // Объект освобождается, но anotherObj.myObject не обнуляется
// Попытка доступа к myObject через unowned ссылку после освобождения объекта вызовет runtime crash
print(anotherObj?.myObject.name) // Runtime crashСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Да, это два разных концепта, и асинхронные операции могут быть выполнены без использования многопоточности.
Относится к возможности выполнения операций, не блокируя выполнение других операций. В асинхронном программировании вы можете начать операцию, которая выполняется в фоновом режиме, и продолжить выполнение других задач, не дожидаясь завершения этой операции.
Предполагает выполнение нескольких потоков параллельно. Поток (thread) — это наименьшая единица обработки, которую операционная система может управлять независимо. Многопоточность используется для выполнения нескольких операций одновременно, что может улучшить производительность на многоядерных процессорах.
Операции могут быть реализованы без создания дополнительных потоков. Вместо этого, они могут использовать механизм, называемый кооперативной многозадачностью, где выполнение задач управляется посредством событийного цикла (event loop).
JavaScript в браузере и Node.js используют одно поточный событийный цикл для управления асинхронными операциями. Операции, такие как сетевые запросы или таймеры, выполняются асинхронно, но они не требуют создания дополнительных потоков.
console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 1000);
console.log("End");
// Output:
// Start
// End
// Timeout callback (после 1 секунды)В iOS,
DispatchQueue.main.async позволяет выполнять код асинхронно на главной очереди без создания нового потока. Это часто используется для обновления пользовательского интерфейса. DispatchQueue.main.async {
// Этот код выполняется асинхронно на главной очереди
print("Async task on main queue")
}Асинхронные операции могут быть выполнены с использованием событийного цикла (event loop), который непрерывно проверяет наличие событий (таких как завершение асинхронных задач) и вызывает соответствующие обработчики событий. В этом случае, хотя операции выполняются асинхронно, они обрабатываются последовательно в контексте одного потока.
Нет необходимости в управлении несколькими потоками и синхронизации доступа к общим ресурсам.
Отсутствие переключения контекста между потоками может улучшить производительность.
Только одна операция может выполняться в каждый момент времени в одном потоке, что может быть ограничением на многоядерных системах.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3