如何在 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 个可能不需要调酒。您的用例听起来像是您可能不得不这样做的可能性。