解释这段代码:使用字符串作为过滤函数

Explain this code: uses a string as filter function

在审查我监督的一个项目中的一些 Swift 代码时,我遇到了这个:

return (realm?.objects(ExerciseDBObject.self).filter("isDeleted == false")) as! Results<ExerciseDBObject>

作为 JVM/Python/JS 程序员,让我印象深刻的是 filter("isDeleted == false") 位。据推测,这段代码工作正常:它过滤了未删除的练习,但它是一个字符串。这是如何工作的?

我没有使用过 Swift,在谷歌搜索时我刚刚看到 String#filter 上的文档,这似乎暗示我通常会把那段代码写成 filter({![=13= ].isDeleted}).

字符串的isDeleted位指的是对象上的一个道具。 Swift 如何避免将其绑定到某个也称为 isDeleted 的变量(如果存在,它不在此代码块中)?

您可能正在查看“Realm”的一项功能,我认为它是一种用于处理存储对象的工具,而不是 Swift 的一项功能。

本例中的问题是“什么是领域?.objects(...) 返回?”。猜测我会说它不是一个字符串...它是一些具有 filter 自定义定义的其他对象,它知道如何解析 String 并将其用作数据获取操作的查询。

这是方法 filter 上的多态性。它可以根据调用它的实体的 class 做不同的事情。

正在使用:

// MARK: Filtering
/**
 Returns a `Results` containing all objects matching the given predicate in the collection.
 - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
 */
func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element>

来源:RealmCollectiton

在幕后,它使用 NSPredicate(format:),它通过简化和避免每次写入 NSPredicate(format: ...) 来“隐藏”它,并且也应该使用 KeyPath。在 Predicate Format String Syntax.

处可以详细了解您可以用它做什么

没有

func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> [Self.Element]

Sequence

这里是 illustrate/mimic 的示例代码:

class TestClass: NSObject {
    @objc var title: String
    init(title: String) {
        self.title = title
        super.init()
    }
    override var description: String {
        "TestClass - title: \(title)"
    }
}

let objects: [TestClass] = [TestClass(title: "Title1"), TestClass(title: "Title2")]

// In a "Swifty way
let searchedText = "Title1"
let filtered1 = objects.filter {
    [=12=].title == searchedText
}
print("filtered1: \(filtered1)")

// With a NSPredicate, which is more Objective-C (filtering on NSArray for instance)
// But also there is a translation with Core-Data, and then fetching optimization
let filtered2 = objects.filter {
    NSPredicate(format: "title == 'Title1'").evaluate(with: [=12=])
}
print("filtered2: \(filtered2)")
// Same, but with avoiding hard coding strings
let filtered3 = objects.filter {
    NSPredicate(format: "%K == %@", #keyPath(TestClass.title), searchedText).evaluate(with: [=12=])
}
print("filtered3: \(filtered3)")

extension Sequence {
    func filter(_ predicateFormat: String, _ args: Any...) -> [Element] {
        let predicate = NSPredicate(format: predicateFormat, argumentArray: args)
        return self.filter({ predicate.evaluate(with: [=12=]) })
    }
}

// With an extension similar to what does Realm
let filtered4 = objects.filter("title == 'Title1'")
print("filtered4: \(filtered4)")

// With an extension similar to what does Realm, and less hard coded string (cf filtered3 construction)
let filtered5 = objects.filter("%K == %@", #keyPath(TestClass.title), searchedText)
print("filtered5: \(filtered5)")

输出:

$>filtered1: [TestClass - title: Title1]
$>filtered2: [TestClass - title: Title1]
$>filtered3: [TestClass - title: Title1]
$>filtered4: [TestClass - title: Title1]
$>filtered5: [TestClass - title: Title1]