Как удалить методы экземпляра во время выполнения в 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
из суперкласса или чего-то подобного.