В Swift существует несколько видов диспетчеризации, которые определяют, как и когда вызываются методы или функции. Основные виды диспетчеризации включают статическую диспетчеризацию (static dispatch), диспетчеризацию по таблице виртуальных функций (vtable dispatch), диспетчеризацию по свидетелю (witness table dispatch) и динамическую диспетчеризацию (dynamic dispatch). Рассмотрим их подробнее:
Вызов функции или метода определяется на этапе компиляции. Используется для функций и методов, которые не переопределяются в подклассах или не являются динамическими. Для структур, перечислений и final классов.
struct MyStruct {
func printMessage() {
print("Hello from MyStruct")
}
}
let instance = MyStruct()
instance.printMessage() // Вызов определяется на этапе компиляции
Используется для вызова методов класса, которые могут быть переопределены в подклассах. Виртуальная таблица (vtable) используется для определения, какой метод вызывать. Для классов и их подклассов.
class BaseClass {
func printMessage() {
print("Hello from BaseClass")
}
}
class SubClass: BaseClass {
override func printMessage() {
print("Hello from SubClass")
}
}
let instance: BaseClass = SubClass()
instance.printMessage() // Вызов определяется в runtime с использованием vtable
Используется для вызова методов протоколов, реализованных типами. Таблица свидетелей (witness table) используется для определения, какой метод вызывать. Для типов, соответствующих протоколам.
protocol MyProtocol {
func printMessage()
}
struct MyStruct: MyProtocol {
func printMessage() {
print("Hello from MyStruct")
}
}
let instance: MyProtocol = MyStruct()
instance.printMessage() // Вызов определяется в runtime с использованием witness table
Используется для вызова методов, отмеченных как
dynamic или методов Objective-C. Метод определяется в runtime с использованием Objective-C runtime. Для методов, которые должны быть динамически разрешены в runtime, обычно для взаимодействия с Objective-C. import Foundation
class MyClass: NSObject {
@objc dynamic func printMessage() {
print("Hello from MyClass")
}
}
let instance = MyClass()
instance.printMessage() // Вызов определяется в runtime с использованием Objective-C runtime
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Замыкание — это референсный тип. Оно является объектом и передаётся по ссылке. При захвате переменных — сохраняет их в памяти до завершения использования.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥1
В контексте Swift термин Satisfiable (удовлетворяемый) относится к тому, может ли конкретный тип соответствовать (удовлетворять) требованиям протокола.
Протокол считается satisfiable, если его можно реализовать без противоречий. Например, если в протоколе указаны требования, которые могут быть выполнены в классе или структуре.
protocol Animal {
var name: String { get }
func speak()
}
struct Dog: Animal {
var name = "Барсик"
func speak() {
print("Гав-гав")
}
}
let dog = Dog()
dog.speak() // "Гав-гав"Протокол не может быть удовлетворён (то есть, не satisfiable), если он содержит требования, которые невозможно реализовать в конкретном типе.
protocol Impossible {
var value: Self { get } // Self требует, чтобы свойство содержало сам тип
}
struct Example: Impossible {
var value: Example // ОШИБКА: бесконечная рекурсия!
}Если вы видите ошибку Protocol 'X' cannot be satisfied, значит, Swift не может найти способ правильно реализовать все его требования.
Проверьте ассоциированные типы и
Self – они должны корректно ссылаться на реализуемый тип.Проверьте требования протокола – убедитесь, что все свойства и методы могут быть реализованы в классе/структуре.
Попробуйте использовать
any Protocol – если Self создаёт проблемы, возможно, нужно использовать any: func test(value: any Impossible) { } // Позволяет работать с протоколом без строгого SelfСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Зависит от языка:
- В Python или Swift, копирование изменяемого массива создаёт новый массив, но если элементы — ссылки на объекты, то копируются ссылки, а не сами объекты (поверхностная копия).
- В других языках может быть как поверхностная, так и глубокая копия, в зависимости от реализации. Копия — это независимая структура, но её элементы могут по-прежнему ссылаться на одни и те же объекты.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊2
RunLoop — это фундаментальный механизм в iOS и macOS, который управляет циклом обработки событий в приложении. Он отслеживает и обрабатывает входящие события, такие как нажатия клавиш, касания экрана, таймеры и сетевые запросы, и поддерживает приложение в активном состоянии, пока оно не завершится.RunLoop постоянно выполняет цикл, ожидая входящие события и обрабатывая их по мере поступления. Этот цикл состоит из нескольких этапов: ожидание события, обработка события и повтор цикла.RunLoop может работать в разных режимах, которые определяют, какие источники событий будут отслеживаться и обрабатываться. Основные режимы включают default и tracking (для событий отслеживания, таких как прокрутка). В каждой итерации RunLoop обрабатывает события только для текущего режима.RunLoop.current.run(mode: .default, before: Date.distantFuture)
RunLoop может отслеживать различные источники событий, такие как таймеры (Timer), порты (Port), ввод пользователей (такие как касания экрана и клики мыши), а также пользовательские источники (Input Source).RunLoop может управлять таймерами, которые выполняют задачи через определенные интервалы времени. let timer = Timer(timeInterval: 1.0, repeats: true) { _ in
print("Timer fired!")
}
RunLoop.current.add(timer, forMode: .default)RunLoop используется для обработки событий в основном потоке (main thread) приложения. Это особенно важно для поддержания отзывчивости пользовательского интерфейса, поскольку все взаимодействия с UI происходят в основном потоке.import Foundation
class Example {
var timer: Timer?
func startRunLoop() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
RunLoop.current.run()
}
@objc func timerFired() {
print("Timer fired!")
}
}
let example = Example()
example.startRunLoop()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это механизм анимации в SwiftUI, позволяющий плавно анимировать переходы между представлениями, сохраняя визуальную взаимосвязь между элементами, которые логически одинаковые, но физически находятся в разных частях UI.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Обеспечение безопасности работы с историей изображений в iOS-приложениях может включать несколько уровней защиты:
- Используйте Privacy Settings (
NSPhotoLibraryUsageDescription, NSCameraUsageDescription) для контроля доступа к фотоальбому. - Для ограниченного доступа в iOS 14+ используйте
PHPickerViewController. let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
if status == .authorized {
// Доступ есть
} else {
// Запросить разрешение
PHPhotoLibrary.requestAuthorization { newStatus in
if newStatus == .authorized {
print("Доступ получен")
}
}
}
- Если изображения сохраняются локально, используйте AES-шифрование.
- Можно хранить ключ в Keychain или использовать Secure Enclave.
import CommonCrypto
func encryptImage(data: Data, key: Data) -> Data? {
var buffer = Data(count: data.count + kCCBlockSizeAES128)
var numBytesEncrypted: size_t = 0
let status = CCCrypt(
CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding), key.bytes, key.count,
nil, data.bytes, data.count, &buffer, buffer.count, &numBytesEncrypted
)
return status == kCCSuccess ? buffer.prefix(numBytesEncrypted) : nil
}
Core Data с Encrypted Store – можно использовать зашифрованные базы данных, такие как
SQLCipher. FileManager + Data Protection – файлы можно хранить в
Application Support с флагом .completeFileProtection. Keychain – если нужно хранить ссылки на изображения или ключи.
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("secure_image.jpg")
try? imageData.write(to: fileURL, options: .completeFileProtection)- Если изображение чувствительное, отключите кеширование в
URLSession: let config = URLSessionConfiguration.default
config.urlCache = nil // Отключаем кеш
let session = URLSession(configuration: config)
- Убедитесь, что ссылки на изображения не создают retain cycle.
- Используйте weak ссылки в кэше изображений.
class ImageCache {
private let cache = NSCache<NSString, UIImage>()
func store(image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key as NSString)
}
func retrieve(forKey key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
}- Используйте HTTPS вместо HTTP.
- Включите App Transport Security (ATS) в
Info.plist. - Верифицируйте SSL-сертификаты при загрузке.
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if let serverTrust = challenge.protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
static используется для определения методов и свойств, которые принадлежат типу, а не экземпляру. Внутри классов static по сути означает final, так как такие методы нельзя переопределить в подклассах.
final, в свою очередь, может применяться к классам, методам и свойствам, чтобы запретить наследование или переопределение, но не обязательно делает метод или свойство статическим.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Это классификация различных видов данных, которые могут быть использованы и манипулированы в программе. Они определяют, какие операции можно выполнять с данными и как они хранятся в памяти.
Представляют целые числа.
Int, UInlet age: Int = 25
Представляют дробные числа.
Float, Doublelet pi: Double = 3.14159
Представляют логические значения
true или false. Boollet isActive: Bool = true
Представляют отдельные символы.
Characterlet letter: Character = "A"
Представляют последовательности символов.
Stringlet greeting: String = "Hello, World!"
Представляют упорядоченные коллекции элементов одного типа.
Array<T>let numbers: [Int] = [1, 2, 3, 4, 5]
Представляют коллекции пар ключ-значение.
Dictionary<Key, Value>let user: [String: String] = ["name": "Alice", "age": "30"]
Представляют коллекции уникальных элементов.
Set<T>let uniqueNumbers: Set<Int> = [1, 2, 3, 4, 5]
Представляют тип данных с набором связанных значений.
enum enum CompassPoint {
case north
case south
case east
case west
}
Представляют группы связанных значений.
struct struct Person {
var name: String
var age: Int
}
let person = Person(name: "Alice", age: 30)
Представляют объекты с состоянием и поведением.
class class Car {
var model: String
var year: Int
init(model: String, year: Int) {
self.model = model
self.year = year
}
}
let car = Car(model: "Tesla", year: 2021)
Представляют группы нескольких значений различных типов.
(Type1, Type2, ...)let coordinates: (Int, Int) = (10, 20)
Представляют значение, которое может быть либо некоторым значением, либо nil.
Optional<T>var optionalName: String? = "Alice"
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Эти вьюшки не двигаются физически — они находятся в contentView, которая движется вместе с contentOffset.
- Относительно ScrollView они могут визуально выходить за пределы видимости, но физически остаются на месте внутри большого contentSize.
Если вью ушла за пределы видимой области, она не рендерится до тех пор, пока не попадёт в видимую область (оптимизация).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Термин "посылка" может означать разные вещи в зависимости от контекста
Передача данных между экранами (Navigation, Segue, Delegate, Notification)
Передача данных через API (Network Request, JSON, WebSocket)
Передача данных в многопоточной среде (DispatchQueue, OperationQueue)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊6👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Принцип OCP (Open-Closed Principle) гласит:
"Программные сущности должны быть открыты для расширения, но закрыты для модификации."
Это значит, что код должен позволять добавлять новый функционал без изменения существующего кода.
Меньше багов – изменения не ломают старый код.
Лучшая поддержка – новый функционал добавляется без переписывания старого.
Гибкость – можно расширять систему без изменения её базовой логики.
Допустим, у нас есть класс, который рисует фигуры:
class ShapeDrawer {
func draw(shape: String) {
if shape == "circle" {
print("Рисуем круг")
} else if shape == "square" {
print("Рисуем квадрат")
}
}
}Лучше использовать наследование или протоколы, чтобы расширять функциональность, не меняя существующий код:
protocol Drawable {
func draw()
}
class Circle: Drawable {
func draw() {
print("Рисуем круг")
}
}
class Square: Drawable {
func draw() {
print("Рисуем квадрат")
}
}
class ShapeDrawer {
func draw(shape: Drawable) {
shape.draw()
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
– Протоколы — только набор требований, не содержат реализации состояния.
Swift не поддерживает полноценные абстрактные классы, поэтому протоколы часто используются вместо них.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
автоматизированная разработка ПО.
непрерывная разработка приложений.
развёртывание облачных приложений.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊5
Ключевое слово open позволяет классу быть доступным и наследуемым за пределами модуля. Это максимальный уровень доступа и расширяемости в Swift.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7
В iOS существует несколько ленивых (
lazy) контейнеров и вью, которые откладывают создание или загрузку элементов до момента их фактического использования. Обычное свойство инициализируется сразу, а
lazy – только при первом вызове. class Example {
lazy var expensiveObject: Data = {
print("Объект создан!")
return Data()
}()
}
let obj = Example()
print("Объект ещё не создан")
_ = obj.expensiveObject // Только теперь создастсяЕсли вы хотите избежать лишних вычислений, можно использовать ленивую последовательность:
let numbers = (1...1000).lazy.map { $0 * 2 } // Не вычисляется сразу!
print(numbers.first!) // Только теперь вычисляется первый элементВ отличие от обычных
VStack и HStack, ленивые версии создают элементы только при прокрутке.ScrollView {
LazyVStack {
ForEach(0..<1000) { index in
Text("Элемент \(index)")
}
}
}Они работают по принципу переиспользования ячеек, загружая их только когда нужно.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Строка \(indexPath.row)"
return cell
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Элементы массива (Array) в Swift (и других языках) хранятся в непрерывном участке памяти:
- Все значения идут друг за другом — это позволяет быстро получать элементы по индексу (O(1)).
- При увеличении размера массива может происходить перевыделение памяти, чтобы вместить больше элементов.
- В Swift Array — структура с копированием по необходимости (copy-on-write), что делает работу с массивами эффективной и безопасной.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
В языке Swift нет встроенного оператора
future, но если речь идет о концепции Future из асинхронного программирования, то давай разберемся, зачем она нужна и как используется.Future (или Promise в некоторых реализациях) — это объект, который представляет значение, которое станет доступным в будущем после завершения асинхронной операции. Это удобно, когда нужно работать с кодом, который выполняется не мгновенноЗапросы в сеть (API)
Чтение файлов
Долгие вычисления
В Swift
Future чаще всего используется в рамках Combine.В Combine есть структура
Future, которая позволяет создать асинхронную операцию и подписаться на ее результат:import Combine
// Функция, которая возвращает Future
func fetchData() -> Future<String, Error> {
return Future { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
let success = Bool.random() // Симулируем успех или ошибку
if success {
promise(.success("Данные загружены!"))
} else {
promise(.failure(NSError(domain: "Ошибка загрузки", code: -1, userInfo: nil)))
}
}
}
}
// Используем Future
let future = fetchData()
let cancellable = future.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Завершено без ошибок")
case .failure(let error):
print("Ошибка: \(error.localizedDescription)")
}
}, receiveValue: { value in
print("Получены данные: \(value)")
})
Когда нужна одноразовая асинхронная операция (например, запрос в сеть)
Когда используешь Combine и хочешь обернуть асинхронный код в реактивный стиль
Если в будущем планируешь объединять несколько асинхронных операций (композиция
Future)Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Capture list используется в замыканиях Swift, чтобы управлять тем, как значения захватываются: как weak, unowned или strong. Это предотвращает retain cycle и утечки.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2