我想知道在id
上调用方法的Swift等价物是什么,其中方法的可用性是在运行时确定的。具体来说,我希望在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];
}
}];
}
最大的问题是setEnabled:
作为属性(例如UIBarItem
)在Swift中导入,并且以下任何构造都没有编译
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)
}
答案 0 :(得分:24)
事实上,您可以完全按照以前的方式执行此操作:通过调用respondsToSelector:
。实际上,这正是你提出的表达式所做的:
sender.setEnabled?(false)
该表达式实际上是一种简写 - 它首先调用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
}
}
或者:
@IBAction func doButton(sender: AnyObject) {
(sender as? UIControl)?.enabled = false
(sender as? UIBarItem)?.enabled = false
}
答案 1 :(得分:6)
在Swift 2.0 beta 4中,您的祈祷得到了回应;这段代码变得合法:
EventLogReader log2 = new EventLogReader("Microsoft-Windows-TaskScheduler/Operational");
for (EventRecord eventInstance = log2.ReadEvent(); null != eventInstance; eventInstance = log2.ReadEvent())
{
if (!eventInstance.Properties.Select(p => p.Value).Contains("\\{YOUR SCHEDULED TASK NAME}}"))
{
continue;
}
Console.WriteLine("-----------------------------------------------------");
Console.WriteLine("Event ID: {0}", eventInstance.Id);
Console.WriteLine("Publisher: {0}", eventInstance.ProviderName);
try
{
Console.WriteLine("Description: {0}", eventInstance.FormatDescription());
}
catch (EventLogException)
{
}
EventLogRecord logRecord = (EventLogRecord)eventInstance;
Console.WriteLine("Description: {0}", logRecord.FormatDescription());
}
答案 2 :(得分:2)
如果您想避免使用respondsToSelector:
方法,则可以定义协议。然后扩展您要使用的类,这些类已经符合此协议的定义(已启用),并使用符合您协议的通用变量定义该函数。
protocol Enablable{
var enabled:Bool { get set }
}
extension UIButton : Enablable {}
extension UIBarButtonItem : Enablable {}
//....
func handleEvent<T:Enablable>(var sender: T) {
sender.enabled = false
}
如果您需要将其与IBAction方法一起使用,则需要进行一些处理,因为您不能直接在它们上使用泛型。
@IBAction func handleEventPressed(sender:AnyObject){
handleEvent(sender);
}
我们还需要一个没有Enablable一致性的匹配泛型函数,这样我们就可以在不知道发送者是否可用的情况下调用handleEvent。幸运的是,编译器足够聪明,可以找出要使用的两个通用函数中的哪一个。
func handleEvent<T>(var sender: T) {
//Do Nothing case if T does not conform to Enablable
}
答案 3 :(得分:1)
作为解决方法/替代方案,您可以使用键值编码:
@IBAction func handler(sender: AnyObject) {
if sender.respondsToSelector("setEnabled:") {
sender.setValue(false, forKey:"enabled")
}
}
这适用于Swift 1.2(Xcode 6.4)和Swift 2.0(Xcode 7 beta)。