Приложения, написанные на 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
на стеке, если используются как локальные переменные.
в куче, если они:
— вложены в класс,
— захвачены замыканием,
— передаются как inout,
— возвращаются из функции и живут дольше текущего контекста.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
Напоминаю, что в честь релиза запускаем акцию.
Первые 500 покупателей получат:
🚀 Скидку 50% на PRO тариф на 1 год
🎁 Подарок ценностью 5000₽ для тех, кто подписан на этот канал
🔔 Подпишитесь на этот канал: https://t.me/+b2fZN17A9OQ3ZmJi
В нем мы опубликуем сообщение о релизе в первую очередь
Please open Telegram to view this post
VIEW IN TELEGRAM
Перед вызовом
layoutIfNeeded() или layoutSubviews() в iOS-приложении, нужно изменить значение констрейнта и вызвать анимацию, если необходимо. Вот основные шаги:Перед вызовом
layoutIfNeeded(), измените свойство констрейнта (например, constant у NSLayoutConstraint).class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.changeButtonHeight()
}
}
func changeButtonHeight() {
heightConstraint.constant = 100 // Меняем значение констрейнта
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded() // Перестраиваем макет
}
}
}Если нужно удалить/добавить констрейнты, используйте `activate()` / `deactivate()`.
var expanded = false
@IBOutlet weak var smallHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var largeHeightConstraint: NSLayoutConstraint!
func toggleHeight() {
expanded.toggle()
if expanded {
NSLayoutConstraint.deactivate([smallHeightConstraint])
NSLayoutConstraint.activate([largeHeightConstraint])
} else {
NSLayoutConstraint.deactivate([largeHeightConstraint])
NSLayoutConstraint.activate([smallHeightConstraint])
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
Во
viewDidLoad() элементы ещё не отрисованы, поэтому layoutIfNeeded() не сработает. Используйте viewDidAppear() или вызовите layoutIfNeeded() после view.layoutIfNeeded().override func viewDidLoad() {
super.viewDidLoad()
heightConstraint.constant = 100
view.layoutIfNeeded() // НЕ обновит макет, потому что он ещё не загружен
}Решение
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
heightConstraint.constant = 100
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}Разница между
layoutIfNeeded() и setNeedsLayout()heightConstraint.constant = 100
view.setNeedsLayout() // Обновление произойдет на следующем цикле рендера
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Это механизм в iOS и macOS для обмена сообщениями между различными частями приложения. Он позволяет объектам отправлять и получать уведомления без необходимости напрямую ссылаться друг на друга, что способствует более гибкому и модульному дизайну.
Любой объект может отправить уведомление с помощью Notification Center. Уведомления идентифицируются по имени (
Notification.Name). Можно передать дополнительную информацию в виде словаря (userInfo).NotificationCenter.default.post(name: .myNotification, object: nil)
Объекты могут регистрироваться для получения уведомлений с определенным именем. Для обработки уведомлений используется метод, который будет вызван при получении уведомления.
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification), name: .myNotification, object: nil)
@objc func handleNotification(notification: Notification) {
print("Received notification")
}
Важно удалять наблюдателей, когда они больше не нужны, чтобы избежать утечек памяти. Обычно это делается в методе
deinit или перед уничтожением объекта.NotificationCenter.default.removeObserver(self, name: .myNotification, object: nil)
Объекты не нужно напрямую ссылаться друг на друга, что улучшает модульность кода.
Позволяет легко организовать обмен сообщениями между различными частями приложения.
Уведомления могут использоваться многими объектами, что облегчает реализацию функций, таких как обновление интерфейса или реагирование на события.
Когда данные изменяются, можно отправить уведомление для обновления пользовательского интерфейса.
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
Например, можно подписаться на уведомление о смене состояния сети.
NotificationCenter.default.addObserver(self, selector: #selector(handleNetworkChange), name: .reachabilityChanged, object: nil)
Различные модули приложения могут общаться друг с другом через Notification Center, не создавая сильных зависимостей.
NotificationCenter.default.post(name: .userDidLogin, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: .userDidLogin, object: nil)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2