Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift можно заставить оператор поддерживать функцию типа
T с помощью перегрузки операторов. Это особенно полезно, когда мы хотим, чтобы оператор мог работать с пользовательскими типами или функциями.Функция в Swift — это тоже тип данных.
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}Тип этой функции
(Int, Int) -> Int
Допустим, у нас есть две функции, и мы хотим, чтобы оператор
+ создавал новую функцию, объединяя их поведение.import Foundation
// Функция типа (Int) -> Int
func double(_ x: Int) -> Int {
return x * 2
}
func increment(_ x: Int) -> Int {
return x + 1
}
// Перегружаем оператор + для функций (Int) -> Int
func + (lhs: @escaping (Int) -> Int, rhs: @escaping (Int) -> Int) -> (Int) -> Int {
return { x in rhs(lhs(x)) } // Сначала вызываем первую, затем вторую
}
// Используем оператор
let combinedFunction = double + increment
print(combinedFunction(3)) // (3 * 2) + 1 = 7
Можно сделать оператор
*, который применяет функцию несколько раз.// Перегружаем оператор * для дублирования применения функции
func * (lhs: @escaping (Int) -> Int, rhs: Int) -> (Int) -> Int {
return { x in
var result = x
for _ in 0..<rhs {
result = lhs(result)
}
return result
}
}
// Используем оператор
let tripleDouble = double * 3
print(tripleDouble(2)) // (2 * 2) * 2 * 2 = 16
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Value-типы хранятся в стеке, создаются копии при передаче. Reference-типы хранятся в куче, передаются по ссылке. Управление памятью для reference-типа осуществляется через ARC. Value-типы быстрее в простых задачах, reference — гибче при сложных связях.
Вот подробные ответы на все вопросы, которые касаются управления памятью, навигации, работы потоков и асинхронности в iOS, а также концепций ARC и утечек:
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Сайт
UITableView (Table View) в iOS используется для отображения списков данных в виде таблицы. Это один из наиболее часто используемых компонентов пользовательского интерфейса в приложениях iOS, так как позволяет организовывать и представлять большие объемы информации в удобном для пользователя формате.UITableView позволяет отображать большие списки данных, которые могут быть прокручиваемыми. Это особенно полезно для отображения динамически загружаемых данных, таких как ленты новостей, списки контактов, каталоги товаров и т.д.UITableView предоставляет методы для добавления, удаления и обновления строк данных, что упрощает управление динамическими списками. Он также поддерживает анимацию изменений, что улучшает пользовательский опыт.UITableView позволяет использовать кастомные ячейки для отображения данных, предоставляя гибкость в создании пользовательского интерфейса. Это можно сделать путем создания пользовательских классов ячеек и настройки их внешнего вида.UITableView поддерживает разделение данных на секции с заголовками и подзаголовками, что упрощает организацию и навигацию по спискам данных.Основной элемент, используемый для отображения строки данных. Ячейки могут быть стандартными или кастомными.
UITableView может содержать несколько секций, каждая из которых может содержать несколько строк. Секции могут иметь заголовки и подзаголовки.UITableView предоставляет методы для обновления данных, такие как reloadData(), которые перезагружают всю таблицу, и методы для обновления отдельных строк или секций с анимацией.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊15
- Использует реиспользуемые ячейки (dequeueReusableCell) для оптимизации памяти.
- Работает с делегатами (UITableViewDelegate) и источниками данных (UITableViewDataSource) для динамического обновления контента.
- Поддерживает разделы, перетаскивание, редактирование.
- Может быть обновлен с анимацией (reloadData, performBatchUpdates).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это высокоуровневый механизм синхронизации, который объединяет взаимное исключение (mutex) и условные переменные (condition variables) для управления доступом к объектам в многопоточной среде.
Только один поток может выполнить защищенный блок кода в любой момент времени.
Позволяют потокам ожидать определенных условий, а другим потокам уведомлять их о наступлении этих условий.
class ThreadSafeClass {
private var internalState = 0
private let queue = DispatchQueue(label: "com.example.threadSafeQueue")
func increment() {
queue.sync {
internalState += 1
}
}
func getState() -> Int {
return queue.sync {
internalState
}
}
}С
NSLockclass ThreadSafeClass {
private var internalState = 0
private let lock = NSLock()
func increment() {
lock.lock()
internalState += 1
lock.unlock()
}
func getState() -> Int {
lock.lock()
let state = internalState
lock.unlock()
return state
}
}С
objc_sync_enter и objc_sync_exitclass ThreadSafeClass: NSObject {
private var internalState = 0
func increment() {
objc_sync_enter(self)
internalState += 1
objc_sync_exit(self)
}
func getState() -> Int {
objc_sync_enter(self)
let state = internalState
objc_sync_exit(self)
return state
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) гласит:
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
class MySQLDatabase {
func fetchData() -> String {
return "Данные из MySQL"
}
}
class DataManager {
let database = MySQLDatabase() // Прямая зависимость от MySQL
func getData() -> String {
return database.fetchData()
}
}Вводим абстракцию (Протокол)
protocol Database {
func fetchData() -> String
}Реализуем конкретные базы данных
class MySQLDatabase: Database {
func fetchData() -> String {
return "Данные из MySQL"
}
}
class PostgreSQLDatabase: Database {
func fetchData() -> String {
return "Данные из PostgreSQL"
}
}Используем абстракцию в
DataManagerclass DataManager {
private let database: Database // Зависимость от абстракции
init(database: Database) {
self.database = database
}
func getData() -> String {
return database.fetchData()
}
}Использование
let mySQLDataManager = DataManager(database: MySQLDatabase())
print(mySQLDataManager.getData()) // "Данные из MySQL"
let postgreSQLDataManager = DataManager(database: PostgreSQLDatabase())
print(postgreSQLDataManager.getData()) // "Данные из PostgreSQL"
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1💊1
Если оставить контекст в замыкании, не принимая во внимание возможные проблемы, это может привести к нескольким серьезным проблемам, особенно в многопоточном и асинхронном программировании.
Одной из самых распространенных проблем является утечка памяти из-за циклов удержания (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
👍1
ARC вставляет вызовы retain и release во время компиляции, когда создаются или уничтожаются сильные ссылки. Это работает прозрачно и синхронно, без необходимости вручную управлять памятью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift (и во многих других языках программирования) по умолчанию числовой литерал с плавающей запятой интерпретируется как `Double`, а не
Float. Double имеет 64 бита, а Float – 32 бита. Это значит, что Double может хранить более точные значения, что особенно важно при математических вычислениях.Большинство API и стандартных библиотек Swift (например,
sin(), cos(), pow()) работают именно с Double. Например:
let x = 3.14 // По умолчанию это Double
let y = sin(x) // sin() принимает Double
На современных 64-битных процессорах операции с
Double выполняются так же быстро или даже быстрее, чем с Float, из-за оптимизаций в аппаратном обеспечении.Float может округлять числа с потерей точности, что может привести к неожиданным результатам. Пример ошибки округления в
Float:let a: Float = 0.1 + 0.2
print(a == 0.3) // false 😱
Если всё же нужен
Float, надо указать это явно:let number: Float = 3.14
или
let number = 3.14 as Float
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это паттерны, связанные с созданием объектов. Они управляют созданием экземпляров, чтобы сделать код более гибким, масштабируемым и независимым от конкретных классов. Примеры: Factory Method, Abstract Factory, Builder, Singleton, Prototype.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift переменные (свойства), объявленные в протоколе, должны указывать:
Если свойство объявлено как
{ get }, класс или структура, реализующая протокол, должна предоставить как минимум геттерprotocol Animal {
var name: String { get } // Только чтение
}
struct Dog: Animal {
let name = "Барсик" // Реализуем только get
}
let dog = Dog()
print(dog.name) // "Барсик"Можно также использовать вычисляемое свойство:
struct Cat: Animal {
var name: String {
return "Мурзик"
}
}Если свойство
{ get set }, класс или структура обязательно должны предоставить и get, и set.protocol Vehicle {
var speed: Int { get set } // Чтение и запись
}
class Car: Vehicle {
var speed: Int = 100 // Реализуем и get, и set
}
let car = Car()
car.speed = 120 // Можно изменить значение
print(car.speed) // 120Вычисляемое свойство тоже подойдёт, если оно имеет
get и set:class Bike: Vehicle {
private var internalSpeed = 50
var speed: Int {
get { return internalSpeed }
set { internalSpeed = newValue }
}
}Если свойство должно быть общим для всех экземпляров (не индивидуальным), то оно объявляется
static.protocol Config {
static var appVersion: String { get }
}
struct AppSettings: Config {
static let appVersion = "1.0.0"
}
print(AppSettings.appVersion) // "1.0.0"Класс может использовать
class var, если свойство можно переопределять в подклассах:class AppInfo: Config {
class var appVersion: String {
return "2.0.0"
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Паттерн стратегия позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми. Таким образом, поведение объекта может изменяться во время выполнения без изменения его класса.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В 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
Добавление задачи в concurrent queue через .sync означает:
- Задача выполняется синхронно — вызывающий поток ждёт завершения.
- Даже если очередь параллельная, .sync блокирует текущий поток до завершения блока.
Это может привести к deadlock'у, если .sync вызван изнутри той же очереди.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift все типы данных делятся на два вида по семантике хранения и передачи:
Структуры (
struct), перечисления (enum) и кортежи (tuple) передаются по значению, то есть каждое присваивание создаёт копию объекта. struct User {
var name: String
}
var user1 = User(name: "Alice")
var user2 = user1 // Копия!
user2.name = "Bob"
print(user1.name) // "Alice" (НЕ изменилось)
print(user2.name) // "Bob"Классы (
class), акторы (actor) и замыкания (closure) передаются по ссылке, то есть несколько переменных могут ссылаться на один и тот же объект. class User {
var name: String
init(name: String) {
self.name = name
}
}
var user1 = User(name: "Alice")
var user2 = user1 // Передача ссылки!
user2.name = "Bob"
print(user1.name) // "Bob" (ИЗМЕНИЛОСЬ!)
print(user2.name) // "Bob"Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Points (pt) – абстрактные единицы измерения в iOS, независимые от физического разрешения экрана.
- Pixels (px) – физические точки дисплея, отображающие контент.
На Retina-дисплеях 1 точка (pt) может состоять из 2×2 или 3×3 пикселей (px) (@2x, @3x). Это позволяет поддерживать четкость интерфейса на разных устройствах.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Это ключевой механизм ООП, позволяющий классам наследовать свойства, методы и другие характеристики от других классов. Это позволяет создавать новые классы на основе существующих, расширяя их функциональность или изменяя её.
Базовый класс определяет общие свойства и методы, которые могут быть унаследованы подклассами.
Подкласс наследует (или "расширяет") базовый класс. Он может переопределять унаследованные методы и свойства, добавлять новые методы и свойства, а также добавлять инициализаторы или изменять существующие.
Подклассы могут переопределять методы, свойства и индексаторы базового класса для изменения или расширения их поведения.
Можно предотвратить переопределение методов, свойств или индексаторов с помощью ключевого слова
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