Цель C - получить уведомление при вызове метода - PullRequest
8 голосов
/ 12 февраля 2012

есть ли способ отслеживать вызов метода специального объекта и получать уведомления?Я хотел бы, чтобы функция "Observer" вызывалась при вызове [object someMethod].Я знаю, что могу контролировать изменение значения объекта, используя target c KVO, но он не говорит мне, что стек вызовов (какие функции) вызвал изменение значения объекта.Есть ли что-то похожее на KVO, но для методов, где я могу отслеживать вызовы функций?Любые предложения о том, как реализовать такую ​​функцию, если это невозможно с существующей структурой?Спасибо!

Ответы [ 2 ]

11 голосов
/ 12 февраля 2012

Я предполагаю, что вы не контролируете реализацию класса, чей метод вы хотите перехватить, и поэтому изменение API, как предполагает @Conrad Shultz, невозможно. На ум приходит несколько других подходов.

Первый - это метод Swizzling. Это работает так: вы добавляете метод с той же сигнатурой в класс, используя категорию, затем вы меняете реализации метода, который вы хотите перехватить, и метода, который вы добавили. Метод, который вы добавите, будет вызывать любые хуки, которые вы хотите, до и / или после вызова самого себя. Поскольку вы меняете реализации, вызов «себя» фактически вызывает оригинальный метод. Есть много хороших объяснений метода метания там. Вот один.

Другой вариант может заключаться в том, чтобы обернуть объект в NSProxy и пропустить вызовы, используя -forwardInvocation. Опять же, есть много ресурсов, подробно объясняющих прокси и переадресацию вызовов, поэтому я не буду перефразировать это здесь. Вот один из них. Этот подход ограничен тем, что у вас должен быть необходимый доступ к свопу в прокси для реального объекта. Если объект создается в частном порядке внутри какого-либо API, такой подход не будет полезен.

Другой подход - делать то, что делает КВО. Наблюдение значения ключа работает путем создания подкласса класса во время выполнения, а затем выполнения то, что называется isa - колебание, чтобы изменить класс существующего экземпляра на новый динамический подкласс. Нет причины, по которой вы не могли бы создать сгенерированный во время выполнения подкласс класса, метод экземпляра которого вы хотите перехватить, а затем заставить реализацию этого метода, сгенерированную во время выполнения, вызывать любые другие хуки до и / или после вызова суперкласса. (реальная) реализация метода. Это может позволить вам перехватывать более чем один метод более просто, но не будет простого способа перехвата произвольных методов с произвольными сигнатурами, потому что вы должны найти способ вызывать свои хуки без искажения стек, а затем направить в основную реализацию. Этот способ перехвата произвольных методов с произвольными сигнатурами, вероятно, потребует написания батутов в сборке и последующего вызова в реальной реализации, как это делает objc_msgSend. Вот хорошая статья о создании подклассов во время выполнения .

Для исторической перспективы: раньше также использовалась функция по имени poseAsClass, которая также позволяла бы это, но она была удалена, я думаю, со времен SnowLeopard-ish, так что, вероятно, это не стартер.

Любой из этих вариантов будет иметь последствия для производительности и все они будут чрезвычайно «умными». Я не говорю это, чтобы погладить себя по спине - я не придумал ничего из этого, только отрыгнул их. Но я заметил, что на протяжении многих лет, когда решение кажется чрезмерно «умным», это явный признак того, что я направляюсь к возможной катастрофе. Иными словами, если API, с которым вы работаете, не дает вам возможности перехватывать эти вызовы методов, это, вероятно, признак того, что вам следует подойти к проблеме с другой стороны. Но ваш пробег может меняться, очевидно.

1 голос
/ 12 февраля 2012

В связанном вопросе Билл Бамгарнер отмечает (не каламбур), что это невозможно в общем случае.

Я бы предположил, что если вам нужно знать вызывающего абонента из-за вашего дизайна API, лучшим подходом является модификация вашего метода, чтобы он принимал параметр "отправителя" (как это делают многие из базовых методов, включая каждый IBAction). В этом случае вызывающий обязан передать себя методу.

Это дает дополнительное преимущество, позволяя создавать более сложные шаблоны. Я мог бы, например, представить прокси-объект, законно желающий передать другой объект вместо себя. (Очевидно, что это нужно делать с осторожностью и предусмотрительностью.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...