This document describes in general how you use predicates, and how the use of predicates may influence the structure of your application data.
- 本文档概述了如何使用谓词,以及谓词的使用如何影响应用程序数据的结构。
Evaluating Predicates
To evaluate a predicate, you use the NSPredicate method evaluateWithObject: and pass in the object against which the predicate will be evaluated. The method returns a Boolean value—in the following example, the result is YES.
- 要评估谓词,可以使用NSPredicate方法evaluateWithObject:并传入将对其求值谓词的对象。 该方法返回一个布尔值 - 在以下示例中,结果为YES。
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"SELF IN %@", @[@"Stig", @"Shaffiq", @"Chris"]];
BOOL result = [predicate evaluateWithObject:@"Shaffiq"];
You can use predicates with any class of object, but the class must support key-value coding for the keys you want to use in a predicate.
- 您可以将谓词与任何类对象一起使用,但该类必须支持要在谓词中使用的键的键值编码。
Using Predicates with Arrays
NSArray and NSMutableArray provide methods to filter array contents. NSArray provides filteredArrayUsingPredicate: which returns a new array containing objects in the receiver that match the specified predicate. NSMutableArray provides filterUsingPredicate: which evaluates the receiver’s content against the specified predicate and leaves only objects that match.
- NSArray和NSMutableArray提供了过滤数组内容的方法。 NSArray提供filteredArrayUsingPredicate:它返回一个新数组,其中包含接收器中与指定谓词匹配的对象。 NSMutableArray提供filterUsingPredicate:它根据指定的谓词计算接收者的内容,并仅保留匹配的对象。
NSMutableArray *names = [@[@"Nick", @"Ben", @"Adam", @"Melissa"] mutableCopy];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB = [names filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Ben" }.
NSPredicate *ePredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"];
[names filterUsingPredicate:ePredicate];
// array now contains { @"Ben", @"Melissa" }
If you use the Core Data framework, the array methods provide an efficient means of filtering an existing array of objects without—as a fetch does—requiring a round trip to a persistent data store.
- 如果使用Core Data框架,则数组方法提供了一种有效的方法来过滤现有的对象数组,而不需要进行提取 - 需要往返于持久数据存储。
Using Predicates with Key-Paths
Recall that you can follow relationships in a predicate using a key path. The following example illustrates the creation of a predicate to find employees that belong to a department with a given name (but see also Performance).
- 回想一下,您可以使用键路径跟踪谓词中的关系。 以下示例说明了如何创建谓词以查找属于具有给定名称的部门的员工(但另请参见性能)。
NSString *departmentName = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"department.name like %@", departmentName];
If you use a to-many relationship, the construction of a predicate is slightly different. If you want to fetch Departments in which at least one of the employees has the first name "Matthew," for instance, you use an ANY operator as shown in the following example:
- 如果使用to-many关系,则谓词的构造会略有不同。 如果要获取其中至少有一个员工的名字为“Matthew”的部门,则使用ANY运算符,如以下示例所示:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"ANY employees.firstName like 'Matthew'"];
If you want to find Departments in which at least one of the employees is paid more than a certain amount, you use an ANY operator as shown in the following example:
- 如果要查找其中至少有一名员工的薪水超过一定金额的部门,则使用ANY运算符,如以下示例所示:
float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY employees.salary > %f", salary];
Using Null Values
A comparison predicate does not match any value with null except null (nil) or the NSNull null value (that is, ($
value == nil) returns YES if $
value is nil). Consider the following example.
- 除了null(nil)或NSNull null值(即
$
value == nil)如果$ value为nil,则比较谓词与null匹配任何值都不匹配。 请考虑以下示例。
NSString *firstName = @"Ben";
NSArray *array = @[ @{ @"lastName" : "Turner" }];
@{ @"firstName" : @"Ben", @"lastName" : @"Ballard",
@"birthday", [NSDate dateWithString:@"1972-03-24 10:45:32 +0600"] } ];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"firstName like %@", firstName];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
NSLog(@"filteredArray: %@", filteredArray);
// Output:
// filteredArray ({birthday = 1972-03-24 10:45:32 +0600; \\
firstName = Ben; lastName = Ballard;})
The predicate does match the dictionary that contains a value Ben for the key firstName, but does not match the dictionary with no value for the key firstName. The following code fragment illustrates the same point using a date and a greater-than comparator.
- 谓词确实匹配包含密钥firstName的值Ben的字典,但与密钥firstName没有值的字典不匹配。 以下代码片段使用日期和大于比较器说明了相同的点。
NSDate *referenceDate = [NSDate dateWithTimeIntervalSince1970:0];
predicate = [NSPredicate predicateWithFormat:@"birthday > %@", referenceDate];
filteredArray = [array filteredArrayUsingPredicate:predicate];
NSLog(@"filteredArray: %@", filteredArray);
// Output:
// filteredArray: ({birthday = 1972-03-24 10:45:32 +0600; \\
firstName = Ben; lastName = Ballard;})
Testing for Null
If you want to match null values, you must include a specific test in addition to other comparisons, as illustrated in the following fragment.
- 如果要匹配空值,除了其他比较之外,还必须包含特定测试,如以下片段所示。
predicate = [NSPredicate predicateWithFormat:@"(firstName == %@) || (firstName = nil)", firstName];
filteredArray = [array filteredArrayUsingPredicate:predicate];
NSLog(@"filteredArray: %@", filteredArray);
// Output:
// filteredArray: ( { lastName = Turner; }, { birthday = 1972-03-23 20:45:32 -0800; firstName = Ben; lastName = Ballard; }
By implication, a test for null that matches a null value returns true. In the following code fragment, ok is set to YES for both predicate evaluations.
- 通过暗示,与null值匹配的null测试返回true。 在以下代码片段中,对于两个谓词评估,ok都设置为YES。
predicate = [NSPredicate predicateWithFormat:@"firstName = nil"];
BOOL ok = [predicate evaluateWithObject:[NSDictionary dictionary]];
ok = [predicate evaluateWithObject:
[NSDictionary dictionaryWithObject:[NSNull null] forKey:@"firstName"]];
Using Predicates with Core Data
If you are using the Core Data framework, you can use predicates in the same way as you would if you were not using Core Data (for example, to filter arrays or with an array controller). In addition, however, you can also use predicates as constraints on a fetch request and you can store fetch request templates in the managed object model (see Managed Object Models).
- 如果您使用的是Core Data框架,则可以使用与未使用Core Data时相同的谓词(例如,过滤数组或使用数组控制器)。 但是,您还可以将谓词用作获取请求的约束,并且可以在管理对象模型中存储获取请求模板(请参阅受管对象模型)。
Limitations: You cannot necessarily translate “arbitrary” SQL queries into predicates or fetch requests. There is no way, for example, to convert a SQL statement such as
- 限制:您不一定能将“任意”SQL查询转换为谓词或获取请求。 例如,没有办法转换SQL语句,例如
SELECT t1.name, V1, V2
FROM table1 t1 JOIN (SELECT t2.name AS V1, count(*) AS V2
FROM table2 t2 GROUP BY t2.name as V) on t1.name = V.V1
into a fetch request. You must fetch the objects of interest, then either perform a calculation directly using the results, or use an array operator.
- 进入获取请求。 您必须获取感兴趣的对象,然后使用结果直接执行计算,或使用数组运算符。
Fetch Requests
You create a predicate to match properties of the target entity (note that you can follow relationships using key paths) and associate the predicate with a fetch request. When the request is executed, an array is returned that contains the objects (if any) that match the criteria specified by the predicate. The following example illustrates the use of a predicate to find employees that earn more than a specified amount.
- 您可以创建谓词以匹配目标实体的属性(请注意,您可以使用关键路径跟踪关系)并将谓词与提取请求相关联。 执行请求时,将返回一个数组,其中包含与谓词指定的条件匹配的对象(如果有)。 以下示例说明如何使用谓词查找收入超过指定金额的员工。
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Employee"
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSNumber *salaryLimit = <#A number representing the limit#>;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"salary > %@", salaryLimit];
[request setPredicate:predicate];
NSError *error;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
Object Controllers
If you are using Cocoa bindings, you can specify a fetch predicate for an object controller (such as an instance of NSObjectController
or NSArrayController
). You can type a predicate directly into the predicate editor text field in the Attributes Inspector in Xcode or you can set it programmatically using setFetchPredicate:
. The predicate is used to constrain the results returned when the controller executes a fetch. If you are using an NSObjectController
object, you specify a fetch that uniquely identifies the object you want to be the controller's content—for example, if the controller’s entity is Department, the predicate might be name like "Engineering"
.
- 如果使用Cocoa绑定,则可以为对象控制器(例如NSObjectController或NSArrayController的实例)指定fetch谓词。 您可以直接在Xcode的Attributes Inspector中的谓词编辑器文本字段中键入谓词,也可以使用setFetchPredicate:以编程方式设置谓词。 谓词用于约束控制器执行提取时返回的结果。 如果您正在使用NSObjectController对象,则指定一个唯一标识您想要成为控制器内容的对象的提取 - 例如,如果控制器的实体是Department,则谓词可能是“Engineering”之类的名称。
Using Regular Expressions
The MATCHES
operator uses ICU's Regular Expressions package, as illustrated in the following example:
- MATCHES运算符使用ICU的正则表达式包,如以下示例所示:
NSArray *array = @[@"TATACCATGGGCCATCATCATCATCATCATCATCATCATCATCACAG",
@"CGGGATCCCTATCAAGGCACCTCTTCG", @"CATGCCATGGATACCAACGAGTCCGAAC",
@"CAT", @"CATCATCATGTCT", @"DOG"];
// find strings that contain a repetition of at least 3 'CAT' sequences,
// but not followed by a further 'CA'
NSPredicate *catPredicate =
[NSPredicate predicateWithFormat:@"SELF MATCHES '.*(CAT){3,}(?!CA).*'"];
NSArray *filteredArray = [array filteredArrayUsingPredicate:catPredicate];
// filteredArray contains just 'CATCATCATGTCT'
According to the ICU specification, regular expression metacharacters are not valid inside a pattern set. For example, the regular expression \d{9}[\dxX] does not match valid ISBN numbers (any ten digit number, or a string with nine digits and the letter 'X') since the pattern set ([\dxX]) contains a metacharacter (\d). Instead you could write an OR expression, as shown in the following code sample:
- 根据ICU规范,正则表达式元字符在模式集内无效。 例如,正则表达式\ d {9} [\ dxX]与有效的ISBN编号(任何十位数字,或九位数字符串和字母“X”)不匹配,因为模式集([\ dxX]) 包含元字符(\ d)。 相反,您可以编写OR表达式,如以下代码示例所示:
NSArray *isbnTestArray = @[@"123456789X", @"987654321x", @"1234567890", @"12345X", @"1234567890X"];
NSPredicate *isbnPredicate =
[NSPredicate predicateWithFormat:@"SELF MATCHES '\\\\d{10}|\\\\d{9}[Xx]'"];
NSArray *isbnArray = [isbnTestArray filteredArrayUsingPredicate:isbnPredicate];
// isbnArray contains (123456789X, 987654321x, 1234567890)
Performance Considerations
You should structure compound predicates to minimize the amount of work done. Regular expression matching in particular is an expensive operation. In a compound predicate, you should therefore perform simple tests before a regular expression; thus instead of using a predicate shown in the following example:
- 您应该构造复合谓词以最小化完成的工作量。 正则表达式匹配尤其是昂贵的操作。 因此,在复合谓词中,您应该在正则表达式之前执行简单测试; 因此,而不是使用以下示例中显示的谓词:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"( title matches .*mar[1-10] ) OR ( type = 1 )"];
you should write
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"( type = 1 ) OR ( title matches .*mar[1-10] )"];
In the second example, the regular expression is evaluated only if the first clause is false.
- 在第二个示例中,仅当第一个子句为false时才计算正则表达式。
Using Joins
In general, joins (queries that cross relationships) are also expensive operations, and you should avoid them if you can. When testing to-one relationships, if you already have—or can easily retrieve—the relationship source object (or its object ID), it is more efficient to test for object equality than to test for a property of the source object. Instead of writing the following:
- 通常,连接(跨越关系的查询)也是昂贵的操作,如果可以,您应该避免使用它们。 在测试一对一关系时,如果您已经拥有 - 或者可以轻松检索 - 关系源对象(或其对象ID),则测试对象相等性比测试源对象的属性更有效。 而不是写下面的内容:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"department.name like %@", [department name]];
it is more efficient to write:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"department == %@", department];
If a predicate contains more than one expression, it is also typically more efficient to structure it to avoid joins. For example, @"firstName beginswith[cd] 'Matt' AND (ANY directreports.paygrade <= 7)"
is likely to be more efficient than @"(ANY directreports.paygrade <= 7) AND (firstName beginswith[cd] 'Matt')"
because the former avoids making a join unless the first test succeeds.
- 如果谓词包含多个表达式,则通常更有效地构造它以避免连接。 例如,@“firstName始于[cd]'Matt'AND(ANY directreports.paygrade <= 7)”可能比@“更高效(任何directreports.paygrade <= 7)AND(firstName以[cd]'开头' Matt')“因为前者避免了连接,除非第一次测试成功。
Structuring Your Data
In some situations, there may be tension between the representation of your data and the use of predicates. If you intend to use predicates in your application, the pattern of typical query operations may influence how you structure your data. In Core Data, although you specify entities and entity-class mapping, the levels that create the underlying structures in the persistent store are opaque. Nevertheless, you still have control over your entities and the properties they have.
- 在某些情况下,数据表示与谓词的使用之间可能存在紧张关系。 如果您打算在应用程序中使用谓词,则典型查询操作的模式可能会影响数据的结构。 在Core Data中,虽然您指定了实体和实体类映射,但在持久性存储中创建底层结构的级别是不透明的。 不过,您仍然可以控制您的实体及其拥有的属性。
In addition to tending to be expensive, joins may also restrict flexibility. It may be appropriate, therefore, to de-normalize your data. In general—assuming that queries are issued often—it may be a good trade-off to have larger objects, but for it to be easier to find the right ones (and so have fewer in memory).
- 除了趋于昂贵之外,连接还可能限制灵活性。 因此,对数据进行反规范化可能是合适的。 一般来说 - 假设经常发出查询 - 拥有更大的对象可能是一个很好的权衡,但是更容易找到正确的对象(因此内存更少)。
Using Predicates with Cocoa Bindings
In OS X, you can set a predicate for an array controller to filter the content array. You can set the predicate in code (using setFilterPredicate:
). You can also bind the array controller’s filterPredicate
binding to a method that returns an NSPredicate
object. The object that implements the method may be the File's Owner or another controller object. If you change the predicate, remember that you must do so in a key-value observing compliant way (see Key-Value Observing Programming Guide) so that the array controller updates itself accordingly.
- 在OS X中,您可以为阵列控制器设置谓词以过滤内容数组。 您可以在代码中设置谓词(使用setFilterPredicate :)。 您还可以将数组控制器的filterPredicate绑定绑定到返回NSPredicate对象的方法。 实现该方法的对象可以是文件的所有者或另一个控制器对象。 如果更改谓词,请记住必须以符合键值观察的方式执行此操作(请参阅键值观察编程指南),以便阵列控制器相应地更新自身。
You can also bind the predicate
binding of an NSSearchField
object to the filterPredicate
of an array controller. A search field’s predicate
binding is a multi-value binding, described in Binding Types.
- 您还可以将NSSearchField对象的谓词绑定绑定到数组控制器的filterPredicate。 搜索字段的谓词绑定是一种多值绑定,在绑定类型中描述。