- Списки, словари, множества и массивы в стандартной библиотеке не потокобезопасны, если несколько потоков одновременно читают и пишут в них.
- Это приводит к гонке данных, крашам, повреждению состояния или утечкам памяти.
- Часто это проявляется при параллельной модификации коллекций или одновременном доступе без синхронизации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Обеспечение безопасности работы с историей изображений в 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
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Swift словарь (
Dictionary) представляет собой структуру данных, которая хранит пары ключ-значение и обеспечивает быстрый доступ к значениям по ключу (O(1) в среднем). Но если бы мы хотели реализовать словарь на основе массива, то пришлось бы использовать линейный поиск, что делает операции менее эффективными (O(n)). Один из простых способов создать словарь на базе массива — использовать массив кортежей
(ключ, значение). struct ArrayDictionary<Key: Equatable, Value> {
private var items: [(Key, Value)] = [] // Храним пары ключ-значение
// Получение значения по ключу
func value(for key: Key) -> Value? {
for (k, v) in items {
if k == key {
return v
}
}
return nil
}
// Добавление или обновление значения
mutating func insert(value: Value, for key: Key) {
for i in 0..<items.count {
if items[i].0 == key {
items[i].1 = value // Обновляем значение, если ключ уже есть
return
}
}
items.append((key, value)) // Добавляем новую пару
}
// Удаление элемента по ключу
mutating func remove(for key: Key) {
items.removeAll { $0.0 == key }
}
}
// Пример использования
var myDict = ArrayDictionary<String, Int>()
myDict.insert(value: 42, for: "age")
myDict.insert(value: 30, for: "height")
print(myDict.value(for: "age")!) // 42
myDict.remove(for: "age")
print(myDict.value(for: "age")) // nilЕсли мы отсортируем массив по ключам, можно использовать бинарный поиск (O(log n)) для ускорения поиска ключа.
struct SortedArrayDictionary<Key: Comparable, Value> {
private var items: [(Key, Value)] = []
// Бинарный поиск индекса ключа
private func index(of key: Key) -> Int? {
var left = 0
var right = items.count - 1
while left <= right {
let mid = (left + right) / 2
if items[mid].0 == key {
return mid
} else if items[mid].0 < key {
left = mid + 1
} else {
right = mid - 1
}
}
return nil
}
// Получение значения по ключу
func value(for key: Key) -> Value? {
if let index = index(of: key) {
return items[index].1
}
return nil
}
// Вставка с сохранением сортировки
mutating func insert(value: Value, for key: Key) {
if let index = index(of: key) {
items[index].1 = value
} else {
items.append((key, value))
items.sort { $0.0 < $1.0 } // Сортируем после вставки
}
}
}
// Использование
var sortedDict = SortedArrayDictionary<String, Int>()
sortedDict.insert(value: 50, for: "b")
sortedDict.insert(value: 20, for: "a")
sortedDict.insert(value: 70, for: "c")
print(sortedDict.value(for: "b")!) // 50Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Массив может содержать только один тип данных (например, [String], [Int]).
Однако, можно хранить разные типы данных, если использовать:
- Any – массив [Any] может содержать String, Int, Double, но требует явного приведения типов.
- protocol – если все элементы реализуют общий протокол ([CustomProtocol]).
- enum с ассоциативными значениями – позволяет хранить разные типы в одном контейнере (enum DataType { case string(String), int(Int) }).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍5
Приложения, написанные на Swift, могут быть довольно большими по размеру.
Swift — это относительно новый язык, и его стандартная библиотека не встроена в iOS (как, например, Objective-C runtime). Это значит, что при компиляции приложения в его бинарь включаются стандартные Swift-библиотеки.
При публикации в App Store, Xcode компилирует приложение для нескольких архитектур (arm64, armv7, x86_64). Это называется Fat Binary — один исполняемый файл включает версии для разных процессоров.
С версии Swift 5.0 ABI (Application Binary Interface) стабилизирован. Это означает, что в iOS 12.2+ уже есть встроенные Swift-библиотеки.
SwiftUI и Combine — новые технологии, они не так оптимизированы, как UIKit. При их использовании код разрастается за счёт декларативного подхода и дополнительной логики от Apple.
Если приложение использует локализацию, изображения, шрифты, CoreML, ARKit, это тоже увеличивает размер .ipa.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
1. Асинхронность: Управляет задачами так, чтобы они не блокировали выполнение других операций, независимо от числа потоков.
2. Многопоточность: Использует несколько потоков для параллельного выполнения задач.
Асинхронность может быть реализована на одном потоке, например, с помощью событийного цикла.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
NSManagedObjectID – это уникальный идентификатор объекта в Core Data, который остаётся неизменным на протяжении всего жизненного цикла объекта. Используем
uriRepresentation() – это строка (URL), которая уникально идентифицирует объект. func saveObjectID(_ object: NSManagedObject) {
let objectID = object.objectID.uriRepresentation().absoluteString
UserDefaults.standard.set(objectID, forKey: "savedObjectID")
}1. Получаем
URL из UserDefaults. 2. Преобразуем
URL в NSManagedObjectID. 3. Загружаем объект из Core Data.
func fetchSavedObjectID(context: NSManagedObjectContext) -> NSManagedObject? {
guard let objectIDString = UserDefaults.standard.string(forKey: "savedObjectID"),
let objectURL = URL(string: objectIDString) else { return nil }
let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: objectURL)
if let objectID = objectID {
return context.object(with: objectID)
}
return nil
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Реляционные БД (например, PostgreSQL, MySQL) используют таблицы, строгую схему и SQL. Нереляционные БД (например, MongoDB, Redis) хранят данные в формате документов, графов или пар "ключ-значение" и лучше подходят для гибких или масштабируемых структур.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Паттерны проектирования – это готовые решения частых задач, улучшающие структуру кода, его поддержку и масштабируемость.
Помогают упростить создание объектов и сделать его гибким.
Определяет интерфейс для создания объекта, но поручает подклассам выбрать его тип.
protocol Button {
func press()
}
class iOSButton: Button {
func press() { print("iOS button pressed") }
}
class AndroidButton: Button {
func press() { print("Android button pressed") }
}
class ButtonFactory {
static func createButton(for os: String) -> Button {
return os == "iOS" ? iOSButton() : AndroidButton()
}
}
let button = ButtonFactory.createButton(for: "iOS")
button.press() // "iOS button pressed"Гарантирует, что у класса будет только один экземпляр.
class Database {
static let shared = Database() // Единственный экземпляр
private init() { }
func query() { print("Запрос в базу данных") }
}
Database.shared.query()Позволяет пошагово создавать сложные объекты.
class Burger {
var cheese = false
var bacon = false
}
class BurgerBuilder {
private var burger = Burger()
func addCheese() -> Self {
burger.cheese = true
return self
}
func addBacon() -> Self {
burger.bacon = true
return self
}
func build() -> Burger {
return burger
}
}
let myBurger = BurgerBuilder().addCheese().addBacon().build()
print(myBurger.cheese) // true
print(myBurger.bacon) // trueОпределяют удобные способы связи между объектами.
Позволяет совместить несовместимые интерфейсы.
protocol EuropeanSocket {
func provide220V()
}
class EuropeanPlug: EuropeanSocket {
func provide220V() { print("220V подано") }
}
class USPlug {
func provide110V() { print("110V подано") }
}
// Адаптер для американской вилки
class USAdapter: EuropeanSocket {
private let usPlug: USPlug
init(usPlug: USPlug) { self.usPlug = usPlug }
func provide220V() {
usPlug.provide110V()
print("Адаптация до 220V")
}
}
let adapter = USAdapter(usPlug: USPlug())
adapter.provide220V()
// "110V подано"
// "Адаптация до 220V"Динамически добавляет объекту новое поведение.
protocol Coffee {
func cost() -> Int
}
class SimpleCoffee: Coffee {
func cost() -> Int { return 100 }
}
// Декоратор "Молоко"
class MilkDecorator: Coffee {
private let coffee: Coffee
init(_ coffee: Coffee) { self.coffee = coffee }
func cost() -> Int {
return coffee.cost() + 30
}
}
let coffee = MilkDecorator(SimpleCoffee())
print(coffee.cost()) // 130Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍3
В зависимости от платформы:
- GCD (Grand Central Dispatch) — диспетчер задач в iOS.
- Operation и OperationQueue.
- NSThread (устаревшее).
- DispatchQueue.async / sync.
- Thread, RunLoop.
- coroutine-подходы в других языках (например, Kotlin, Python).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔3
В Swift есть несколько способов отлавливать и диагностировать ошибки в коде:
Используется, если функция генерирует ошибку (
throws). enum LoginError: Error {
case wrongPassword
case userNotFound
}
func login(user: String, password: String) throws {
if user != "admin" { throw LoginError.userNotFound }
if password != "1234" { throw LoginError.wrongPassword }
}
do {
try login(user: "admin", password: "wrong")
} catch LoginError.wrongPassword {
print("Ошибка: Неверный пароль")
} catch {
print("Ошибка: \(error)")
}Эти функции прерывают выполнение программы, если что-то пошло не так.
assert() (только в Debug) let age = -5
assert(age >= 0, "Возраст не может быть отрицательным")
precondition() (работает в Release) precondition(age >= 0, "Возраст не может быть отрицательным")
fatalError() (прерывает программу) func getData() -> String {
fatalError("Метод ещё не реализован")
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Это термины из управления памятью в Objective-C, но понимание их полезно и в Swift:
- assign — простое присваивание без увеличения счётчика ссылок. Используется для значимых типов (int, float, struct) или для слабых ссылок.
- retain — увеличивает счётчик ссылок (reference count), предотвращая уничтожение объекта. Используется для ссылочных типов (классов).
В Swift аналогом являются сильные (strong), слабые (weak) и некопируемые (unowned) ссылки.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Фреймы (frames) в iOS-разработке используются для задания размеров и расположения элементов интерфейса (UIView) вручную.
Когда нужно анимировать движение элемента, проще всего работать с его
frame, так как он напрямую управляет origin (координаты) и size (размеры). UIView.animate(withDuration: 0.5) {
self.button.frame.origin.x += 100
}Если вы не используете Auto Layout или хотите задать положение элементов программно,
frame позволяет точно указать размеры и координаты. let button = UIButton(type: .system)
button.frame = CGRect(x: 50, y: 100, width: 200, height: 50)
button.setTitle("Нажми меня", for: .normal)
view.addSubview(button)
При динамической подгрузке ячеек в
UITableView или UICollectionView можно вручную вычислять frame для ускорения работы, вместо использования Auto Layout, который может замедлить скроллинг.При рисовании или настройке слоев
CALayer используется frame, чтобы точно определить размеры слоя. let borderLayer = CALayer()
borderLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
borderLayer.borderWidth = 2
borderLayer.borderColor = UIColor.red.cgColor
view.layer.addSublayer(borderLayer)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Если все переменные в коде сделать
weak, объекты могут мгновенно удаляться из памяти, потому что никто не будет владеть ими (strong reference). -
weak создает слабую ссылку – объект не увеличивает счетчик ссылок (retain count). - Если нет других (strong) ссылок на объект, он удаляется (ARC освобождает память).
-
weak переменные всегда являются Optional, потому что объект может стать nil в любой момент. class Person {
weak var name: String? // ❌ Ошибка! Строки – это value type, weak нельзя
}Проблема, если
weak используется вездеclass Car {
weak var model: String? // ❌ Ошибка (value type)
weak var owner: Person? // ⚠️ Будет nil, если нет других strong ссылок
}
class Person {
var car: Car?
}
var person: Person? = Person()
person?.car = Car()
person?.car?.owner = person // ❌ `owner` - weak, объект сразу удалится
print(person?.car?.owner) // nil, объект Person уничтоженНапример, между
delegate и ViewController protocol SomeDelegate: AnyObject {
func doSomething()
}
class ViewController {
weak var delegate: SomeDelegate? // ✅ Prevent retain cycle
}Например, ячейки в
UITableView не должны владеть ViewController.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Stable ABI (Application Binary Interface) означает, что двоичный интерфейс языка стабилен, и можно использовать скомпилированные Swift-библиотеки между разными версиями без перекомпиляции. Это важно для модульности и совместимости на уровне фреймворков.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Garbage Collector (GC) и Automatic Reference Counting (ARC) – это два разных подхода к управлению памятью в программировании. Они решают одну задачу: автоматическое освобождение неиспользуемой памяти, но делают это по-разному.
Java, Kotlin, C#, Python, JavaScript
- GC периодически просматривает всю память приложения и ищет объекты, на которые больше нет ссылок.
- Когда такие объекты находятся, они удаляются, а память освобождается.
- Это автоматический процесс, который запускается по мере необходимости.
Где используется: Swift, Objective-C
- Каждый объект имеет счетчик ссылок (
reference count). - Когда переменная создает ссылку на объект, счетчик увеличивается.
- Когда переменная перестает ссылаться на объект, счетчик уменьшается.
- Когда счетчик достигает нуля, объект удаляется из памяти сразу же.
class Person {
var pet: Pet?
}
class Pet {
var owner: Person?
}
let person = Person()
let pet = Pet()
person.pet = pet
pet.owner = person // Теперь оба объекта держат друг друга, и ARC их не удалитРешение – использовать
weak: class Pet {
weak var owner: Person? // Теперь утечки памяти не будет
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM