Swift swizzling 初始化函数

Swift swizzling init function

我正在尝试使用 Swift 中的文件 URL 捕获 InputStream 的初始化。我已经在 Objective-C 中为 NSInputStream class 成功实现了这样的捕获。 问题是在交换初始化器之后,不会触发 swizzled 方法。此外,方法交换后,直接调用 swizzled 方法并不会导致其执行,这意味着它的实现已成功交换。我想知道这种奇怪行为的原因是什么,因为方法已成功调配,但不清楚是哪种方法被调配的方法取代了。在 Swift.

中提供了当前实施 InputStream swizzling 的示例
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
  if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
     let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)  {
      method_exchangeImplementations(originalMethod, swizzledMethod)
  }
}

extension InputStream {
  @objc dynamic func swizzledInit(url: URL) -> InputStream? {
      print("Swizzled constructor")
      return self.swizzledInit(url: url)
  }
  
  static func Swizzle() {
      swizzling(InputStream.self, #selector(InputStream.init(url:)), #selector(swizzledInit(url:)))
  }
}

来自InputStream documentation:

NSInputStream is an abstract superclass of a class cluster consisting of concrete subclasses of NSStream that provide standard read-only access to stream data.

这里的关键:当你创建一个InputStream时,你得到的对象将不是InputStream类型,而是[=11]的(私有)子class =].与 Foundation 集合类型(NSArray/NSMutableArrayNSDictionary/NSMutableDictionary 等非常相似),您与之交互的父类型不是 有效的 您正在处理的类型:当您 +alloc 这些类型之一时,返回的对象通常是私有子 class.

在大多数情况下,这是不相关的实现细节,但在您的情况下,因为您正在尝试调整 初始化程序 ,您实际上关心从 +alloc,因为你正在调配一个永远不会被调用的初始化程序。

InputStream的具体情况下,从+[NSInputStream alloc]返回的值是私有的NSCFInputStreamclass,这是共享的有效免费桥接类型与 CoreFoundation。 这是私有实现细节,可能随时更改,但您可以在 that 上调整初始化程序class 改为:

guard let class = NSClassFromString("NSCFInputStream") else {
    // Handle the fact that the class is absent / has changed.
    return
}

swizzling(class, #selector(InputStream.init(url:)), #selector(swizzledInit(url:)))

请注意,如果您要提交应用以供 App Store 审核,包含私人 class 名称可能会影响审核流程。