> Как разделить функции для чтения и записи значений (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Revolut
Стек: iOS, Swift
> Пример ответа
Короткий ответ
Разделение чтения и записи в Swift достигается через протоколы с разными уровнями доступа, computed properties с отдельными геттерами и сеттерами, а также через value types с приватными мутирующими методами. Основная цель - инкапсуляция состояния и контроль над изменениями. Для iOS-приложений это особенно важно при работе с моделями данных, UserDefaults, Core Data и реактивными потоками, где нужно предотвратить несанкционированную запись при свободном чтении.
Подробное объяснение
В Swift разделение доступа к чтению и записи реализуется несколькими способами:
-
Протоколы с разным уровнем доступа - определяете два протокола:
ReadableиWritable. Тип conforms к обоим, но внешний код получает толькоReadablereference. Это классический принцип interface segregation. -
Computed properties с отдельными геттерами и сеттерами - используете
private(set)для свойства, которое можно читать снаружи, но изменять только внутри типа. Либо полностью приватное stored property с публичным computed getter. -
Value types с мутирующими методами - структуры с приватными свойствами и публичными методами, которые изменяют состояние через
mutating. Это позволяет контролировать инварианты. -
Акторы (actors) - для конкурентного доступа в Swift 5.5+ акторы изолируют состояние и предоставляют асинхронные методы чтения и записи.
-
Функциональный подход - иммутабельные структуры данных, где "запись" - это создание новой копии с изменённым значением.
Для iOS-приложений часто комбинируют эти подходы: модель данных - иммутабельная структура, а менеджер состояния - класс с private(set) и методами обновления.
На практике
В реальном iOS-проекте разделение чтения и записи применяется:
- В
UserDefaultsилиKeychainобёртках: публичный getter для чтения, приватный setter внутри сервиса. - В
ViewModelдля SwiftUI:@Publishedсвойство сprivate(set)- view читает, view model пишет. - В Core Data:
NSManagedObjectс readonly computed properties для отображения, отдельный сервис для мутаций. - В реактивных потоках (Combine):
CurrentValueSubjectприватный, наружу -AnyPublisherтолько для чтения.
Ключевой паттерн - "read-only interface": вы возвращаете протокол или обёртку, которая не позволяет писать, даже если реальный объект мутабелен.
Пример кода
SWIFT// Протокольный подходprotocol UserReadable {var id: UUID { get }var name: String { get }}protocol UserWritable: UserReadable {func updateName(_ newName: String)}struct User: UserWritable {private(set) var id: UUIDprivate(set) var name: Stringmutating func updateName(_ newName: String) {name = newName}}// Использованиеfunc displayUser(_ user: UserReadable) {print(user.name) // только чтение}var user = User(id: UUID(), name: "Alice")displayUser(user)// private(set) с классомfinal class UserStore {private(set) var currentUser: Userprivate let repository: UserRepositoryfunc updateUser(_ user: User) {// валидация, логированиеcurrentUser = user}}// Актор для конкурентностиactor Counter {private var value = 0func increment() {value += 1}func getValue() -> Int {value}}
Как отвечать на собеседовании
Начните с протоколов и private(set), затем упомяните акторы для конкурентности. Покажите понимание trade-off: протоколы добавляют абстракцию, private(set) проще, но не защищает от мутаций внутри класса. Приведите пример из реального iOS-проекта - например, ViewModel с @Published private(set) var state. Подчеркните, что выбор зависит от контекста: для простых моделей достаточно private(set), для сложных систем - протоколы или акторы. Если спросят про SwiftUI - объясните, как это связано с @State и @Binding.
Что проверяет интервьюер
Интервьюер оценивает:
- Понимание инкапсуляции и принципов SOLID (особенно interface segregation).
- Знание возможностей Swift: access control, протоколы, акторы.
- Умение применять паттерны в контексте iOS: Combine, SwiftUI, Core Data.
- Способность объяснить trade-off между простотой и гибкостью.
- Понимание конкурентности и потокобезопасности при разделении доступа.
Типичные ошибки
- Использование
private(set)для классов, когда нужна потокобезопасность - это не защищает от гонок. - Создание избыточных протоколов для тривиальных случаев - overengineering.
- Забывание про
mutatingдля структур при изменении свойств. - Путаница между
letиprivate(set):let- константа,private(set)- изменяемая внутри. - Возврат мутабельного массива или словаря наружу - копирование ссылочных типов внутри коллекций.
- Игнорирование акторов при работе с shared state в многопоточных сценариях.
> Похожие задачи по mobile
Как проверить, что число положительное или отрицательное
Как реализовать проверку на переполнение при конвертации строки в число
Как реализовать фильтрацию массива с сохранением уникальных элементов и порядка
Как реализовать функцию setValue для обновления значения по ключу
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью