使用 Swift 5.5 的方法调配
Method Swizzling with Swift 5.5
我正在尝试覆盖 NSURL
上的 initWithString
方法,我查看了这些过去 issues/posts。
- Method swizzling in swift 4
- https://www.uraimo.com/2015/10/23/effective-method-swizzling-with-swift/
我尝试了以下代码,但无法固定新的 log_initWithString
方法,swiftc
除了在 运行 上没有标记任何东西,我得到 index/index.swift:20: Fatal error: Unexpectedly found nil while unwrapping an Optional value
.
import AppKit
import WebKit
let app = NSApplication.shared
//example: https://github.com/kickstarter/ios-oss/blob/39edeeaefb5cfb26276112e0af5eb6948865cf34/Library/DataSource/UIView-Extensions.swift
private var hasSwizzled = false
extension NSURL {
public final class func doSwizzle() {
guard !hasSwizzled else { return }
hasSwizzled = true
let original = Selector("initWithString:")
let swizzled = Selector("log_initWithString:")
let originalMethod = class_getInstanceMethod(NSURL.self, original)!
let swizzledMethod = class_getInstanceMethod(NSURL.self, swizzled)!
let didAddMethod = class_addMethod(NSURL.self, original, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(NSURL.self, swizzled, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
@objc internal func log_initWithString(string URLString: String) {
NSLog("Hello from initWithString")
return log_initWithString(string: URLString)
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
let window = NSWindow.init(contentRect: NSRect(x: 0, y: 0, width: 750, height: 600), styleMask: [
NSWindow.StyleMask.titled,
NSWindow.StyleMask.closable,
NSWindow.StyleMask.resizable,
NSWindow.StyleMask.miniaturizable
], backing: NSWindow.BackingStoreType.buffered, defer: false)
func applicationDidFinishLaunching(_ notification: Notification) {
NSURL.doSwizzle()
let webview = WKWebView(frame: window.contentView!.frame)
let request = URLRequest(url: URL(string: "https://google.com")!)
window.contentView?.addSubview(webview)
webview.load(request)
window.makeKeyAndOrderFront(nil)
window.orderFrontRegardless()
window.center()
}
}
let delegate = AppDelegate()
app.delegate = delegate
app.run()
那是因为
@objc internal func log_initWithString(string URLString: String)
作为 log_initWithStringWithString:
而不是 log_initWithString:
.
暴露给 Objective-C
明显的修复是:
...
let swizzled = Selector("log_initWithStringWithString:")
...
要更好地进行编译时检查,您可以使用此语法:
let original = #selector(NSURL.init(string:))
let swizzled = #selector(NSURL.log_initWithString(string:))
这将编译,但至少还有一件事需要修复 - 混合方法 return 值。在您的示例中:
@objc internal func log_initWithString(string URLString: String) {
NSLog("Hello from initWithString")
return log_initWithString(string: URLString)
}
return没什么,而 NSURL
的初始化应该是 return NSURL
,所以修复是:
@objc internal func log_initWithString(string URLString: String) -> NSURL {
...
我正在尝试覆盖 NSURL
上的 initWithString
方法,我查看了这些过去 issues/posts。
- Method swizzling in swift 4
- https://www.uraimo.com/2015/10/23/effective-method-swizzling-with-swift/
我尝试了以下代码,但无法固定新的 log_initWithString
方法,swiftc
除了在 运行 上没有标记任何东西,我得到 index/index.swift:20: Fatal error: Unexpectedly found nil while unwrapping an Optional value
.
import AppKit
import WebKit
let app = NSApplication.shared
//example: https://github.com/kickstarter/ios-oss/blob/39edeeaefb5cfb26276112e0af5eb6948865cf34/Library/DataSource/UIView-Extensions.swift
private var hasSwizzled = false
extension NSURL {
public final class func doSwizzle() {
guard !hasSwizzled else { return }
hasSwizzled = true
let original = Selector("initWithString:")
let swizzled = Selector("log_initWithString:")
let originalMethod = class_getInstanceMethod(NSURL.self, original)!
let swizzledMethod = class_getInstanceMethod(NSURL.self, swizzled)!
let didAddMethod = class_addMethod(NSURL.self, original, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(NSURL.self, swizzled, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
@objc internal func log_initWithString(string URLString: String) {
NSLog("Hello from initWithString")
return log_initWithString(string: URLString)
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
let window = NSWindow.init(contentRect: NSRect(x: 0, y: 0, width: 750, height: 600), styleMask: [
NSWindow.StyleMask.titled,
NSWindow.StyleMask.closable,
NSWindow.StyleMask.resizable,
NSWindow.StyleMask.miniaturizable
], backing: NSWindow.BackingStoreType.buffered, defer: false)
func applicationDidFinishLaunching(_ notification: Notification) {
NSURL.doSwizzle()
let webview = WKWebView(frame: window.contentView!.frame)
let request = URLRequest(url: URL(string: "https://google.com")!)
window.contentView?.addSubview(webview)
webview.load(request)
window.makeKeyAndOrderFront(nil)
window.orderFrontRegardless()
window.center()
}
}
let delegate = AppDelegate()
app.delegate = delegate
app.run()
那是因为
@objc internal func log_initWithString(string URLString: String)
作为 log_initWithStringWithString:
而不是 log_initWithString:
.
明显的修复是:
...
let swizzled = Selector("log_initWithStringWithString:")
...
要更好地进行编译时检查,您可以使用此语法:
let original = #selector(NSURL.init(string:))
let swizzled = #selector(NSURL.log_initWithString(string:))
这将编译,但至少还有一件事需要修复 - 混合方法 return 值。在您的示例中:
@objc internal func log_initWithString(string URLString: String) {
NSLog("Hello from initWithString")
return log_initWithString(string: URLString)
}
return没什么,而 NSURL
的初始化应该是 return NSURL
,所以修复是:
@objc internal func log_initWithString(string URLString: String) -> NSURL {
...