Что такое «метод Swizzling» и когда вам следует использовать эту функцию

Одна из вещей, с которой я впервые столкнулся, когда начал работать над устаревшими приложениями для iOS, был «Method Swizzling».

Swizzling позволяет разработчикам изменять базовую реализацию существующего метода без изменения исходной реализации. Фактически, вы можете поменять местами один метод на другой. Разработчики JavaScript могли быть знакомы с этой концепцией, если они когда-либо делали исправления обезьяны.

В большинстве современных объектно-ориентированных и статически типизированных языков есть способ переопределения существующих методов, если вы наследуете от базового класса. Иногда это называют полиморфизмом. Но что, если вы хотите изменить исходную реализацию, не изменяя исходный базовый код в базовом классе?

Вы можете сделать это на большинстве языков Apple, используя метод swizzling. Вы можете спросить себя, зачем это делать, если у вас есть исходный код метода, который нужно изменить? Основная причина обычно заключается в том, что если вы используете библиотеку, которую вы не можете изменить или у вас нет разрешения на изменение, вы можете, по крайней мере, изменить поведение метода, используя swizzling во время выполнения.

Objective-C Среда выполнения

Среда выполнения для Objective-C предоставляет несколько хороших утилит для выполнения операций во время выполнения приложения. Вы можете использовать селекторы для создания ссылок на сигнатуры методов в Objective-C, а затем использовать эти селекторы для управления средой выполнения.

В следующем примере у нас есть класс, у которого есть метод, который нам нужно изменить. Мы вызовем класс TaxCalculator и наш метод, который хотим изменить whatAreMyTaxes:

Теперь мы собираемся создать категорию Objective-C, которая похожа на расширение в Swift, чтобы добавить новый метод в наш класс TaxCalculator:

В этой категории добавлен новый метод swizzle_whatAreMyTaxes. Это будет метод, который мы будем использовать для замены исходного метода whatAreMyTaxes.

Чтобы наше приложение могло использовать swizzled-метод, нам нужно будет поменять местами методы при первой загрузке нашего приложения. Мы будем использовать load метод Objective-C, чтобы поменять местами наши методы. Метод load в классе всегда выполняется при инициализации приложения. Нам также нужно будет убедиться, что он загружается только один раз.

Мы будем использовать Grand Central Dispatch (GCD), чтобы убедиться, что метод загружается только один раз. Реализация будет выглядеть как в следующем примере:

Рассматривая метод выше, давайте разберемся, что мы делаем для каждой строки. Первое, что мы делаем в методе load, - это проверяем, является ли self экземпляром класса TaxCalculator.

Убедившись, что мы загружаем правильный класс во время выполнения, мы создаем токен dispatch_once_t, который будем использовать в методе dispatch_once. Этот метод принимает два параметра: первый - адрес токена, а второй - замыкание или блок.

При закрытии dispatch_once мы создаем ссылку на текущий класс, используя тип Class и вызывая [self class]. Затем мы можем создать два селектора для нашего исходного метода и нашего swizzled-метода. В Objective-C мы используем @selector метод, чтобы получить ссылку на наши сигнатуры методов.

После определения наших селекторов нам нужно будет получить фактическую ссылку на метод. Здесь мы начинаем использовать среду выполнения Objective-C. Мы создадим ссылки для обоих методов, используя метод class_getInstanceMethod. Если вы используете методы класса, вы можете использовать другой метод времени выполнения, который называется class_getClassMethod. Оба метода принимают в качестве параметров ссылку на класс и ссылки на селектор.

Теперь, когда у нас есть обе ссылки на методы, мы меняем их, используя метод method_exchangeImplementations. Этот метод также можно использовать для их обратной замены.

Утилита Swizzle

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

Используя этот вспомогательный класс, мы теперь можем реорганизовать наш метод загрузки, чтобы включить swizzling. Обратите внимание, что SimpleSwizzleHelper может обрабатывать как методы экземпляра, так и методы класса.



Заключение

Смена методов - очень мощная функция, которая входит в состав среды выполнения Objective-C. Будьте осторожны при использовании этой функции. Это следует использовать только в том случае, если вы не можете напрямую изменить базовую реализацию.

Любой разработчик, использующий эту функцию, также должен быть осторожен с распространением методов, в которых вы не знаете, какова исходная реализация, т.е. у вас есть заголовки и статическая библиотека, но нет доступа к исходному коду. Я бы также не стал выгружать любой системный код из операционных систем Apple. Это может причинить вред при неправильном использовании.

Теперь, когда вы прочитали этот пост, веселитесь, наслаждаясь!

Хотите связаться с автором?

Эта статья изначально была опубликована на https://fek.io. Вы также можете ознакомиться с видеоуроком на YouTube.