如何使用测试包调配主捆绑包

时间:2017-12-04 10:08:21

标签: ios objective-c swift swift4 swizzling

我习惯用测试包调整主要包,如下面的obj c

#import "NSBundle+Bundle.h"
#import <objc/runtime.h>

@implementation NSBundle (Bundle)

+(void)loadSwizzler {
    static dispatch_once_t once_token;
    dispatch_once(&once_token,  ^{
        Method originalMethod = class_getClassMethod(self, @selector(mainBundle));
        Method extendedMethod = class_getClassMethod(self, @selector(bundleForTestTarget));
        //swizzling mainBundle method with our own custom method
        method_exchangeImplementations(originalMethod, extendedMethod);
    });
}

//method for returning app Test target
+(NSBundle *)bundleForTestTarget {
    NSBundle * bundle = [NSBundle bundleWithIdentifier:@"Philips.AppInfraTests"];

    return bundle;
}

@end

但我在swift

中尝试了以下相同的内容
     extension Bundle {
  class func swizzle() {
        let originalSelector = #selector(mainBundle)
        let swizzledSelector = #selector(testBundle)
        let originalMethod = class_getInstanceMethod(self, originalSelector)
        let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }

    func mainBundle() -> Bundle
    {
        return Bundle.main
    }
    func testBundle() -> Bundle
    {
        return Bundle(for: self.classNamed("swizzler")!)
    }
}

但是这会引发一些错误“#selector'的参数不能引用变量'testBundle'”

有人可以帮助我如何做到这一点

1 个答案:

答案 0 :(得分:0)

这个答案已在Swift 3&amp ;; 4游乐场,任何其他版本和YMMV。

你的Objective-C代码调用了两个类方法,你的Swift版本试图调用两个实例方法 - 所以他们没有做同样的事情。

你(可能)不能调配(纯)Swift函数,你可以调配Objective-C方法,这是由于调度函数/方法的不同。所以在Swift中,替换函数必须在Swift 4中标记为@objc(它是可选的,在Swift 3中显然是无害的。)

Swift将mainBundle重命名为main并将其作为属性显示,因此要获取mainBundle的选择器,您需要使用getter: main

结合上述内容,您将获得以下游乐场代码:

extension Bundle
{
    class func swizzle()
    {
        let originalSelector = #selector(getter: main)
        let swizzledSelector = #selector(testBundle)
        let originalMethod = class_getClassMethod(self, originalSelector)!
        let swizzledMethod = class_getClassMethod(self, swizzledSelector)!
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }

    @objc class func testBundle() -> Bundle
    {
        // just for testing in Playground
        return Bundle(path: "/Applications/TextEdit.app")!
    }
}

let b = Bundle.main
print(b)
Bundle.swizzle()
let c = Bundle.main
print(c)

打印:

NSBundle </Applications/Xcode.app> (not yet loaded)
NSBundle </Applications/TextEdit.app> (not yet loaded)

请注意,class_getClassMethod()会返回Method?,而上述代码强制执行此而不进行任何检查,这些检查应该存在实际代码!< / p>

最后请注意,您的混合代码假定 mainBundleNSBundle而不是其祖先之一直接实现,在这种情况下这可能是一个安全的假设,但不是总是。有关安全调配的问题,请参阅this question

HTH