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

Is it possible in Swift to execute code at runtime from a string?

我有一个具有相关条件的项目列表。我想将此项目列表及其条件存储在 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 文件中的字符串中。

谢谢!

你最接近的是 NSPredicate and/or 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 和键值,因此存在一些限制:

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

这听起来像是 predicates 的工作!

Swift 中的函数式编程构造 — mapfilter、shorthand 闭包等 — 是表达数据之间关系的绝佳工具命令式代码。但它们并不是看待此类问题的唯一方法。

特别是,能够将数据 之间的关系表示为数据 :

通常很有用
  • 如果您从一个简单的文件表示加载一堆数据模型对象,您也可以这样编码它们的关系。

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

  • 如果您正在处理后端数据库/Web 服务/RDBMS/ORM(CloudKit、核心数据等),您需要一种表达过滤器、查询等内容的方法、排序顺序和关系,您可以将它们传递给后端,让昂贵的大数据处理操作在那里发生。

这就是 NSPredicate (and, relatedly, 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 先发制人。这更像是一个 "why" / 高级答案,但他有一些很好的详细信息,例如在枚举案例、数据文件内容和谓词格式之间设置映射。