首页 > 解决方案 > Swizzle `init` 并调用内部的实际实现,Swift 4

问题描述

我想调酒init(frame:)NSView但在里面打电话是真实的init(frame:)。这可能吗?我知道,我可以通过子类化和覆盖来实现相同的目标,但是在所有地方都需要改变它。我不知道如何正确实现 swizzled 方法来避免递归。我的代码:

@objc func swizzledFrameInit(frame: NSRect) {
    //?? how to call init here
    self.wantsLayer = true
}


static func swizzleInitDescription() {
    DispatchQueue.once(token: "swizzleInitDescription") {
        let originalInitWithFrameSelector = #selector(NSView.init(frame:))

        let swizzledFrameSelector = #selector(NSView.swizzledFrameInit(frame:))

        let originalFrameInit = class_getInstanceMethod(self, originalInitWithFrameSelector)

        let swizzledFrameInit = class_getInstanceMethod(self, swizzledFrameSelector)

        let didAddSwizzledFrameInit = class_addMethod(self, originalInitWithFrameSelector, method_getImplementation(swizzledFrameInit!), method_getTypeEncoding(swizzledFrameInit!))

        if didAddSwizzledFrameInit {
            class_replaceMethod(self, swizzledFrameSelector, method_getImplementation(originalFrameInit!), method_getTypeEncoding(originalFrameInit!))
        } else {
            method_exchangeImplementations(originalFrameInit!, swizzledFramaInit!)
        }
    }
}

我也尝试了几乎相同的方法,但使用convenience init并得到了递归。任何帮助,将不胜感激。

标签: swiftcocoamethod-swizzling

解决方案


撇开这是一个非常危险且不明智的混合,如果用于除探索和调试之外的任何事情,重点method_exchangeImplementations是它交换实现。这意味着旧的实现变成了混合的方法。因此,要调用您自己的旧实现:

@objc func swizzledFrameInit(frame: NSRect) {
    self.swizzledFrameInit(frame: frame)
    self.wantsLayer = true
}

通常,实现您在此处执行的操作的正确方法是wantsLayer在顶级视图上进行设置。此属性适用于所有子视图。

创建一个支持层的视图会隐式地导致该视图下的整个视图层次结构成为支持层的。因此,视图和它的所有子视图(包括子视图的子视图)变成了 layer-backed


推荐阅读