如何混合 Swift.print(items:separator:terminator)

How to swizzle Swift.print(items:separator:terminator)

我正在寻找调整 Swift.print 函数的方法。覆盖它不是一个选项,因为如果您使用 Swift.print(:)

它可能会被绕过

选择器无法识别标识符:

@objc class func printSwizzle() {
    guard let instance = class_getInstanceMethod(self, #selector(print(separator:terminator:))),
    let swizzleInstance = class_getInstanceMethod(self, #selector(swizzlePrint(separator:terminator:))) else { return }
    method_exchangeImplementations(instance, swizzleInstance)
}

这可能吗?因为 swizzling 是一个 obj-c 运行时特性。

通过 Alexander and Carpsen90

添加评论

Method Swizzling 是一个 Objective-C 运行时特性,它在 Swift 中不可用,因为 Swift 不是动态语言。但是,您可以像 那样拥有一个全局函数。以下是 Swift 4.2 的更新代码。 但不幸的是,如果函数名称是 print,它会调用原始的 Swift.print 函数。所以我把函数名改成了logs

public func logs(items: Any..., separator: String = " ", terminator: String = "\n") {
    let output = items.map { "*\([=10=])"}.joined(separator: " ")
    Swift.print(output, terminator: terminator)
}

另一种可能的选择是将其放入协议中

public protocol CustomPrintable {
    func print(_ items: Any...)
}

extension CustomPrintable {
    func print(_ items: Any...) {
        let output = items.map { "*\([=11=])"}.joined(separator: " ")
        Swift.print("****" + output)
    }
}

用法

class SampleClass : CustomPrintable {

  func printValue() {
      print ("Hello World")
  }
}

但是这样函数就不再是全局函数了,在使用 print 时你必须 select 正确的方法

输出

*****Hello World

方法调配是一项 Objective-C 功能,可让您在运行时交换方法的实现。为此,您需要一个继承自 NSObject@objc 对象。你需要一个方法.

Swift.print 不是一种方法。它是在 Swift 模块中声明的函数。我们可以说它是全球性的,但它并不是真正的全球性。它是在模块 Swift 中定义的,它会自动导入到每个 Swift 代码中,因此您可以在没有 Swift. 前缀的情况下使用它。

综上所述,没办法swizzleSwift.print.

你可以做的是隐藏使用你自己的实现的函数,也就是说,如果你在你自己的模块中声明一个同名的函数,那么当print 时,编译器会更喜欢你的函数,因为当前模块中的函数优先于其他模块(包括 Swift. 模块)中的函数。

public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    let output = items.map { "\([=10=])" }.joined(separator: separator)
    Swift.print(output, terminator: terminator)
}

你可以在其中添加任何你想要的逻辑。

使用它从生产中删除日志记录实际上很常见,例如:

#if !DEBUG

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {}
func debugPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") {}

#endif

有关详细信息,请参阅 Remove println() for release version iOS Swift

本质上,您可以通过在模块中重新声明来隐藏整个 Swift 模块,例如作为 enum,因此禁用对 Swift.print:

的调用
enum Swift {
    public static func print(_ items: Any..., separator: String = " ", terminator: String = " ") {
        // do something
    }
}

但是,我通常不建议这样做,因为很难解决与 Swift. 模块内的标准库的任何命名冲突。

一般来说,我会建议实施您的自定义日志记录系统并通过其他方式强制使用它,例如代码审查或 linting 规则(例如 swiftlint)。