Swift中是否可以通过字符串在运行时执行代码?

3
我有一个与条件相关联的项目列表。我想将这个项目列表及其条件存储在 plist 文件中,而不是硬编码到 .swift 文件中。
唯一的问题是每个项目都需要关联一个函数来检查条件。以下是硬编码方式的示例:
let myJobStep1 = JobStep(heading: "My Heading", description: "This is the description", warningText: "", condition_check: { () -> Bool in
    return (self.trayColor == .Blue) || (self.trayColor == .Red)
})

let myJobStep2 = JobStep(heading: "My Heading", description: "Another description", warningText: "", condition_check: { () -> Bool in
    return (self.trayColor == .Green)
})

问题是如何将检查条件的函数封装在一个可以在plist文件中的字符串中的函数中。谢谢!
2个回答

3

你可能会用到的最接近的工具是NSPredicate和/或NSExpression,它们可以让你有限地动态评估以字符串形式给出的表达式。

enum Colors : Int {
    case Red = 1
    case Green = 2
    case Blue = 3
}

class Line : NSObject {
    var lineColor : Int

    init(lineColor:Colors) {
        self.lineColor = lineColor.rawValue
    }
}

let red = Line(lineColor: .Red)
let green = Line(lineColor: .Green)

let basic = NSPredicate(format: "self.lineColor == $Red")
let test = basic.predicateWithSubstitutionVariables(["Red":Colors.Red.rawValue])

test.evaluateWithObject(red)       // true
test.evaluateWithObject(green)     // false

由于NSExpression基于Objective-C和Key Values,因此有一些限制:

  1. 被评估的对象必须源自NSObject
  2. 属性必须自动转换为AnyObject,因此我们存储int而不是Color。请注意,您可以通过使用派生属性来稍微处理此问题。
  3. 替换字典必须为[String:AnyObject],因此您必须使用枚举原始值而不是枚举值本身。

你不需要可变属性来使用键值编码(KVC):class Foo: NSObject { let val = "Bar" }; let f = Foo(); f.valueForKey("val") 能够正常工作。(还有一些更长的变体也适用,但无法放在注释中。) - rickster
所以它确实可以,出于某种原因,最初它对我不起作用,但这很可能是因为我开始时将其类型设置为Colors。 - David Berry
感谢您提供完整的示例,并展示了如何使用枚举值。在您和@rickster的回答之间,这正是我所需要的。谢谢! - chaimp

1
这似乎是谓词的工作!
Swift中的函数式编程构造 - mapfilter、简写闭包等等,是表达数据关系时在命令式代码中使用的好工具。但这不是看待这些问题的唯一方式。
特别是,能够将数据之间的关系表达为数据通常是非常有用的:
  • 如果你从简单的文件表示中加载一堆数据模型对象,则可以将它们的关系编码为这样。

  • 如果你将数据关系编码为数据,则可以在运行时加载/更新/下载它们,而不是更改代码并发布新的应用程序二进制文件。

  • 如果你正在处理后端数据库/网络服务/RDBMS/ORM(CloudKit、Core Data等),则需要一种表达过滤器、查询、排序和关系的方法,你可以将它们传递给后端,并让大量昂贵的数据计算操作在那里发生。

这就是NSPredicate(以及相关的NSSortDescriptor)的用途。与过滤器/比较闭包不同,谓词关联的基本逻辑可以是数据而不是代码。因此,您可以拥有以下类似的数据源:
[
    "heading": "My Heading",
    "description": "This is the description",
    "condition": "trayColor == \"Blue\" || trayColor == \"Red\""
]

然后,您可以从数据源创建谓词:
let predicate = NSPredicate(format: item.condition)

使用它们来测试单个对象的条件、过滤集合等:

predicate.evaluateWithObject(item)
(items as NSArray).filteredArrayUsingPredicate(predicate)

编辑: @DavidBerry比我更快地回答了这个问题。这更多是一个“为什么”/高层次的答案,但他的回答包含一些关于设置枚举情况、数据文件内容和谓词格式之间映射的细节。


哇,太棒了。谢谢!我甚至不知道我在寻找的东西有一个特定的名称 - NSPredicate。这非常有帮助。你觉得谓词可以包含枚举值吗?(例如 trayColor == TrayColor.BluetrayColor == .Blue)?我想我可以试一下并找出来... - chaimp
1
枚举是Swift的一种特性,而NSPredicate是一个派生自ObjC的API。有多种方法可以解决这个问题 - @DavidBerry在他的回答中展示了其中一种方法。另一个选择是让您的类公开第二个(计算)属性,该属性将枚举值表示为int或string或其他内容。 - rickster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接