Что такое method swizzling в Swift и приведи пример его использования?

«Что такое method swizzling в Swift и приведи пример его использования?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Method swizzling — это техника подмены реализации метода во время выполнения (runtime) путем обмена указателей на реализации (implementations) двух методов. Используется в крайних случаях для отладки, логирования или добавления функциональности, когда другие подходы (наследование, делегирование, композиция) неприменимы.

Пример: добавление логирования ко всем вызовам viewDidLoad в UIViewController.

import UIKit

extension UIViewController {
    // 1. Объявляем новый метод для подмены
    @objc dynamic func swizzled_viewDidLoad() {
        // 2. Вызываем оригинальную реализацию (теперь она доступна под именем `swizzled_viewDidLoad`)
        swizzled_viewDidLoad()
        // 3. Добавляем свою логику
        print("[Swizzle] ViewDidLoad called for: (type(of: self))")
    }

    // 4. Статический метод для однократного выполнения swizzling
    static func swizzleViewDidLoad() {
        // Гарантируем выполнение только один раз
        guard self === UIViewController.self else { return }

        let originalSelector = #selector(viewDidLoad)
        let swizzledSelector = #selector(swizzled_viewDidLoad)

        guard 
            let originalMethod = class_getInstanceMethod(self, originalSelector),
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
        else {
            print("Swizzling failed: methods not found")
            return
        }

        // 5. Меняем реализации местами
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

// Вызов в AppDelegate при запуске приложения:
// UIViewController.swizzleViewDidLoad()

Ключевые моменты и best practices:

  • @objc dynamic: Обязателен для методов, участвующих в swizzling, чтобы включить динамическую диспетчеризацию Objective-C runtime.
  • Вызов оригинала: Всегда вызывайте оригинальную реализацию (под новым именем), чтобы не сломать ожидаемое поведение системы или фреймворка.
  • Однократность: Swizzling должен выполняться только один раз, обычно в +load (Objective-C) или в инициализаторе AppDelegate (Swift). В примере используется проверка self === UIViewController.self.
  • Опасности: Может привести к сложно отлаживаемым ошибкам, конфликтам между библиотеками и поломке при обновлениях iOS. Избегайте в продакшн-коде, если есть альтернативы.
  • Альтернативы: Рассмотрите использование делегирования, подклассов, композиции или сабклассинга (например, через method_exchangeImplementations только в своих классах).