NSLocale 使用方法 swizzling 更改 currentLocale 输出以进行测试

NSLocale using method swizzling to change the currentLocale output for testing purposes

我正在尝试更改设备 currentLocale 输出以执行一些有趣的单元测试,这是我正在使用的代码,但返回的 currentLocale 似乎没有被覆盖。有什么提示吗?

extension NSLocale {
    class func frLocale()->NSLocale{
        return NSLocale(localeIdentifier: "fr_FR")
    }

    class func forceCurrentLocale(){
        let originalSelector = #selector(NSLocale.currentLocale)
        let swizzledSelector = #selector(self.frLocale)

        let originalMethod = class_getClassMethod(self, originalSelector)
        let swizzledMethod = class_getClassMethod(self, swizzledSelector)

        let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

        if didAddMethod {
            class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

// 编辑

上面的代码不起作用。但是如果我这样写它就可以了:

class func forceCurrentLocale(){
    let originalSelector = #selector(NSLocale.currentLocale)
    let swizzledSelector = #selector(NSLocale.frLocale)

    let originalMethod = class_getClassMethod(self, originalSelector)
    let swizzledMethod = class_getClassMethod(self, swizzledSelector)

    method_exchangeImplementations(originalMethod, swizzledMethod)
}

在那种情况下 class_addMethod 有什么问题?

你的第一个方法是正确的,可以调配一个实例方法, 但不适用于 class 方法。 发生了什么——如果我理解正确的话——是

let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

实例方法 添加到 class 和 return 的 true。然后

 class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))

替换失败的实例方法

如果您查看来自 NSHipster 的 Method Swizzling 文章,您会发现以下评论:

// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);

翻译成 Swift 即

class func forceCurrentLocale(){
    let originalSelector = #selector(NSLocale.currentLocale)
    let swizzledSelector = #selector(self.frLocale)

    let classObject : AnyClass = object_getClass(self)

    let originalMethod = class_getClassMethod(classObject, originalSelector)
    let swizzledMethod = class_getClassMethod(classObject, swizzledSelector)

    let didAddMethod = class_addMethod(classObject, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

    if didAddMethod {
        class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

然后 swizzling 按预期工作。 (关键点在于 class_addMethod() 在 class 对象上调用,而不是在 self 上调用。)

但实际上我看不出比你的第二种方法有任何优势。 didAddMethod 将永远 return false 因为 frLocale 已经定义为 NSLocale.

的 class 方法