什么是在`id`上设置属性的快速等价物?

adi*_*dib 11 objective-c swift

我想知道Swift在调用方法时id是什么,在方法中可以在运行时确定方法的可用性.具体来说,我想在Swift中做这个模式:

-(IBAction) handleEvent:(id) sender {
    BOOL didDisable = NO;
    if([sender respondsToSelector:@selector(setEnabled:)]) {
        [sender setEnabled:NO];
        didDisable = YES;
    }
    [self doSomethingAsyncWithCompletionHandler:^{
        if(didDisable) {
            [sender setEnabled:YES];
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

最大的问题是 setEnabled:在Swift中作为属性导入(例如UIBarItem)并且以下构造都没有编译

func handleEvent(sender: AnyObject) {
    // Error: AnyObject does not have a member named "enabled"
    sender.enabled? = false

    // Error: (BooleanLiteralCompatible) -> _ is not identical to Bool
    sender.setEnabled?(false)
}
Run Code Online (Sandbox Code Playgroud)

mat*_*att 24

事实上,你可以按照以前的方式完成它:通过调用respondsToSelector:.实际上,这正是您提出的表达式所做的:

sender.setEnabled?(false)
Run Code Online (Sandbox Code Playgroud)

该表达式实际上是一个简写 - 它respondsToSelector:首先调用,然后setEnabled:仅在respondsToSelector:测试通过时调用.不幸的是,正如你所说,你无法编译代码.然而,这仅仅是斯威夫特已知的可用方法库的一个怪癖.事实是,虽然编译它有点棘手,但它可以完成 - 一旦你得到它编译,它的行为就像你期望的那样.

但是,我不打算解释如何编译它,因为我不想鼓励这种诡计.在Swift中不鼓励这种动态消息传递.一般而言,Swift中不需要动态消息传递技巧,例如键值编码,内省等,并且与Swift的强类型方法不一致.以Swift的方式做事情会更好,通过选择性地投射到你有理由相信这个东西可能具有enabled属性的东西.例如:

@IBAction func doButton(sender: AnyObject) {
    switch sender {
    case let c as UIControl: c.enabled = false
    case let b as UIBarItem: b.enabled = false
    default:break
    }
}
Run Code Online (Sandbox Code Playgroud)

要么:

@IBAction func doButton(sender: AnyObject) {
    (sender as? UIControl)?.enabled = false
    (sender as? UIBarItem)?.enabled = false
}
Run Code Online (Sandbox Code Playgroud)

  • 你可以自由地这样做!然而,有趣的是,我已将我自己的所有应用程序翻译成Swift; 我很大程度上使用了动态,并且最初惊恐地发现`performSelector:`在行动中缺失 - 然而在每种情况下我都找到了更好,更快捷的方式 - 我的代码现在更清洁,更安全.不告诉你该怎么做; 只是报告我自己的经验.当然,即使在Swift中,如果您需要它们,也无法阻止您进入旧的Objective-C Runtime调用. (3认同)
  • 竖起大拇指不鼓励臭臭的代码 (2认同)

mat*_*att 6

在Swift 2.0 beta 4中,您的祈祷得到了回应; 这段代码变得合法:

@IBAction
func handleEvent(sender: AnyObject) {
    if sender.respondsToSelector("setHidden:") {
        sender.performSelector("setHidden:", withObject: true)
    }
}
Run Code Online (Sandbox Code Playgroud)