ios - Swift 中是否有 NSInvocation 的替代方案?

标签 ios swift swift3 selector nsinvocation

我正在尝试使用多个 (2+) 个参数(可以确定参数的数量)调用一个选择器。但是,选择器在编译时是未知的(实际上是用 NSSelectorFromString 生成的)。

在 Objective-C 中,我可以创建一个调用并为其设置参数并调用它。但这在 Swift 中是不可用的。有没有办法解决?喜欢:

let obj = SomeClass()
let selector = NSSelectorFromString("arg1:arg2:arg3:") //selector, arguments known only at runtime
//invoke selector

最佳答案

swift 3.1

NSInvocation 可以动态使用,但只能作为一个有趣的练习,绝对不能用于严肃的应用程序。还有更好的alternatives .

import Foundation

class Test: NSObject {
    @objc var name: String? {
        didSet {
            NSLog("didSetCalled")
        }
    }

    func invocationTest() {
        // This is the selector we want our Invocation to send
        let namePropertySetterSelector = #selector(setter:name)
        
        // Look up a bunch of methods/impls on NSInvocation
        let nsInvocationClass: AnyClass = NSClassFromString("NSInvocation")!
        
        // Look up the "invocationWithMethodSignature:" method
        let nsInvocationInitializer = unsafeBitCast(
            method_getImplementation(
                class_getClassMethod(nsInvocationClass, NSSelectorFromString("invocationWithMethodSignature:"))!
            ),
            to: (@convention(c) (AnyClass?, Selector, Any?) -> Any).self
        )
        
        // Look up the "setSelector:" method
        let nsInvocationSetSelector = unsafeBitCast(
            class_getMethodImplementation(nsInvocationClass, NSSelectorFromString("setSelector:")),
            to:(@convention(c) (Any, Selector, Selector) -> Void).self
        )
        
        // Look up the "setArgument:atIndex:" method
        let nsInvocationSetArgAtIndex = unsafeBitCast(
            class_getMethodImplementation(nsInvocationClass, NSSelectorFromString("setArgument:atIndex:")),
            to:(@convention(c)(Any, Selector, OpaquePointer, NSInteger) -> Void).self
        )
        
        // Get the method signiture for our the setter method for our "name" property.
        let methodSignatureForSelector = NSSelectorFromString("methodSignatureForSelector:")
        let getMethodSigniatureForSelector = unsafeBitCast(
            method(for: methodSignatureForSelector)!,
            to: (@convention(c) (Any?, Selector, Selector) -> Any).self
        )
        
        // ObjC:
        // 1. NSMethodSignature *mySignature = [self methodSignatureForSelector: @selector(setName:)];
        // 2. NSInvocation *myInvocation = [NSInvocation invocationWithMethodSignature: mySignature];
        // 3. [myInvocation setSelector: @selector(setName:)];
        // 4. [myInvocation setArgument: @"new name", atIndex: 2];
        // 5. [myInvocation invokeWithTarget: self];
        
        // 1.
        let namyPropertyMethodSigniature = getMethodSigniatureForSelector(self, methodSignatureForSelector, namePropertySetterSelector)

        // 2.
        let invocation = nsInvocationInitializer(
            nsInvocationClass,
            NSSelectorFromString("invocationWithMethodSignature:"),
            namyPropertyMethodSigniature
        ) as! NSObject // Really it's an NSInvocation, but that can't be expressed in Swift.
        
        // 3.
        nsInvocationSetSelector(
            invocation,
            NSSelectorFromString("setSelector:"),
            namePropertySetterSelector
        )
        
        var localName = "New name" as NSString
        
        // 4.
        withUnsafePointer(to: &localName) { stringPointer in
            nsInvocationSetArgAtIndex(
                invocation,
                NSSelectorFromString("setArgument:atIndex:"),
                OpaquePointer(stringPointer),
                2
            )
        }
        
        // 5.
        invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
    }
}

let object = Test()
object.invocationTest()

关于ios - Swift 中是否有 NSInvocation 的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40533914/

相关文章:

Swift - 使用 UIKeyboardWillChangeFrame 时关闭键盘并调用 TouchUpInside 按钮同时不起作用

ios - 如何扩展 UITextView 类?

swift - 如何从 swift 数组中删除多个项目?

php - 快速登录 POST 连接 php

ios - 如何在 iOS Swift 中处理多个 UISlider 值?

ios - 饼图文本显示不正确?

swift - 当另一个节点的子节点时,Spritekit 节点 zRotation 不保持节点的纵横比

转换为 Swift 3 后 iOS 应用程序无法启动

ios - Chartboost 委托(delegate)错误

ios - 信号已经绑定(bind)到关键路径