如何将主包与测试包调配
How do I swizzle main bundle with test bundle
我使用 swizzle 主包和测试包,就像在 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")!)
}
}
但这会引发一些错误"Argument of '#selector' cannot refer to variable 'testBundle'"
有人可以帮我怎么做吗
此答案已在 Swift 3 & 4 Playground、任何其他版本和 YMMV 中进行测试。
您的 Objective-C 代码调配了两个 class 方法,您的 Swift 版本试图调配两个实例方法 - 所以它们做的不是同一件事。
您(可能)无法调配(纯)Swift 函数,您可以调配 Objective-C 方法,这是因为 functions/methods 的调度方式不同。所以在 Swift 中替换函数必须在 Swift 4 中标记为 @objc
(它是可选的并且在 Swift 3 中显然是无害的)。
Swift 将 mainBundle
重命名为 main
并将其显示为 属性,因此要获得 mainBundle
的选择器,您需要使用 getter: main
.
结合以上内容,您将获得以下 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()
returns a Method?
和上面的代码强制此 没有 任何检查,这些检查 实际代码中应该存在!
最后请注意,您的 swizzle 代码 assumes mainBundle
是由 NSBundle
而不是其祖先之一直接实现的,这可能是一个安全的假设这种情况但并非总是如此。例如,请参阅 安全地进行 swizzling。
HTH
我使用 swizzle 主包和测试包,就像在 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")!)
}
}
但这会引发一些错误"Argument of '#selector' cannot refer to variable 'testBundle'"
有人可以帮我怎么做吗
此答案已在 Swift 3 & 4 Playground、任何其他版本和 YMMV 中进行测试。
您的 Objective-C 代码调配了两个 class 方法,您的 Swift 版本试图调配两个实例方法 - 所以它们做的不是同一件事。
您(可能)无法调配(纯)Swift 函数,您可以调配 Objective-C 方法,这是因为 functions/methods 的调度方式不同。所以在 Swift 中替换函数必须在 Swift 4 中标记为 @objc
(它是可选的并且在 Swift 3 中显然是无害的)。
Swift 将 mainBundle
重命名为 main
并将其显示为 属性,因此要获得 mainBundle
的选择器,您需要使用 getter: main
.
结合以上内容,您将获得以下 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()
returns a Method?
和上面的代码强制此 没有 任何检查,这些检查 实际代码中应该存在!
最后请注意,您的 swizzle 代码 assumes mainBundle
是由 NSBundle
而不是其祖先之一直接实现的,这可能是一个安全的假设这种情况但并非总是如此。例如,请参阅
HTH