Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
Да, в функциях часто используются свойства класса. Функции, методы и замыкания могут взаимодействовать со свойствами класса или структуры, чтобы получить доступ к данным или изменить их.
Могут использовать свойства класса для чтения или изменения данных. В этом примере метод
celebrateBirthday использует свойства name и age для обновления возраста и печати сообщения.class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func celebrateBirthday() {
age += 1
print("Happy Birthday, \(name)! You are now \(age) years old.")
}
}
let person = Person(name: "Alice", age: 30)
person.celebrateBirthday()
// Output: "Happy Birthday, Alice! You are now 31 years old."Используют свойства класса для установки начальных значений при создании экземпляра класса.
class Car {
var model: String
var year: Int
init(model: String, year: Int) {
self.model = model
self.year = year
}
func displayInfo() {
print("Car model: \(model), year: \(year)")
}
}
let car = Car(model: "Toyota", year: 2022)
car.displayInfo()
// Output: "Car model: Toyota, year: 2022"Могут захватывать и использовать свойства класса. Это часто используется в асинхронных операциях, таких как сетевые запросы.
class Downloader {
var url: String
init(url: String) {
self.url = url
}
func download(completion: @escaping () -> Void) {
print("Downloading from \(url)...")
DispatchQueue.global().async {
// Симуляция загрузки
sleep(2)
DispatchQueue.main.async {
completion()
}
}
}
}
let downloader = Downloader(url: "https://example.com/file")
downloader.download {
print("Download completed.")
}
// Output:
// "Downloading from https://example.com/file..."
// (2 секунды спустя)
// "Download completed."Используются для данных или функций, которые относятся ко всему классу, а не к конкретному экземпляру. В этом примере статическое свойство
count используется для отслеживания количества созданных экземпляров класса.class Counter {
static var count = 0
init() {
Counter.count += 1
}
static func displayCount() {
print("Number of instances: \(count)")
}
}
let counter1 = Counter()
let counter2 = Counter()
Counter.displayCount()
// Output: "Number of instances: 2"Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍1
Объект может быть освобожден из памяти, даже если на него существует сильная ссылка, при использовании ключевого слова
unowned в некоторых специфических обстоятельствах. Неустранимые ссылки (
unowned) не увеличивают счетчик ссылок на объект, к которому они относятся. В отличие от слабых ссылок (weak), unowned ссылки не обнуляются автоматически, когда объект освобождается. Это означает, что если вы попытаетесь обратиться к объекту через unowned ссылку после его освобождения, это приведет к аварийному завершению программы (runtime crash).Если бы вы попытались обратиться к owner после освобождения john, это привело бы к аварийному завершению программы, так как unowned ссылка не обнуляется и остается указателем на освобожденный объект.class Person {
var name: String
var creditCard: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
var number: Int
unowned var owner: Person
init(number: Int, owner: Person) {
self.number = number
self.owner = owner
}
deinit {
print("CreditCard #\(number) is being deinitialized")
}
}
var john: Person? = Person(name: "John")
john!.creditCard = CreditCard(number: 1234, owner: john!)
john = nil
// Output:
// John is being deinitialized
// CreditCard #1234 is being deinitializedPerson с именем "John" и сильной ссылкой john.CreditCard, который имеет неустранимую ссылку owner на john.john освобождается (при установке john в nil), также освобождается и объект CreditCard.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Может вызывать проблемы, особенно в контексте замыканий, циклических ссылок и асинхронных операций.
Возникают, когда два объекта удерживают друг друга сильными ссылками. Решение: Использовать
weak или unowned ссылки. class Person {
var name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
}
class Apartment {
var unit: String
weak var tenant: Person? // Используем weak
init(unit: String) {
self.unit = unit
}
}Может привести к циклическим ссылкам и утечкам памяти. Решение: Использовать списки захвата (
capture lists) с weak или unowned. class MyViewController: UIViewController {
func loadData() {
someAsyncMethod { [weak self] in
guard let self = self else { return }
print(self.name)
}
}
}Могут завершиться после освобождения объекта, вызвавшего их. Решение: Использовать
weak или unowned ссылки внутри замыканий. class MyViewController: UIViewController {
func fetchData() {
DispatchQueue.global().async { [weak self] in
sleep(2)
DispatchQueue.main.async {
self?.updateUI()
}
}
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это ключевое слово, которое используется для создания неустранимых ссылок. Неустранимая ссылка (
unowned) не увеличивает счётчик ссылок на объект, к которому она относится, и, в отличие от слабых ссылок (weak), не обнуляется автоматически, когда объект освобождается из памяти. Использование unowned ссылок полезно в тех случаях, когда вы уверены, что объект, на который ссылается unowned ссылка, будет существовать на протяжении всей жизни ссылки.Unowned ссылки не удерживают объект в памяти, что предотвращает циклические ссылки.Если объект, на который ссылается
unowned ссылка, освобождается из памяти, попытка доступа к этой ссылке приведёт к ошибке времени выполнения (runtime crash).Unowned ссылки следует использовать только тогда, когда вы уверены, что объект будет существовать на протяжении всей жизни ссылки.class Person {
var name: String
var creditCard: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
var number: Int
unowned var owner: Person
init(number: Int, owner: Person) {
self.number = number
self.owner = owner
}
deinit {
print("CreditCard #\(number) is being deinitialized")
}
}
var john: Person? = Person(name: "John")
john!.creditCard = CreditCard(number: 1234, owner: john!)
john = nil
// Output:
// John is being deinitialized
// CreditCard #1234 is being deinitializedPerson с именем "John".CreditCard, который имеет unowned ссылку owner на john.john освобождается (при присвоении nil), также освобождается и объект CreditCard. Случаи использования unownedВы уверены, что объект будет существовать на протяжении всей жизни ссылки.
unowned позволяет избежать использования опционалов (Optional), что упрощает код.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Используется для отображения списков данных и оптимизирует производительность с помощью повторного использования ячеек.
tableView(_:numberOfRowsInSection:): Количество строк.tableView(_:cellForRowAt:): Конфигурация ячеек.tableView(_:didSelectRowAt:): Обработка выбора строки.tableView(_:heightForRowAt:): Настройка высоты строки.Ячейки (
UITableViewCell) отображают контент.Добавьте
UITableView в ViewController через Interface Builder или программно. Подключите UITableView как IBOutlet.Назначьте
ViewController источником данных и делегатом таблицы. Реализуйте методы протоколов. import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1000 // Количество строк
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected row: \(indexPath.row)")
}
}
Используйте
dequeueReusableCell(withIdentifier:for:) для повторного использования ячеек.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2