3

我正在开发一个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 现在可以正常编译和构建,但由于 MyLib 中不存在 C1.h,因此 mylib.framework 不会有此类别。

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

我无法解决任何一个选项。关于现有选项的任何想法?或任何新的选择?

编辑:添加了详细信息,因为问题不是很清楚

4

1 回答 1

5

此代码不需要属于某个类别。

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

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

我过去不得不做类似的事情。就我而言,我必须从框架中查找实现 AppDelegate 协议的类的实例。在这种情况下,不会调用触发 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];

我希望这有助于澄清你可以用 swizzling 做什么。1000 人中有 999 人,你可能不需要调酒。您的用例听起来像是您可能不得不这样做的可能性。

于 2017-10-25T14:20:35.013 回答