Как удалить методы экземпляра во время выполнения в Objective-C 2.0?

Можно ли удалить методы, добавленные в класс с class_addMethod?

Или, если я хочу это сделать, должен ли я продолжать создавать классы во время выполнения с помощью objc_allocateClassPair и добавлять к ним различные наборы методов для изменения реализованных методов?

Я буду принимать ответы, которые включают хакерство: -)

Ответ 1

Короче говоря, вы не можете.

Вы можете в Objective-C 1.0 ABI/API через:

OBJC_EXPORT void class_removeMethods(Class, struct objc_method_list *) OBJC2_UNAVAILABLE;

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

Также удаленный из ObjC2.0 ABI был возможностью прямого доступа к структурам класса/метода. Они теперь непрозрачны, так что их можно изменить в будущем, не нарушая бинарную совместимость.

Однако вы можете использовать собственный прокси-сервер, который варьирует набор методов, на которые он отвечает. См. Документацию для класса NSProxy; http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSProxy_Class/Reference/Reference.html

Конечно, этот вопрос задает вопрос "Что вы пытаетесь сделать?". Такое мета-программирование на лету нетипично. Как только экземпляр класса создается, обычно не считается желательным изменять набор методов, на которые он реагирует, в предположении, что предыдущие экземпляры могут все еще зависеть от указанных методов.

Ответ 2

Вы не можете точно "удалить" метод, но вы можете получить тот же эффект, что и удаление, сделав метод просто перенаправляя все на путь пересылки сообщения (код, который в конечном итоге вызовет -forwardInvocation:). Это можно сделать двумя способами:

  • _objc_msgForward(), объявленный в /usr/include/objc/message.h, но не включенный в онлайн-документацию, может использоваться как IMP для вашего метода, который вы пытаетесь удалить. Поскольку он недокументирован, его можно считать закрытым или неподдерживаемым.
  • Вы можете вызвать class_getMethodImplementation() с помощью селектора, который, как вы знаете, не существует, и использовать результат как IMP для метода, который вы пытаетесь удалить, Основываясь на документации, это должно иметь тот же эффект, что и первый вариант:

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

В любом случае он становится таким простым, как:

method_setImplementation(methodToRemove, forwardingIMP);

Обратите внимание, что это в основном блокирует реализацию суперкласса, поэтому вам нужно быть более осторожным, если какой-либо суперкласс может иметь правильную реализацию, которую вы хотите сохранить. В таком случае вы можете получить IMP из суперкласса или чего-то подобного.