如何在 objective c 中动态创建类别?
How to create categories dynamically in objective c?
我正在从事 objective-c 图书馆项目 ('MyLib')。
假设此库随后包含在 'TestApp' 中。现在,'TestApp' 还包括另一个名为 'Xlib' 的库。这个 Xlib 有一个 class 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 没有 class C1。所以我无法构建 MyLib,因为我试图在 class 上创建一个不存在的类别。因此,编译错误。
所以,然后我尝试将上面的代码包装在里面:
#if defined(__has_include)
#if __has_include("C1.h")
/* above category code */
#endif
#endif
MyLib 现在可以很好地编译和构建,但是由于 C1.h 不存在于 MyLib 中,因此 mylib.framework 不会有这个类别。
现在,我有两个选择:
1.动态创建这个类,这样一旦这个库被包含在app中,然后当app运行时,这个类就会根据TestApp是否包含Xlib来创建。
2. 从编译源中删除包含此类代码的文件,然后以某种方式将该文件在我的框架中暴露给 TestApp。
我无法解决任何一个选项。对现有选项有什么想法吗?或任何新选项?
编辑:添加了详细信息,因为问题不是很清楚
此代码不需要在类别中。
在 swizzling 时经常使用类别,因为大多数时候,人们都试图 "wrap" 一个具有特定 class 行为的函数。 Swizzling 通常在 +load
class 方法中完成。 Category 上的 load class 方法是组织性地放置它的好地方,因为您知道主 class 的 +load
方法刚刚被调用。
在你的情况下,Swizzling 仍然很有可能。使用 +load
方法在您的框架中实现 class。 swizzling 代码将像往常一样去那里。您只需要查找要调配的 Class,而不是假设目标引用是 self
,就像它在类别中一样。也就是说,如果您正在引用博客 post 或使用最喜欢的技术来混合引用 self
,请知道它可能不会。一定要注意 class 你试图混合的东西 to/from.
我过去不得不做类似的事情。在我的例子中,我必须查找 class 的实例,它从框架中实现了 AppDelegate 协议。在那种情况下,触发调配的代码不是从 +load
调用的(因为 AppDelegate class 可能尚未加载,并且 AppDelgate 的 class 的实例肯定是'没有实例化)。在那种情况下,我从我的 class 的 -init
方法中调用代码,并对其进行保护,使其不能被调用两次。但是,我保证能够从应用程序代码中实例化我的 class,因此该技术有效;我不是 100% 确定你的用例。 post 您尝试过的代码不会有什么坏处。
更新:具体示例
这是我用来调配的方法。
+ (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));
}
就像我说的,您需要以某种方式查找目标的 class,但假设您是从具有替换方法的 class 调用它,并假设 m1:
是您要调配的目标选择器,您可以这样调用:
Class class = NSClassFromString(@"C1");
// Check that `class` is not `nil`
[self swizzleSelector:@selector(m1)
onClass:class
withSelector:@selector(my_m1)
fromClass:self];
我希望这有助于阐明您可以使用 swizzling 做什么。 1000 个中有 999 个可能不需要调酒。您的用例听起来像是您可能不得不这样做的可能性。
我正在从事 objective-c 图书馆项目 ('MyLib')。 假设此库随后包含在 'TestApp' 中。现在,'TestApp' 还包括另一个名为 'Xlib' 的库。这个 Xlib 有一个 class 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 没有 class C1。所以我无法构建 MyLib,因为我试图在 class 上创建一个不存在的类别。因此,编译错误。
所以,然后我尝试将上面的代码包装在里面:
#if defined(__has_include)
#if __has_include("C1.h")
/* above category code */
#endif
#endif
MyLib 现在可以很好地编译和构建,但是由于 C1.h 不存在于 MyLib 中,因此 mylib.framework 不会有这个类别。
现在,我有两个选择: 1.动态创建这个类,这样一旦这个库被包含在app中,然后当app运行时,这个类就会根据TestApp是否包含Xlib来创建。 2. 从编译源中删除包含此类代码的文件,然后以某种方式将该文件在我的框架中暴露给 TestApp。
我无法解决任何一个选项。对现有选项有什么想法吗?或任何新选项?
编辑:添加了详细信息,因为问题不是很清楚
此代码不需要在类别中。
在 swizzling 时经常使用类别,因为大多数时候,人们都试图 "wrap" 一个具有特定 class 行为的函数。 Swizzling 通常在 +load
class 方法中完成。 Category 上的 load class 方法是组织性地放置它的好地方,因为您知道主 class 的 +load
方法刚刚被调用。
在你的情况下,Swizzling 仍然很有可能。使用 +load
方法在您的框架中实现 class。 swizzling 代码将像往常一样去那里。您只需要查找要调配的 Class,而不是假设目标引用是 self
,就像它在类别中一样。也就是说,如果您正在引用博客 post 或使用最喜欢的技术来混合引用 self
,请知道它可能不会。一定要注意 class 你试图混合的东西 to/from.
我过去不得不做类似的事情。在我的例子中,我必须查找 class 的实例,它从框架中实现了 AppDelegate 协议。在那种情况下,触发调配的代码不是从 +load
调用的(因为 AppDelegate class 可能尚未加载,并且 AppDelgate 的 class 的实例肯定是'没有实例化)。在那种情况下,我从我的 class 的 -init
方法中调用代码,并对其进行保护,使其不能被调用两次。但是,我保证能够从应用程序代码中实例化我的 class,因此该技术有效;我不是 100% 确定你的用例。 post 您尝试过的代码不会有什么坏处。
更新:具体示例
这是我用来调配的方法。
+ (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));
}
就像我说的,您需要以某种方式查找目标的 class,但假设您是从具有替换方法的 class 调用它,并假设 m1:
是您要调配的目标选择器,您可以这样调用:
Class class = NSClassFromString(@"C1");
// Check that `class` is not `nil`
[self swizzleSelector:@selector(m1)
onClass:class
withSelector:@selector(my_m1)
fromClass:self];
我希望这有助于阐明您可以使用 swizzling 做什么。 1000 个中有 999 个可能不需要调酒。您的用例听起来像是您可能不得不这样做的可能性。