Могут возникать, когда два объекта удерживают сильные ссылки друг на друга, что препятствует их освобождению из памяти. Замыкания (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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это раздельные понятия, хотя их можно использовать вместе. Асинхронность связана с выполнением задач без блокировки основного потока, когда результат операции может быть обработан позже, что особенно полезно при работе с вводом-выводом. Многопоточность же подразумевает параллельное выполнение задач в нескольких потоках, что позволяет использовать ресурсы процессора более эффективно. Асинхронные операции могут выполняться в одном потоке, тогда как многопоточность обычно задействует несколько потоков одновременно.
Относится к выполнению операций, которые позволяют программе продолжать работать, не дожидаясь завершения этих операций. Асинхронные операции могут быть выполнены без блокировки основного потока, что повышает отзывчивость приложения.
Пример: Отправка сетевого запроса и ожидание ответа. Программа может продолжить выполнение других задач, пока ждет ответа от сервера.
Многопоточность предполагает выполнение нескольких потоков параллельно. Это позволяет использовать несколько ядер процессора для выполнения задач одновременно, что может повысить производительность.
Пример: Распараллеливание вычислений для ускорения обработки данных.
Асинхронные операции могут быть выполнены без создания дополнительных потоков. Например, JavaScript и Node.js используют одно поточный событийный цикл для управления асинхронными операциями. В этом примере
setTimeout выполняет асинхронную операцию в одном потоке.console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 1000);
console.log("End"); Многопоточность может использоваться для выполнения нескольких задач параллельно без необходимости в асинхронности. Например, выполнение вычислительных задач в нескольких потоках без ожидания завершения других задач. В этом примере два потока выполняются параллельно, но операции не являются асинхронными.
let queue1 = DispatchQueue(label: "queue1", qos: .userInitiated)
let queue2 = DispatchQueue(label: "queue2", qos: .userInitiated)
queue1.sync {
for i in 0..<5 {
print("Task 1 - \(i)")
}
}
queue2.sync {
for i in 0..<5 {
print("Task 2 - \(i)")
}
}
Асинхронные операции могут быть выполнены в разных потоках для повышения производительности. Например, асинхронная загрузка данных в фоновом потоке, чтобы не блокировать основной поток пользовательского интерфейса. В этом примере данные загружаются асинхронно в фоновом потоке, а затем результат обновляется в основном потоке.
DispatchQueue.global(qos: .background).async {
// Выполнение фоновой задачи
let data = loadData()
DispatchQueue.main.async {
// Обновление UI после завершения фоновой задачи
updateUI(with: data)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это архитектурный шаблон для iOS-приложений, который обещает улучшить разбиение кода на модули и упростить тестирование. Однако, несмотря на свои преимущества, VIPER может быть воспринят как "боль" по ряду причин.
VIPER разделяет логику приложения на пять отдельных компонентов, что может привести к значительному увеличению количества файлов и сложности структуры проекта. Для одного экрана в приложении могут понадобиться следующие файлы: View, Interactor, Presenter, Entity, Router.
Это значит, что для каждого экрана нужно создавать и поддерживать пять отдельных файлов, что может быстро привести к перегрузке проекта большим количеством файлов.
VIPER требует глубокого понимания каждого из компонентов и их ролей. Новичкам или разработчикам, не знакомым с этим шаблоном, может потребоваться значительное время, чтобы понять и эффективно использовать VIPER.
VIPER требует написания большого количества шаблонного кода, особенно для создания связей между компонентами. Это может привести к усталости разработчика и увеличению времени разработки. Этот шаблонный код нужно будет повторять для каждого модуля, что может быть утомительным и трудоемким.
protocol SomeModuleViewProtocol: AnyObject {
var presenter: SomeModulePresenterProtocol? { get set }
}
protocol SomeModuleInteractorProtocol: AnyObject {
var presenter: SomeModulePresenterProtocol? { get set }
}
protocol SomeModulePresenterProtocol: AnyObject {
var view: SomeModuleViewProtocol? { get set }
var interactor: SomeModuleInteractorProtocol? { get set }
var router: SomeModuleRouterProtocol? { get set }
}
protocol SomeModuleRouterProtocol: AnyObject {
static func createModule() -> SomeModuleViewProtocol
}Так как компоненты VIPER сильно разделены, это может усложнить навигацию и отладку кода. Переход от одного компонента к другому может потребовать больше времени и усилий.
Когда проект растет, количество VIPER-модулей может стать огромным. Это может сделать проект перегруженным и сложным для управления, особенно в крупных командах.
Каждый компонент имеет свою четко определенную роль.
Модули VIPER легко тестировать из-за их четкой структуры и разделения.
Легче переиспользовать и обновлять компоненты.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯6
Да, есть несколько способов для выполнения задач параллельно или одновременно. Это может повысить производительность вашего приложения, особенно если вы выполняете независимые задачи, такие как загрузка данных, обработка изображений или выполнение сетевых запросов.
Предоставляет низкоуровневый интерфейс для управления параллельными задачами, используя глобальные или пользовательские конкурентные очереди.
DispatchQueue.global(qos: .userInitiated).async {
// Первая задача
performFirstTask()
}
DispatchQueue.global(qos: .userInitiated).async {
// Вторая задача
performSecondTask()
}Это более высокоуровневый интерфейс для управления параллельными задачами с возможностью задания зависимостей и приоритетов.
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 2 // Устанавливаем количество одновременно выполняемых задач
let operation1 = BlockOperation {
performFirstTask()
}
let operation2 = BlockOperation {
performSecondTask()
}
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)
Позволяет группировать несколько асинхронных задач и уведомлять, когда все задачи в группе завершены.
let dispatchGroup = DispatchGroup()
DispatchQueue.global(qos: .userInitiated).async(group: dispatchGroup) {
// Первая задача
performFirstTask()
}
DispatchQueue.global(qos: .userInitiated).async(group: dispatchGroup) {
// Вторая задача
performSecondTask()
}
dispatchGroup.notify(queue: .main) {
// Все задачи завершены
updateUI()
}
Это фреймворк для реактивного программирования, который позволяет работать с асинхронными потоками данных и выполнять задачи параллельно.
import Combine
let publisher1 = Just("Task 1").delay(for: 2.0, scheduler: DispatchQueue.global())
let publisher2 = Just("Task 2").delay(for: 2.0, scheduler: DispatchQueue.global())
Publishers.Zip(publisher1, publisher2)
.receive(on: DispatchQueue.main)
.sink { task1, task2 in
print("Both tasks completed: \(task1), \(task2)")
}
.store(in: &cancellables)
Автоматически управляет параллельным выполнением сетевых запросов.
let url1 = URL(string: "https://example.com/data1")!
let url2 = URL(string: "https://example.com/data2")!
let task1 = URLSession.shared.dataTask(with: url1) { data, response, error in
// Обработка первого ответа
}
let task2 = URLSession.shared.dataTask(with: url2) { data, response, error in
// Обработка второго ответа
}
task1.resume()
task2.resume()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
Разделение кода на модули позволяет разработчикам легче понимать и навигировать по коду. Когда код структурирован и разбит на логически связанные части, его проще читать и поддерживать.
View: Отвечает за отображение данных и взаимодействие с пользователем.
Model: Содержит бизнес-логику и данные.
Controller/Presenter/Interactor: Управляет взаимодействием между View и Model.
Когда код разбит на отдельные модули, каждый модуль может быть использован повторно в разных частях приложения или даже в разных проектах. Это способствует созданию более модульных и переиспользуемых компонентов.
Разделение кода на модули упрощает написание и проведение тестов. Модульный код легче тестировать изолированно, что позволяет находить и исправлять ошибки быстрее и эффективнее.
Модульный код
class DataService {
func fetchData() -> [String] {
// Логика получения данных
}
}
class ViewModel {
let dataService: DataService
init(dataService: DataService) {
self.dataService = dataService
}
func getData() -> [String] {
return dataService.fetchData()
}
}Тест
func testFetchData() {
let dataService = MockDataService()
let viewModel = ViewModel(dataService: dataService)
let data = viewModel.getData()
XCTAssertEqual(data, ["MockData1", "MockData2"])
}Когда код структурирован и разбит на модули, разные члены команды могут работать над разными частями проекта одновременно, не мешая друг другу. Это способствует параллельной разработке и уменьшает конфликты слияния (merge conflicts).
Разделение кода на более мелкие, управляемые части помогает снизить общую сложность системы. Каждый модуль решает одну конкретную задачу, что делает систему более понятной и управляемой.
Разделение кода помогает следовать принципам SOLID:
S ingle Responsibility Principle (Принцип единственной ответственности): Каждый модуль отвечает за одну конкретную задачу.
O pen/Closed Principle (Принцип открытости/закрытости): Модули открыты для расширения, но закрыты для модификации.
L iskov Substitution Principle (Принцип подстановки Лисков): Модули можно заменить другими, не нарушая работу системы.
I nterface Segregation Principle (Принцип разделения интерфейсов): Избегайте создания "толстых" интерфейсов; предпочтительно иметь несколько специализированных интерфейсов.
D ependency Inversion Principle (Принцип инверсии зависимостей): Модули зависят от абстракций, а не от конкретных реализаций.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Реактивное программирование упрощает координацию асинхронных операций, избегая "callback hell".
До:
fetchData { data in
process(data) { processedData in
save(processedData) { success in
print("Data saved: \(success)")
}
}
}После:
fetchData()
.flatMap { process($0) }
.flatMap { save($0) }
.subscribe(onNext: { success in
print("Data saved: \(success)")
})
Обеспечивает простое и автоматическое обновление UI при изменении данных.
data.asObservable()
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
cell.textLabel?.text = model
}
.disposed(by: disposeBag)
Позволяет легко объединять данные из разных источников.
Observable.zip(loadUser(), loadPosts())
.subscribe(onNext: { user, posts in
display(posts)
})
Централизованная обработка ошибок в асинхронных цепочках.
fetchData()
.flatMap { process($0) }
.flatMap { save($0) }
.subscribe(onNext: { success in
print("Data saved: \(success)")
}, onError: { error in
print("Error: \(error)")
})
Автоматическая привязка данных к элементам UI.
usernameObservable
.bind(to: usernameLabel.rx.text)
.disposed(by: disposeBag)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Главный поток отвечает за обновление пользовательского интерфейса (UI) и обработку взаимодействия с пользователем. Если этот поток блокируется, приложение может стать неотзывчивым, что вызывает задержки в интерфейсе или даже его полную заморозку.
Если главный поток занят выполнением длительных операций, UI не будет обновляться, что приведет к замораживанию или задержке интерфейса.
Задержки и лаги при взаимодействии с приложением вызывают неудовлетворенность пользователей и могут привести к негативным отзывам.
Длительная блокировка главного потока может привести к тому, что система iOS решит завершить приложение, считая его неотзывчивым.
GCD предоставляет простой способ выполнения асинхронных задач в фоновом режиме.
DispatchQueue.global(qos: .background).async {
// Длительная задача
let result = performHeavyComputation()
DispatchQueue.main.async {
// Обновление UI с результатом
updateUI(with: result)
}
}OperationQueue предлагает более высокоуровневый интерфейс для управления асинхронными задачами с возможностью задания приоритетов и зависимостей.
let backgroundQueue = OperationQueue()
backgroundQueue.addOperation {
// Длительная задача
let result = performHeavyComputation()
OperationQueue.main.addOperation {
// Обновление UI с результатом
updateUI(with: result)
}
}
URLSession автоматически выполняет сетевые запросы в фоновом режиме.
let url = URL(string: "https://example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
// Обработка данных
DispatchQueue.main.async {
// Обновление UI
updateUI(with: data)
}
}
task.resume()
Для выполнения запросов к базе данных можно использовать фоновые контексты (background contexts).
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.perform {
let fetchRequest: NSFetchRequest<Entity> = Entity.fetchRequest()
let results = try? backgroundContext.fetch(fetchRequest)
DispatchQueue.main.async {
// Обновление UI с результатами
updateUI(with: results)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Может быть сложным для реализации из-за необходимости создания множества компонентов даже для простых задач.
Создание пользовательской ячейки
import UIKit
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
func configure(with text: String) {
titleLabel.text = text
}
}
View
import UIKit
protocol TableViewProtocol: AnyObject {
func updateTableView()
}
class TableViewController: UIViewController, TableViewProtocol {
@IBOutlet weak var tableView: UITableView!
var presenter: TableViewPresenterProtocol?
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
presenter?.viewDidLoad()
}
func updateTableView() {
tableView.reloadData()
}
}
extension TableViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter?.numberOfRows() ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
if let text = presenter?.textForRow(at: indexPath) {
cell.configure(with: text)
}
return cell
}
}
Presenter
protocol TableViewPresenterProtocol: AnyObject {
func viewDidLoad()
func numberOfRows() -> Int
func textForRow(at indexPath: IndexPath) -> String
}
class TableViewPresenter: TableViewPresenterProtocol {
weak var view: TableViewProtocol?
var interactor: TableViewInteractorProtocol?
func viewDidLoad() {
interactor?.fetchData()
}
func numberOfRows() -> Int {
return interactor?.data.count ?? 0
}
func textForRow(at indexPath: IndexPath) -> String {
return interactor?.data[indexPath.row] ?? ""
}
}Interactor
protocol TableViewInteractorProtocol: AnyObject {
var data: [String] { get }
func fetchData()
}
class TableViewInteractor: TableViewInteractorProtocol {
var data: [String] = []
func fetchData() {
data = ["Item 1", "Item 2", "Item 3"]
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1