Как динамически создавать категории в цели c?
Я работаю над проектом библиотеки Objective-C ('MyLib'). Допустим, эта библиотека затем включена в "TestApp". Теперь "TestApp" также включает в себя другую библиотеку под названием "Xlib". У этого Xlib есть класс C1, у которого есть метод m1.
//C1.h is part of Xlib
@interface C1
- (void) m1;
@end
Теперь каждый раз, когда вызывается m1, MyLib должен выполнить кусок кода. Мой подход был, если я создаю категорию в MyLib:
@interface C1 (mycat)
@end
@implementation C1 (mycat)
+ (void) load{
//code to swizzle method in class C1 from: @selector(m1) to: @selector(mycat_m1)
}
- (void) mycat_m1{
/*
insert code that I want to execute
*/
[self mycat_m1]; //calling the original m1 implementation
}
@end
Проблема: MyLib не имеет класса C1. Поэтому я не могу создать MyLib, поскольку пытаюсь создать категорию для класса, который не существует. Отсюда и ошибки компиляции.
Итак, я попытался обернуть вышеупомянутый код внутри:
#if defined(__has_include)
#if __has_include("C1.h")
/* above category code */
#endif
#endif
MyLib компилируется и собирается нормально, но, поскольку C1.h отсутствует в MyLib, mylib.framework не будет иметь этой категории.
Теперь у меня есть два варианта: 1. Создать эту категорию динамически, чтобы после включения библиотеки в приложение, а затем при запуске приложения эта категория была создана в зависимости от того, включает ли TestApp Xlib или нет. 2. Удалите файл, содержащий этот код категории, из исходников компиляции, а затем каким-то образом предоставьте этот файл в моей среде для TestApp.
Я не могу решить ни один из вариантов. Есть идеи по поводу существующих опций? или какие-то новые варианты?
Изменить: Добавлены детали, так как вопрос не был достаточно объяснительным
1 ответ
Этот код не должен быть в категории.
Категории часто используются при перегрузке, потому что большую часть времени люди пытаются "обернуть" функцию своим поведением в определенном классе. Шуршание часто делается в +load
метод класса. Метод класса загрузки в категории является хорошим местом для организационной формулировки, потому что вы знаете основной класс +load
метод только что был вызван.
Swizzling все еще вполне возможно в вашем случае. Реализуйте класс в вашей структуре с +load
метод. Сверкающий код пойдет туда как обычно. Вам просто нужно найти класс, который вы хотите использовать, вместо того, чтобы предполагать, что целью является self
как было бы, если бы это было в категории. То есть, если вы ссылаетесь на сообщение в блоге или используете любимую технику для показа этих ссылок self
Знайте, что это, скорее всего, не будет. Убедитесь, что вы остерегаетесь того, к какому классу вы пытаетесь перебраться в / из.
Я должен был сделать что-то подобное в прошлом. В моем случае мне пришлось искать экземпляр класса, который реализовал протокол AppDelegate из Framework. В этом случае код, который вызвал Swizzling, не был вызван из +load
(поскольку класс AppDelegate, возможно, не был загружен, а экземпляр класса для AppDelgate определенно не был создан). В этом случае я вызвал код из моего класса -init
метод, и защитил его, чтобы он не мог быть вызван дважды. Тем не менее, я гарантированно смог создать экземпляр своего класса из кода приложения, поэтому этот метод работал; Я не уверен на 100% в вашем случае использования. Не мешало бы опубликовать код, который вы пробовали.
ОБНОВЛЕНИЕ: конкретный пример
Вот метод, который я держу под рукой для мерзлоты.
+ (void)swizzleSelector:(SEL)sel1 onClass:(Class)class1 withSelector:(SEL)sel2 fromClass:(Class)class2
{
Method method1 = class_getInstanceMethod(class1, sel1);
Method method2 = class_getInstanceMethod(class2, sel2);
// Make sure that both methods are on the target class (the one to swizzle likely already is).
class_addMethod(class1,
sel1,
method_getImplementation(method1),
method_getTypeEncoding(method1));
class_addMethod(class1, // The swizzling is 'on' the first class, so it's the target here, not class2.
sel2,
method_getImplementation(method2),
method_getTypeEncoding(method2));
// Once they are both added to the class, exchange the implementations of the methods.
method_exchangeImplementations(class_getInstanceMethod(class1,sel1),
class_getInstanceMethod(class1,sel2));
}
Как я уже сказал, вам нужно каким-то образом искать класс вашей цели, но при условии, что вы вызываете его из класса, в котором есть методы замены, и при условии, что m1:
это селектор цели, который вы пытаетесь изменить, вы можете вызвать так:
Class class = NSClassFromString(@"C1");
// Check that `class` is not `nil`
[self swizzleSelector:@selector(m1)
onClass:class
withSelector:@selector(my_m1)
fromClass:self];
Я надеюсь, что это поможет прояснить, что вы можете сделать с алкоголем. 999 из 1000, вам, вероятно, не нужно парить. Ваш вариант использования звучит как возможность, где вам, возможно, придется.