ios - 如何将主包与测试包调配

标签 ios objective-c swift swift4 swizzling

我使用 swizzle main bundle 和 test bundle 就像在 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

但我快速尝试了以下方法

     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’”

有人能帮我怎么做吗

最佳答案

此答案已在 Swift 3 & 4 Playground、任何其他版本和 YMMV 中进行测试。

您的 Objective-C 代码调配了两个类方法,您的 Swift 版本尝试调配了两个实例方法 - 所以它们做的不是同一件事。

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

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

结合以上内容,您将获得以下 Playground 代码:

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? 并且上面的代码强制执行此操作没有任何检查,这些检查应该存在 在真实代码中!

最后请注意,您的 swizzle 代码 assumes mainBundle 是由 NSBundle 直接实现的,而不是其祖先之一,这可能是一个安全的假设在这种情况下,但并非总是如此。参见示例 this question关于安全地调配。

HTH

关于ios - 如何将主包与测试包调配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47630884/

相关文章:

ios - NSPredicate如何过滤对象字段

iphone - 获取 NSFileWrapper 的路径

ios - (Swift) 如何对Core Data进行模糊搜索?

ios - swift - 警告 : attempt to present on whose view is not in the window hierarchy

objective-c - 无法从 sqlite3 数据库中选择

ios - 无法从嵌入式 Collection View 中的导航 View 访问数据

ios - 您可以将 UIGestureRecognizer 附加到多个 View 吗?

objective-c - 我可以使用自动释放来修复此内存泄漏吗?

swift - 随着 orientation-xcode 9+ 的变化更新自定义 subview (uiview 或 web View )框架/大小

ios - 使用 AFNetworking 2 高效下载大量图像(1500+)