декабря 21, 2025
17 мин. чтения

Вопросы для собеседования на позицию старшего iOS-разработчика: Полное руководство

interview
career-advice
job-search
Вопросы для собеседования на позицию старшего iOS-разработчика: Полное руководство
MB

Milad Bonakdar

Автор

Освойте продвинутую разработку под iOS с помощью основных вопросов для собеседования, охватывающих архитектурные паттерны, оптимизацию производительности, параллелизм, Core Data и системный дизайн для старших разработчиков.


Введение

От старших iOS-разработчиков ожидается проектирование надежных, масштабируемых приложений с поддержанием высокого качества кода и производительности. Эта роль требует глубоких знаний iOS-фреймворков, шаблонов проектирования, управления памятью и способности принимать обоснованные архитектурные решения.

Это всеобъемлющее руководство охватывает важные вопросы для собеседования на позицию старшего iOS-разработчика, охватывающие продвинутые концепции Swift, архитектурные паттерны, оптимизацию производительности, параллелизм и проектирование систем. Каждый вопрос включает подробные ответы, оценку редкости и уровни сложности.


Продвинутые возможности Swift и языка (6 вопросов)

1. Объясните управление памятью в Swift и ARC (Automatic Reference Counting).

Ответ: ARC автоматически управляет памятью, отслеживая и управляя ссылками на экземпляры классов.

  • Как это работает: Каждый экземпляр класса имеет счетчик ссылок. Когда счетчик достигает нуля, экземпляр освобождается.
  • Strong References (Сильные ссылки): По умолчанию. Увеличивают счетчик ссылок.
  • Weak References (Слабые ссылки): Не увеличивают счетчик ссылок. Автоматически становятся nil, когда экземпляр освобождается.
  • Unowned References (Неконтролируемые ссылки): Не увеличивают счетчик ссылок, но предполагают, что экземпляр всегда существует.
  • Retain Cycles (Циклы удержания): Возникают, когда два объекта содержат сильные ссылки друг на друга, предотвращая освобождение памяти.
class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) { self.name = name }
    deinit { print("\(name) is being deallocated") }
}

class Apartment {
    var unit: String
    weak var tenant: Person?  // weak для разрыва цикла удержания
    
    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) is being deallocated") }
}

Редкость: Очень часто Сложность: Сложно


2. Что такое Generics (Дженерики) в Swift и почему они полезны?

Ответ: Дженерики позволяют писать гибкие, повторно используемые функции и типы, которые могут работать с любым типом.

  • Преимущества: Повторное использование кода, безопасность типов, производительность (отсутствие накладных расходов во время выполнения)
  • Type Constraints (Ограничения типов): Ограничивают дженерик-типы определенными протоколами или классами
  • Associated Types (Ассоциированные типы): Используются в протоколах для определения типов-заполнителей
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// Generic type
struct Stack<Element> {
    private var items: [Element] = []
    
    mutating func push(_ item: Element) {
        items.append(item)
    }
    
    mutating func pop() -> Element? {
        return items.popLast()
    }
}

// Type constraint
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

Редкость: Часто Сложность: Средне


3. Объясните разницу между escaping и non-escaping замыканиями.

Ответ:

  • Non-escaping (неубегающие) (по умолчанию): Замыкание выполняется до возврата функции. Компилятор может лучше оптимизировать.
  • Escaping (убегающие) (@escaping): Замыкание переживает функцию (хранится в свойстве, вызывается асинхронно). Необходимо явно захватывать self.
class NetworkManager {
    var completionHandlers: [() -> Void] = []
    
    // Escaping closure - хранится для последующего использования
    func fetchData(completion: @escaping (Data?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            completion(data)  // Вызывается после возврата функции
        }.resume()
    }
    
    // Non-escaping closure - выполняется немедленно
    func processData(_ data: Data, transform: (Data) -> String) -> String {
        return transform(data)  // Вызывается до возврата функции
    }
}

Редкость: Часто Сложность: Средне


4. В чем разница между map, flatMap и compactMap?

Ответ: Это функции высшего порядка для преобразования коллекций:

  • map: Преобразует каждый элемент и возвращает массив результатов
  • compactMap: Как map, но отфильтровывает значения nil
  • flatMap: Сглаживает вложенные массивы в один массив
let numbers = [1, 2, 3, 4, 5]

// map
let doubled = numbers.map { $0 * 2 }
// [2, 4, 6, 8, 10]

// compactMap - удаляет nil
let strings = ["1", "2", "three", "4"]
let validNumbers = strings.compactMap { Int($0) }
// [1, 2, 4]

// flatMap - сглаживает массивы
let nested = [[1, 2], [3, 4], [5]]
let flattened = nested.flatMap { $0 }
// [1, 2, 3, 4, 5]

Редкость: Часто Сложность: Легко


5. Объясните Property Wrappers (Обертки свойств) в Swift.

Ответ: Обертки свойств добавляют слой разделения между кодом, который управляет тем, как хранится свойство, и кодом, который определяет свойство.

  • Встроенные примеры: @State, @Published, @AppStorage в SwiftUI
  • Пользовательские обертки: Определяют многократно используемое поведение свойств
@propertyWrapper
struct Capitalized {
    private var value: String = ""
    
    var wrappedValue: String {
        get { value }
        set { value = newValue.capitalized }
    }
}

struct User {
    @Capitalized var firstName: String
    @Capitalized var lastName: String
}

var user = User()
user.firstName = "john"
print(user.firstName)  // "John"

// UserDefaults wrapper
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T
    
    var wrappedValue: T {
        get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
        set { UserDefaults.standard.set(newValue, forKey: key) }
    }
}

struct Settings {
    @UserDefault(key: "username", defaultValue: "Guest")
    static var username: String
}

Редкость: Средне Сложность: Сложно


6. Что такое тип Result и как он используется?

Ответ: Result - это enum, представляющий либо успех, либо неудачу, что делает обработку ошибок более явной.

  • Определение: enum Result<Success, Failure: Error>
  • Преимущества: Типобезопасная обработка ошибок, более четкие API-контракты, лучше, чем выбрасывающие функции для асинхронного кода
enum NetworkError: Error {
    case invalidURL
    case noData
    case decodingError
}

func fetchUser(id: Int, completion: @escaping (Result<User, NetworkError>) -> Void) {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        completion(.failure(.invalidURL))
        return
    }
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data else {
            completion(.failure(.noData))
            return
        }
        
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            completion(.success(user))
        } catch {
            completion(.failure(.decodingError))
        }
    }.resume()
}

// Usage
fetchUser(id: 1) { result in
    switch result {
    case .success(let user):
        print("User: \(user.name)")
    case .failure(let error):
        print("Error: \(error)")
    }
}

Редкость: Часто Сложность: Средне


Архитектурные паттерны (5 вопросов)

7. Объясните паттерн MVVM (Model-View-ViewModel).

Ответ: MVVM разделяет логику пользовательского интерфейса от бизнес-логики, делая код более тестируемым и поддерживаемым.

Loading diagram...
  • Model (Модель): Данные и бизнес-логика
  • View (Представление): Пользовательский интерфейс (UIViewController, SwiftUI View)
  • ViewModel (Модель представления): Логика представления, преобразует данные модели для представления
  • Преимущества: Тестируемость (ViewModel не имеет зависимостей от пользовательского интерфейса), повторно используемые ViewModels, четкое разделение задач
// Model
struct User {
    let id: Int
    let firstName: String
    let lastName: String
}

// ViewModel
class UserViewModel {
    private let user: User
    
    var fullName: String {
        "\(user.firstName) \(user.lastName)"
    }
    
    var displayText: String {
        "User #\(user.id): \(fullName)"
    }
    
    init(user: User) {
        self.user = user
    }
}

// View
class UserViewController: UIViewController {
    private let viewModel: UserViewModel
    private let label = UILabel()
    
    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = viewModel.displayText
    }
}

Редкость: Очень часто Сложность: Средне


8. Что такое паттерн Coordinator (Координатор) и зачем его использовать?

Ответ: Паттерн Coordinator отделяет логику навигации от контроллеров представлений.

  • Проблема: Массивные контроллеры представлений с логикой навигации, смешанной с логикой пользовательского интерфейса
  • Решение: Координаторы обрабатывают поток навигации
  • Преимущества: Повторно используемые контроллеры представлений, тестируемая навигация, четкий поток приложения
protocol Coordinator {
    var navigationController: UINavigationController { get }
    func start()
}

class AppCoordinator: Coordinator {
    let navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        showLogin()
    }
    
    func showLogin() {
        let loginVC = LoginViewController()
        loginVC.coordinator = self
        navigationController.pushViewController(loginVC, animated: true)
    }
    
    func showHome() {
        let homeVC = HomeViewController()
        homeVC.coordinator = self
        navigationController.pushViewController(homeVC, animated: true)
    }
}

Редкость: Средне Сложность: Сложно


9. Объясните Dependency Injection (Внедрение зависимостей) в iOS.

Ответ: Внедрение зависимостей - это паттерн проектирования, в котором зависимости предоставляются объекту, а не создаются внутри него.

  • Преимущества: Тестируемость (внедрение моков), гибкость, слабая связанность
  • Типы:
    • Constructor Injection (Внедрение через конструктор): Передача зависимостей через инициализатор (наиболее распространенный)
    • Property Injection (Внедрение через свойство): Установка зависимостей после инициализации
    • Method Injection (Внедрение через метод): Передача зависимостей в качестве параметров метода
// Protocol for abstraction
protocol NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void)
}

// Concrete implementation
class URLSessionNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        // Real network call
    }
}

// Mock for testing
class MockNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        completion(.success(Data()))
    }
}

// ViewModel with dependency injection
class UserViewModel {
    private let networkService: NetworkService
    
    // Constructor injection
    init(networkService: NetworkService) {
        self.networkService = networkService
    }
    
    func loadUsers() {
        networkService.fetchData { result in
            // Handle result
        }
    }
}

// Usage
let viewModel = UserViewModel(networkService: URLSessionNetworkService())

// Testing
let testViewModel = UserViewModel(networkService: MockNetworkService())

Редкость: Часто Сложность: Средне


10. Что такое паттерн Repository (Репозиторий)?

Ответ: Паттерн Repository абстрагирует логику доступа к данным, предоставляя чистый API для операций с данными.

  • Преимущества: Централизованная логика данных, легкое переключение источников данных (API, база данных, кеш), тестируемость
  • Реализация: Репозиторий координирует работу между несколькими источниками данных
protocol UserRepository {
    func getUser(id: Int) async throws -> User
    func saveUser(_ user: User) async throws
}

class UserRepositoryImpl: UserRepository {
    private let apiService: APIService
    private let cacheService: CacheService
    
    init(apiService: APIService, cacheService: CacheService) {
        self.apiService = apiService
        self.cacheService = cacheService
    }
    
    func getUser(id: Int) async throws -> User {
        // Check cache first
        if let cachedUser = try? await cacheService.getUser(id: id) {
            return cachedUser
        }
        
        // Fetch from API
        let user = try await apiService.fetchUser(id: id)
        
        // Update cache
        try? await cacheService.saveUser(user)
        
        return user
    }
    
    func saveUser(_ user: User) async throws {
        try await apiService.updateUser(user)
        try await cacheService.saveUser(user)
    }
}

Редкость: Средне Сложность: Средне


11. Объясните различия между MVC, MVP и MVVM.

Ответ:

  • MVC (Model-View-Controller):
    • Паттерн по умолчанию от Apple
    • Контроллер выступает посредником между Моделью и Представлением
    • Проблема: Массивные контроллеры представлений
  • MVP (Model-View-Presenter):
    • Презентер обрабатывает всю логику пользовательского интерфейса
    • Представление пассивно (только отображает данные)
    • Лучшая тестируемость, чем у MVC
  • MVVM (Model-View-ViewModel):
    • ViewModel предоставляет потоки данных
    • Представление привязывается к ViewModel
    • Лучше всего подходит для реактивного программирования (Combine, RxSwift)

Редкость: Часто Сложность: Сложно


Производительность и оптимизация (5 вопросов)

12. Как вы оптимизируете производительность table view и collection view?

Ответ: Существует несколько стратегий улучшения производительности прокрутки:

  1. Cell Reuse (Повторное использование ячеек): Правильно используйте dequeueReusableCell
  2. Avoid Heavy Operations (Избегайте тяжелых операций): Не выполняйте дорогостоящие вычисления в cellForRowAt
  3. Image Optimization (Оптимизация изображений):
    • Изменяйте размер изображений до размера отображения
    • Используйте фоновые потоки для обработки изображений
    • Кешируйте декодированные изображения
  4. Prefetching (Предварительная выборка): Реализуйте UITableViewDataSourcePrefetching
  5. Height Caching (Кеширование высоты): Кешируйте вычисленные высоты ячеек
  6. Avoid Transparency (Избегайте прозрачности): Непрозрачные представления отображаются быстрее
class ImageTableViewCell: UITableViewCell {
    static let identifier = "ImageCell"
    private let customImageView = UIImageView()
    
    override func prepareForReuse() {
        super.prepareForReuse()
        customImageView.image = nil  // Очистить старое изображение
    }
}

// In view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: ImageTableViewCell.identifier, for: indexPath) as! ImageTableViewCell
    
    // Load image asynchronously
    DispatchQueue.global(qos: .userInitiated).async {
        let image = self.loadAndResizeImage(at: indexPath)
        DispatchQueue.main.async {
            cell.imageView?.image = image
        }
    }
    
    return cell
}

Редкость: Очень часто Сложность: Средне


13. Объясните Instruments и как вы его используете для профилирования производительности.

Ответ: Instruments - это инструмент анализа производительности Xcode.

  • Common Instruments (Распространенные инструменты):
    • Time Profiler: Определяет код, интенсивно использующий ЦП
    • Allocations: Отслеживает выделение памяти и утечки
    • Leaks: Обнаруживает утечки памяти
    • Network: Отслеживает сетевую активность
    • Energy Log: Анализирует использование батареи
  • Workflow (Рабочий процесс):
    1. Profile app (Cmd+I)
    2. Choose instrument
    3. Record and interact with app
    4. Analyze call tree and timeline
    5. Identify bottlenecks

Редкость: Часто Сложность: Средне


14. Как вы обнаруживаете и исправляете утечки памяти?

Ответ: Утечки памяти возникают, когда объекты не освобождаются, когда они больше не нужны.

  • Common Causes (Распространенные причины):
    • Retain cycles (циклы сильных ссылок)
    • Closures capturing self strongly (Замыкания, сильно захватывающие self)
    • Delegates not marked as weak (Делегаты, не помеченные как weak)
  • Detection (Обнаружение):
    • Instruments Leaks tool
    • Memory Graph Debugger in Xcode
    • Watch for increasing memory usage (Наблюдайте за увеличением использования памяти)
  • Fixes (Исправления):
    • Use weak or unowned for delegates
    • Use [weak self] or [unowned self] in closures
    • Break retain cycles (Разрывайте циклы удержания)
class ViewController: UIViewController {
    var closure: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // BAD - retain cycle
        closure = {
            self.view.backgroundColor = .red
        }
        
        // GOOD - weak self
        closure = { [weak self] in
            self?.view.backgroundColor = .red
        }
        
        // GOOD - unowned self (if self always exists)
        closure = { [unowned self] in
            self.view.backgroundColor = .red
        }
    }
}

Редкость: Очень часто Сложность: Средне


15. Какие методы вы используете для оптимизации запуска приложения?

Ответ: Более быстрый запуск приложения улучшает взаимодействие с пользователем:

  1. Lazy Loading (Ленивая загрузка): Инициализируйте объекты только при необходимости
  2. Reduce Dylib Loading (Уменьшите загрузку Dylib): Минимизируйте динамические библиотеки
  3. Optimize application:didFinishLaunching:
    • Move non-critical work to background (Переместите некритичную работу в фоновый режим)
    • Defer heavy initialization (Отложите тяжелую инициализацию)
  4. Binary Size (Размер двоичного файла): Меньший двоичный файл загружается быстрее
  5. Avoid Heavy Operations (Избегайте тяжелых операций): Не блокируйте основной поток
  6. Measure (Измерьте): Используйте шаблон App Launch в Instruments
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // Critical: Setup window and root view controller
    setupWindow()
    
    // Defer non-critical work
    DispatchQueue.main.async {
        self.setupAnalytics()
        self.setupCrashReporting()
    }
    
    // Background work
    DispatchQueue.global(qos: .background).async {
        self.preloadData()
    }
    
    return true
}

Редкость: Часто Сложность: Средне


16. Как вы обрабатываете кеширование и загрузку изображений?

Ответ: Эффективная обработка изображений имеет решающее значение для производительности:

  • Strategies (Стратегии):
    • Memory Cache (Кеш памяти): Быстрый доступ, ограниченный размер
    • Disk Cache (Дисковый кеш): Постоянный, большая емкость
    • Download (Загрузка): Получение из сети
  • Libraries (Библиотеки): SDWebImage, Kingfisher (автоматически обрабатывают кеширование)
  • Custom Implementation (Пользовательская реализация):
class ImageCache {
    static let shared = ImageCache()
    
    private let memoryCache = NSCache<NSString, UIImage>()
    private let fileManager = FileManager.default
    private let cacheDirectory: URL
    
    private init() {
        cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("ImageCache")
        try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
    }
    
    func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        let key = url.absoluteString as NSString
        
        // Check memory cache
        if let cachedImage = memoryCache.object(forKey: key) {
            completion(cachedImage)
            return
        }
        
        // Check disk cache
        let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)
        if let diskImage = UIImage(contentsOfFile: fileURL.path) {
            memoryCache.setObject(diskImage, forKey: key)
            completion(diskImage)
            return
        }
        
        // Download
        URLSession.shared.dataTask(with: url) { data, _, _ in
            guard let data = data, let image = UIImage(data: data) else {
                completion(nil)
                return
            }
            
            // Save to caches
            self.memoryCache.setObject(image, forKey: key)
            try? data.write(to: fileURL)
            
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume()
    }
}

Редкость: Часто Сложность: Сложно


Параллелизм и асинхронное программирование (4 вопроса)

17. Объясните async/await в Swift.

Ответ: Современная модель параллелизма Swift, представленная в Swift 5.5.

  • Преимущества: Более чистый синтаксис, чем у обработчиков завершения, более простая обработка ошибок, обеспечиваемая компилятором безопасность потоков
  • Keywords (Ключевые слова):
    • async: Помечает функцию, которая может приостанавливаться
    • await: Помечает точку приостановки
    • Task: Создает новый асинхронный контекст
    • actor: Типобезопасный ссылочный тип
// Old way with completion handlers
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        // Handle result
    }.resume()
}

// New way with async/await
func fetchUser(id: Int) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
}

// Usage
Task {
    do {
        let user = try await fetchUser(id: 1)
        print(user.name)
    } catch {
        print("Error: \(error)")
    }
}

// Parallel execution
async let user1 = fetchUser(id: 1)
async let user2 = fetchUser(id: 2)
let users = try await [user1, user2]

Редкость: Очень часто Сложность: Сложно


18. Что такое Actors (Акторы) в Swift?

Ответ: Акторы - это ссылочные типы, которые защищают свое изменяемое состояние от гонок данных.

  • Thread Safety (Безопасность потоков): Только одна задача может получить доступ к изменяемому состоянию актора за раз
  • Automatic Synchronization (Автоматическая синхронизация): Компилятор обеспечивает безопасный доступ
  • Main Actor: Специальный актор для обновлений пользовательского интерфейса
actor BankAccount {
    private var balance: Double = 0
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func withdraw(amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
    
    func getBalance() -> Double {
        return balance
    }
}

// Usage
let account = BankAccount()

Task {
    await account.deposit(amount: 100)
    let balance = await account.getBalance()
    print("Balance: \(balance)")
}

// Main Actor for UI updates
@MainActor
class ViewModel: ObservableObject {
    @Published var data: [String] = []
    
    func loadData() async {
        let newData = await fetchDataFromAPI()
        // Automatically on main thread
        self.data = newData
    }
}

Редкость: Средне Сложность: Сложно


19. Объясните фреймворк Combine.

Ответ: Combine - это фреймворк реактивного программирования от Apple.

  • Core Concepts (Основные концепции):
    • Publisher (Издатель): Со временем испускает значения
    • Subscriber (Подписчик): Получает значения
    • Operator (Оператор): Преобразует значения
  • Преимущества: Декларативный, составляемый, встроенные операторы
  • Use Cases (Сценарии использования): Работа с сетью, обработка пользовательского ввода, привязка данных
import Combine

class UserViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var users: [User] = []
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .flatMap { query in
                self.searchUsers(query: query)
            }
            .receive(on: DispatchQueue.main)
            .sink { [weak self] users in
                self?.users = users
            }
            .store(in: &cancellables)
    }
    
    func searchUsers(query: String) -> AnyPublisher<[User], Never> {
        // Return publisher
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [User].self, decoder: JSONDecoder())
            .replaceError(with: [])
            .eraseToAnyPublisher()
    }
}

Редкость: Часто Сложность: Сложно


20. В чем разница между Serial и Concurrent очередями?

Ответ: Очереди диспетчеризации выполняют задачи последовательно или одновременно:

  • Serial Queue (Последовательная очередь): Выполняет одну задачу за раз в порядке FIFO. Задачи ждут завершения предыдущей задачи.
  • Concurrent Queue (Параллельная очередь): Выполняет несколько задач одновременно. Задачи запускаются в порядке FIFO, но могут завершаться в любом порядке.
  • Main Queue (Главная очередь): Специальная последовательная очередь для обновлений пользовательского интерфейса
// Serial queue
let serialQueue = DispatchQueue(label: "com.app.serial")
serialQueue.async { print("Task 1") }
serialQueue.async { print("Task 2") }
// Output: Task 1, Task 2 (always in order)

// Concurrent queue
let concurrentQueue = DispatchQueue(label: "com.app.concurrent", attributes: .concurrent)
concurrentQueue.async { print("Task A") }
concurrentQueue.async { print("Task B") }
// Output: Task A, Task B OR Task B, Task A (order not guaranteed)

// Barrier for write operations
concurrentQueue.async(flags: .barrier) {
    // Exclusive access - no other tasks run simultaneously
    print("Write operation")
}

Редкость: Часто Сложность: Средне


Core Data и постоянство данных (3 вопроса)

21. Объясните архитектуру Core Data и ее основные компоненты.

Ответ: Core Data - это фреймворк Apple для графов объектов и постоянства данных.

Loading diagram...
  • NSManagedObjectModel: Определение схемы (сущности, атрибуты, отношения)
  • NSPersistentStoreCoordinator: Координирует работу между контекстом и хранилищем
  • NSManagedObjectContext: Рабочая память для объектов (как черновик)
  • NSPersistentStore: Фактическое хранилище (SQLite, двоичный файл, в памяти)

Редкость: Часто Сложность: Средне


22. Как вы обрабатываете параллелизм в Core Data?

Ответ: Контексты Core Data не являются потокобезопасными. Используйте правильные паттерны параллелизма:

  • Context Types (Типы контекстов):
    • Main Queue Context (Контекст главной очереди): Для операций пользовательского интерфейса
    • Private Queue Context (Контекст частной очереди): Для фоновых операций
  • Best Practices (Лучшие практики):
    • Never pass managed objects between threads (Никогда не передавайте управляемые объекты между потоками)
    • Use perform or performAndWait for context operations
    • Pass object IDs between contexts
class CoreDataManager {
    let persistentContainer: NSPersistentContainer
    
    var viewContext: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        persistentContainer.performBackgroundTask { context in
            block(context)
            
            if context.hasChanges {
                try? context.save()
            }
        }
    }
}

// Usage
coreDataManager.performBackgroundTask { context in
    let user = User(context: context)
    user.name = "John"
    // Context saves automatically
}

Редкость: Средне Сложность: Сложно


23. Что такое NSFetchedResultsController и когда вы его используете?

Ответ: NSFetchedResultsController эффективно управляет результатами Core Data для таблиц/коллекций.

  • Преимущества:
    • Automatic change tracking (Автоматическое отслеживание изменений)
    • Memory efficient (batching) (Эффективное использование памяти (пакетирование))
    • Section management (Управление разделами)
    • Automatic UI updates (Автоматическое обновление пользовательского интерфейса)
  • Use Case (Сценарий использования): Отображение объектов Core Data в таблицах/коллекциях
class UsersViewController: UITableViewController, NSFetchedResultsControllerDelegate {
    var fetchedResultsController: NSFetchedResultsController<User>!
    
    func setupFetchedResultsController() {
        let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        
        fetchedResultsController = NSFetchedResultsController(
            fetchRequest: fetchRequest,
            managedObjectContext: context,
            sectionNameKeyPath: nil,
            cacheName: nil
        )
        
        fetchedResultsController.delegate = self
        try? fetchedResultsController.performFetch()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.sections?[section].numberOfObjects ?? 0
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView
Newsletter subscription

Еженедельные советы по карьере, которые действительно работают

Получайте последние идеи прямо на вашу почту

Похожие посты

Decorative doodle

Ваше следующее собеседование — всего одно резюме

Создайте профессиональное оптимизированное резюме за несколько минут. Не нужны навыки дизайна—только проверенные результаты.

Создать моё резюме

Поделиться этим постом

Используйте Свои 6 Секунд По Максимуму

Рекрутеры просматривают резюме в среднем всего 6-7 секунд. Наши проверенные шаблоны разработаны так, чтобы мгновенно привлекать внимание и заставлять продолжать чтение.