如何在 Objective C 中动态创建类别?

use*_*992 3 objective-c swizzling ios objc-category method-swizzling

我正在开发一个 Objective-C 库项目(“MyLib”)。假设这个库包含在“TestApp”中。现在,“TestApp”还包含另一个名为“Xlib”的库。该 Xlib 有一个类 C1,其中有一个方法 m1。

//C1.h is part of Xlib
@interface C1
- (void) m1;
@end
Run Code Online (Sandbox Code Playgroud)

现在,每次调用 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
Run Code Online (Sandbox Code Playgroud)

问题: MyLib 没有类 C1。所以我无法构建 MyLib,因为我试图在不存在的类上创建一个类别。因此,编译错误。

所以,然后我尝试将上面的代码包装在里面:

#if defined(__has_include)
#if __has_include("C1.h")
    /* above category code */
#endif
#endif
Run Code Online (Sandbox Code Playgroud)

MyLib 现在可以正常编译和构建,但由于 MyLib 中不存在 C1.h,因此 mylib.framework 不会有此类别。

现在,我有两个选择: 1. 动态创建此类别,以便一旦库包含在应用程序中,然后当应用程序运行时,将根据 TestApp 是否包含 Xlib 创建此类别。2. 从编译源中删除具有此类别代码的文件,然后以某种方式将该文件在我的框架中公开给 TestApp。

我无法解决这两个选项中的任何一个。对现有选项有什么想法吗?或者有什么新的选择?

编辑:添加详细信息,因为问题不太解释

gre*_*ser 5

此代码不需要位于类别中。

混合时经常使用类别,因为大多数时候,人们试图将函数与自己的行为“包装”在特定的类中。Swizzling 通常在类方法中完成+load。Category 上的加载类方法是一个很好的组织位置,因为您知道主类的+load方法刚刚被调用。

在你的情况下,混合仍然是很有可能的。使用方法在框架中实现一个类+load。调配代码将像往常一样去那里。您只需要查找要混合的类,而不是假设目标引用是self喜欢它在类别中的情况。也就是说,如果您引用一篇博客文章或使用最喜欢的技术来混合该引用self,请知道它可能不会。请务必留意您要转入/转出的班级。

我过去也曾做过类似的事情。就我而言,我必须从框架查找实现 AppDelegate 协议的类的实例。在这种情况下,不会调用触发调配的代码+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));
}
Run Code Online (Sandbox Code Playgroud)

就像我说的,您需要以某种方式查找目标的类,但假设您从具有替换方法的类中调用它,并且假设m1:是您要尝试混合的目标选择器,您可能会像这样调用:

Class class = NSClassFromString(@"C1");
// Check that `class` is not `nil`

[self swizzleSelector:@selector(m1)
              onClass:class
         withSelector:@selector(my_m1)
            fromClass:self];
Run Code Online (Sandbox Code Playgroud)

我希望这有助于阐明您可以使用 swizzling 做什么。1000 中的 999,你可能不需要 swizzle。您的用例听起来像是您可能必须这样做的可能性。