Connect with us

Программирование

Не используйте Struct в Swift подобным образом — это вас замедляет

В этой статье мы обсудим ошибки, которые могут замедлить работу приложения.

Опубликовано

/

     
     

Как мы все знаем, структуры — это типы значений, которые легковесны, быстры и безопасны для создания моделей и DTO (Data Transfer Object). Однако их неправильное использование может повлиять на производительность приложения.

В этой статье мы обсудим ошибки, которые могут замедлить работу приложения.

Цена больших значений в Swift

Как вы знаете, структуры — это типы значений, то есть при передаче или назначении они создают копию.

Проще говоря, когда мы присваиваем значение одной структуры другой, Swift создаёт новую независимую копию этой структуры.

Давайте разберёмся на примере:

struct Apple {
    var color: String
}

var apple1 = Apple(color: "Red")
var apple2 = apple1

Здесь и apple1, и apple2 — независимые объекты; любые изменения в apple1 не влияют на объект apple2.

Класс — это ссылочный тип, любые изменения в apple1 также влияют на apple2, поскольку состояние является общим.

В приведённом выше примере есть только одно свойство с именем color, и при копировании структуры всё работает нормально.

Но что произойдёт, если наша структура станет слишком большой — например, если структура будет содержать много вложенных структур?

Копирование этого объекта может быть затратным и существенно повлиять на производительность.

Давайте разберёмся на примере:

struct UserProfile {
    let name: String
    let bio: String
    let posts: [Post]
    let followers: [Followers]
    let following: [Followings]
}

Здесь вы видите структуру UserProfile, которая содержит множество других структур (таких как публикации, подписчики и подписки).

Давайте используем этот UserProfile где-нибудь.

func updateBio(for profile: UserProfile, with newBio: String) -> UserProfile {
    var newProfile = profile
    newProfile = UserProfile(
        name: profile.name,
        bio: newBio,
        posts: profile.posts,
        followers: profile.followers,
        following: profile.following
    )
    return newProfile
}

Хотя мы просто обновляем bio, нам всё равно приходится копировать все эти массивы структуры (посты, подписчики, на кого подписаны).

Это сильно влияет на память и производительность.

Чтобы решить эту проблему, вместо того, чтобы помещать всё в структуру, мы переместим большие данные в класс.

Таким образом, копируется только небольшая структура, а большие массивы переносятся в класс. Поскольку класс является ссылочным типом, его экземпляр не копируется, а только используется совместно.

final class UserDataStore {
    var posts: [String] = []
    var followers: [String] = []
    var following: [String] = []
}

struct UserProfile {
    let name: String
    let bio: String
    let store: UserDataStore
}

Как видите, мы переместили массив структур внутрь класса UserDataStore.

Теперь, когда нам нужно изменить свойство bio, мы сделаем это следующим образом:

func updateBio(_ profile: UserProfile, newBio: String) -> UserProfile {
    var newProfile = profile
    newProfile.bio = newBio
    return newProfile
}

Здесь, когда мы копируем профиль в newProfile, копируются только имя и биография, а не хранилище (потому что это класс).

Думаю, к этому моменту мы уже лучше разобрались!

Оптимизации Swift (Copy-on-Write)

Давайте разберёмся на примере:

struct UserProfile {
    let name: String
    let bio: String
    let posts: [String]
    let followers: [String]
    let following: [String]
}

В приведённом выше коде нет вложенных структур; есть только String и [String]. По сути, Swift использует хитрую оптимизацию, называемую Copy-on-Write (COW), для собственных типов данных, таких как String, Int, Array, Dictionary и т.д.

Это означает, что мы можем записать в структуру столько значений, сколько нам нужно, без ущерба для производительности, но значения должны быть собственных типов, таких как Int, String, Array и т. д.

Swift упоминает об этом в документации по оптимизации. Вы также можете ознакомиться с ответами на Stack Overflow.

Использование структур для общего изменяемого состояния

Выше мы хорошо разобрались со структурами, но некоторые разработчики часто рассматривают их как ссылочные типы (которые могут иметь общее состояние).

Давайте разберём это на примере:

struct Counter {
    var count: Int
}

var counter1 = Counter(count: 0)
var counter2 = counter1

Здесь мы скопировали counter1 в counter2, но это не значит, что изменения в counter1 также повлияют на counter2.

counter1.count += 1
print(counter2.count)  // 0

Здесь мы обновляем значение счётчика объекта counter1, а не counter2. Вывод значения counter2 даёт его результат, а не счётчик counter1.

Это связано с тем, что counter1 и counter2 — это разные независимые объекты.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: