Filtering Fun with Predicate

原文:http://lovebirdegg.co.tv/2010/03/filtering-fun-with-predicates-filtering-fun-with-predicates/

Being present longer than iPhone OS exists on the Mac platform NSPredicate was only introduced to us iPhone developers in Version 3.0 of the SDK. They have multiple interesting uses, some of which I am going to explore in this article.

You will see how you can filter an array of dictionaries, learn that the same also works for your own custom classes. Then we’ll see how to replace convoluted IF trees with simple predicates. We’ll explore how to use predicates to filter entries of a table view and finally peek into the inner workings of predicates.

Being simple and powerful at the same time it took me 3 hours to write this article. I hope you don’t give up halfway through it, because I promise it will be a great addition to your skillset as iPhone developer.
One interesting use of predicates is to filter an array for entries where a specific key matches some criteria. In the following example I am adding four people to an array in the form of individual dictionaries. Then I’m filtering for all the entries that contain the letter o in lastName.

NSMutableArray
 *people = [NSMutableArray
 array];
[people addObject:[NSDictionary
 dictionaryWithObjectsAndKeys:
				   @"Oliver", @"firstName",
				   @"Drobnik", @"lastName", nil]];

[people addObject:[NSDictionary
 dictionaryWithObjectsAndKeys:
				   @"Steve", @"firstName",
				   @"Jobs", @"lastName", nil]];

[people addObject:[NSDictionary
 dictionaryWithObjectsAndKeys:
				   @"Bill", @"firstName",
				   @"Gates", @"lastName", nil]];

[people addObject:[NSDictionary
 dictionaryWithObjectsAndKeys:
				   @"Obiwan", @"firstName",
				   @"Kenobi", @"lastName", nil]];

NSPredicate
 *predicate = [NSPredicate
 predicateWithFormat:@"lastName CONTAINS[cd] %@",
				@"o"];

NSArray
 *filteredArray = [people filteredArrayUsingPredicate:predicate];

NSLog(@"%@", filteredArray);

 

Note that the [cd] next to the operator like causes it to ignore case and diacritics. Case is obvious, o = O. Diacritics are “ancillary glyphs added to a letter”, e.g. ó which is adding an accent to a plain o. With the [d] option o == ò == ö.

In the sample I am creating a new filtered array, but NSMutableArray also has a method to do it in-place. filterUsingPredicate leaves only matching items in the array.

A variety of operators is possible when dealing with string properties:

  • BEGINSWITH
  • CONTAINS
  • ENDSWITH
  • LIKE – wildcard characters ? for single characters and * for multiple characters
  • MATCHES – ICU v3 style regular expression

Predicates can be very useful to avoid monstrous IF trees. You can chain multiple predicates with the logical operators AND, OR and NOT. To evaluate an expression on a specific object use the predicate’s evaluateWithObject method.

NSDictionary
 *person = 	[NSDictionary
 dictionaryWithObjectsAndKeys:
				   @"Steve", @"firstName",
				   @"Jobs", @"lastName", nil];

NSPredicate
 *predicate = [NSPredicate
 predicateWithFormat:
				   @"firstName ENDSWITH %@ AND lastName BEGINSWITH

      
      

%@", @"eve", @"j"];

if ([predicate evaluateWithObject:person]) { NSLog(@"Is YES, matches"); }

Now in the above samples we’ve only been using NSDictionary to old our firstName and lastName properties. A quick experiment shows us if this is also working for our own custom classes. Let’s create a Person class for this purpose. This only has our two properties plus an overriding description to output useful information and a class method to quickly create a Person instance.

Person.h

@interface Person : NSObject
 {
	NSString
 *firstName;
	NSString
 *lastName;
}

@property (nonatomic, retain) NSString
 *firstName;
@property (nonatomic, retain) NSString
 *lastName;

+ (Person *)personWithFirstName:(NSString
 *)firstName lastName:(NSString
 *)lastName;

@end

Person.m

#import "Person.h"

@implementation Person
@synthesize firstName, lastName;

+ (Person *)personWithFirstName:(NSString
 *)firstName lastName:(NSString
 *)lastName
{
	Person *person = [[[Person alloc] init] autorelease];
	person.firstName = firstName;
	person.lastName = lastName;

	return person;
}

- (NSString
 *)description
{
	return [NSString
 stringWithFormat:@"<%@ '%@ %@'>",
			NSStringFromClass([self class]),
			firstName,
			lastName];
}

- (void) dealloc
{
	[firstName release];
	[lastName release];
	[super dealloc];
}

@end

Now let’s see if we still get the same result if we do the same filtering of an array, this time with our own Person instances in it.

Person *person1 = [Person personWithFirstName:@"Oliver" lastName:@"Drobnik"];
Person *person2 = [Person personWithFirstName:@"Steve" lastName:@"Jobs"];
Person *person3 = [Person personWithFirstName:@"Bill" lastName:@"Gates"];
Person *person4 = [Person personWithFirstName:@"Obiwan" lastName:@"Kenobi"];

NSArray
 *people = [NSArray
 arrayWithObjects:person1, person2, person3, person4, nil];

NSPredicate
 *predicate = [NSPredicate
 predicateWithFormat:@"firstName CONTAINS[cd] %@",
			@"i"];

NSArray
 *filteredArray = [people filteredArrayUsingPredicate:predicate];

NSLog(@"%@", filteredArray);

Yup! Still works! Now is that cool or what? One obvious use for predicates is to filter the data array in a table view controller to only match the contents of your search box.

To try this out we need to do the following:

  1. create a new navigation-based iPhone application, WITHOUT CoreData
  2. copy the Person header and implementation files to the new project
  3. replace the RootViewController header and implementation as shown below.

RootViewController.h

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController <UISearchDisplayDelegate, UISearchBarDelegate>
{
	NSArray
 *people;
	NSArray
 *filteredPeople;

	UISearchDisplayController *searchDisplayController;
}

@property (nonatomic, retain) NSArray
 *people;
@property (nonatomic, retain) NSArray
 *filteredPeople;

@property (nonatomic, retain) UISearchDisplayController *searchDisplayController;

@end

RootViewController.m

#import "RootViewController.h"
#import "Person.h"

@implementation RootViewController
@synthesize people, filteredPeople;
@synthesize searchDisplayController;

- (void)dealloc
{
	[searchDisplayController release];
	[people release];
	[filteredPeople release];
    [super dealloc];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

	self.title = @"Search People";

	Person *person1 = [Person personWithFirstName:@"Oliver" lastName:@"Drobnik"];
	Person *person2 = [Person personWithFirstName:@"Steve" lastName:@"Jobs"];
	Person *person3 = [Person personWithFirstName:@"Bill" lastName:@"Gates"];
	Person *person4 = [Person personWithFirstName:@"Obiwan" lastName:@"Kenobi"];

	people = [[NSArray
 alloc] initWithObjects:person1, person2, person3, person4, nil];

	// programmatically set up search bar
	UISearchBar *mySearchBar = [[UISearchBar alloc] init];
	[mySearchBar setScopeButtonTitles:[NSArray
 arrayWithObjects:@"First",@"Last",nil]];
	mySearchBar.delegate = self;
	[mySearchBar setAutocapitalizationType:UITextAutocapitalizationTypeNone];
	[mySearchBar sizeToFit];
	self.tableView.tableHeaderView = mySearchBar;

	// programmatically set up search display controller
	searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];
	[self setSearchDisplayController:searchDisplayController];
	[searchDisplayController setDelegate:self];
	[searchDisplayController setSearchResultsDataSource:self];

	[mySearchBar release];
}

#pragma mark Table view methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
	if (tableView == self.searchDisplayController.searchResultsTableView)
	{
        return [self.filteredPeople count];
    }
	else
	{
        return [self.people count];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
 *)indexPath
{

    static NSString
 *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

	Person *person;

	if (tableView == self.searchDisplayController.searchResultsTableView)
	{
        person = [self.filteredPeople objectAtIndex:indexPath.row];
    }
	else
	{
        person = [self.people objectAtIndex:indexPath.row];
    }

	cell.textLabel.text = [NSString
 stringWithFormat:@"%@ %@",
						   person.firstName, person.lastName];
    return cell;
}

#pragma mark Content Filtering

- (void)filterContentForSearchText:(NSString
 *)searchText scope:(NSString
 *)scope
{
	NSPredicate
 *predicate;

	if ([scope isEqualToString:@"First"])
	{
		predicate = [NSPredicate
 predicateWithFormat:
					 @"firstName CONTAINS[cd] %@", searchText];
	}
	else
	{
		predicate = [NSPredicate
 predicateWithFormat:
					 @"lastName CONTAINS[cd] %@", searchText];
	}

	self.filteredPeople = [people filteredArrayUsingPredicate:predicate];
}

#pragma mark UISearchDisplayController Delegate Methods

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString
 *)searchString
{
    [self filterContentForSearchText:searchString scope:
	 [[self.searchDisplayController.searchBar scopeButtonTitles]
	  objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
	 [[self.searchDisplayController.searchBar scopeButtonTitles]
	  objectAtIndex:searchOption]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

@end

Adding the search display controller is responsible for most of the additional code in this example. Just getting the filtered people to match our search has become very simple due to NSPredicate as you can see in the filterContentForSearchText method. The two UISearchDisplayController Delegate methods are called whenever you type something in the search box or switch between the scope buttons. In this case I am showing how to switch between searching in first names and last names.

The table view for the search results is actually dynamically created when needed. As it’s using the same data source and delegate methods as the original table view we need to respond differently based on which table view the methods are being called for. This is the reason for the IF in each of these methods. If we are in the search results we take the filteredPeople array, otherwise we use the original people array.

NSPredicate was only introduced into the iPhone SDKs as of version 3.0, so my guess is that there might a few instances in your code where you could simplify the logic with replacing a big IF tree with a simple predicate. Down the road, they are the only method how you can filter data coming from a fetch in CoreData.

In this article I’ve only used the predicteWithFormat method to create them. That’s actually a tremendous shortcut, because internally predicates are themselves consisting of several parts, mostly NSExpression instances. So if you feel that your code has become way too easy to understand by using predicates you can also replace them with the original composition.

Using expressions the general approach is to define a left hand expression and a right hand expression and put these into an NSComparisonPredicate. I’m just showing this here so that you can appreciate the simplicity of the shortcut method presented earlier.

NSExpression
 *lhs = [NSExpression
 expressionForKeyPath:@"firstName"];
NSExpression
 *rhs = [NSExpression
 expressionForConstantValue:@"i"];

NSPredicate
 *predicate = [NSComparisonPredicate

				   predicateWithLeftExpression:lhs
				   rightExpression:rhs
				   modifier:NSDirectPredicateModifier
				   type:NSContainsPredicateOperatorType
				   options:NSCaseInsensitivePredicateOption | NSDiacriticInsensitivePredicateOption];

// same as:
//NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstName CONTAINS[cd] %@", @"i"];

Component predicates are achieved the long way in a similar fashion by using NSCompoundPredicate, but there is no using going into these dark depths when the shortcut is so much more convenient.

Finally another hint without going into details: Predicate Templates. You can define any predicate with $Variables instead of an expression. Then when you need them you can use [template predicateWithSubstitutionVariables:] with a dictionary of values to substitute for the $Variables to prep a predicate ready for use.

原文:http://www.drobnik.com/touch/2010/03/filtering-fun-with-predicates/


Being present longer than iPhone OS exists on the Mac platform 
NSPredicate was only introduced to us iPhone developers in Version 3.0 
of the SDK. They have multiple interesting uses, some of which I am 
going to explore in this article.

You will see how you can filter an array of dictionaries, learn that
the same also works for your own custom classes. Then we’ll see how to
replace convoluted IF trees with simple predicates. We’ll explore how
to  use predicates to filter entries of a table view and finally peek
into  the inner workings of predicates.

Being simple and powerful at the same time it took me 3 hours to 
write this article. I hope you don’t give up halfway through it, because
  I promise it will be a great addition to your skillset as iPhone 
developer.

One interesting use of predicates is to filter an array for entries 
where a specific key matches some criteria. In the following example I 
am adding four people to an array in the form of individual 
dictionaries. Then I’m filtering for all the entries that contain the 
letter o in lastName.



NSMutableArray
*people = [NSMutableArray
array];

[people addObject:[NSDictionary
dictionaryWithObjectsAndKeys:

   @&quot;Oliver&quot;, @&quot;firstName&quot;,

   @&quot;Drobnik&quot;, @&quot;lastName&quot;, nil]];



[people addObject:[NSDictionary
dictionaryWithObjectsAndKeys:

   @&quot;Steve&quot;, @&quot;firstName&quot;,

   @&quot;Jobs&quot;, @&quot;lastName&quot;, nil]];



[people addObject:[NSDictionary
dictionaryWithObjectsAndKeys:

   @&quot;Bill&quot;, @&quot;firstName&quot;,

   @&quot;Gates&quot;, @&quot;lastName&quot;, nil]];



[people addObject:[NSDictionary
dictionaryWithObjectsAndKeys:

   @&quot;Obiwan&quot;, @&quot;firstName&quot;,

   @&quot;Kenobi&quot;, @&quot;lastName&quot;, nil]];



NSPredicate
*predicate = [NSPredicate
predicateWithFormat:@&quot;lastName CONTAINS[cd] %@&quot;,

@&quot;o&quot;];



NSArray
*filteredArray = [people filteredArrayUsingPredicate:predicate];



NSLog(@&quot;%@&quot;, filteredArray);



Note that the [cd] next to the operator like causes it to ignore case
  and diacritics. Case is obvious, o = O. Diacritics are “ancillary 
glyphs added to a letter”, e.g. ó which is adding an accent to a plain 
o. With the [d] option o == ò == ö.

In the sample I am creating a new filtered array, but NSMutableArray
also has a method to do it in-place. filterUsingPredicate leaves only 
matching items in the array.

A variety of operators is possible when dealing with string 
properties:

  • BEGINSWITH
  • CONTAINS
  • ENDSWITH

  • LIKE – wildcard characters ? for single characters and * for 
    multiple characters
  • MATCHES – ICU v3 style regular expression


Predicates can be very useful to avoid monstrous IF trees. You can 
chain multiple predicates with the logical operators AND, OR and NOT. To
  evaluate an expression on a specific object use the predicate’s 
evaluateWithObject method.



NSDictionary
*person = [NSDictionary
dictionaryWithObjectsAndKeys:

   @&quot;Steve&quot;, @&quot;firstName&quot;,

   @&quot;Jobs&quot;, @&quot;lastName&quot;, nil];



NSPredicate
*predicate = [NSPredicate
predicateWithFormat:

   @&quot;firstName ENDSWITH %@ AND lastName BEGINSWITH

view plain
print
?


1

 





%@&quot;,



   @&quot;eve&quot;, @&quot;j&quot;];



if ([predicate evaluateWithObject:person])



{



NSLog(@&quot;Is YES, matches&quot;);



}




Now in the above samples we’ve only been using NSDictionary to old 
our firstName and lastName properties. A quick experiment shows us if 
this is also working for our own custom classes. Let’s create a Person 
class for this purpose. This only has our two properties plus an 
overriding description to output useful information and a class method 
to quickly create a Person instance.

Person.h




@interface Person : NSObject
{

NSString
*firstName;

NSString
*lastName;

}



@property (nonatomic, retain) NSString
*firstName;

@property (nonatomic, retain) NSString
*lastName;



+ (Person *)personWithFirstName:(NSString
*)firstName lastName:(NSString
*)lastName;



@end


Person.m




#import &quot;Person.h&quot;



@implementation Person

@synthesize firstName, lastName;



+ (Person *)personWithFirstName:(NSString
*)firstName lastName:(NSString
*)lastName

{

Person *person = [[[Person alloc] init] autorelease];

person.firstName = firstName;

person.lastName = lastName;



return person;

}



- (NSString
*)description

{

return [NSString
stringWithFormat:@&quot;&quot;,

NSStringFromClass([self class]),

firstName,

lastName];

}



- (void) dealloc

{

[firstName release];

[lastName release];

[super dealloc];

}



@end


Now let’s see if we still get the same result if we do the same 
filtering of an array, this time with our own Person instances in it.



Person *person1 = [Person personWithFirstName:@&quot;Oliver&quot; lastName:@&quot;Drobnik&quot;];

Person *person2 = [Person personWithFirstName:@&quot;Steve&quot; lastName:@&quot;Jobs&quot;];

Person *person3 = [Person personWithFirstName:@&quot;Bill&quot; lastName:@&quot;Gates&quot;];

Person *person4 = [Person personWithFirstName:@&quot;Obiwan&quot; lastName:@&quot;Kenobi&quot;];



NSArray
*people = [NSArray
arrayWithObjects:person1, person2, person3, person4, nil];



NSPredicate
*predicate = [NSPredicate
predicateWithFormat:@&quot;firstName CONTAINS[cd] %@&quot;,

@&quot;i&quot;];



NSArray
*filteredArray = [people filteredArrayUsingPredicate:predicate];



NSLog(@&quot;%@&quot;, filteredArray);


Yup! Still works! Now is that cool or what? One obvious use for 
predicates is to filter the data array in a table view controller to 
only match the contents of your search box.




To try this out we need to do the following:

  1. create a new navigation-based iPhone application, WITHOUT CoreData
  2. copy the Person header and implementation files to the new project

  3. replace the RootViewController header and implementation as shown 
    below.


RootViewController.h




#import



@interface RootViewController : UITableViewController

{

NSArray
*people;

NSArray
*filteredPeople;



UISearchDisplayController *searchDisplayController;

}



@property (nonatomic, retain) NSArray
*people;

@property (nonatomic, retain) NSArray
*filteredPeople;



@property (nonatomic, retain) UISearchDisplayController *searchDisplayController;



@end


RootViewController.m




#import &quot;RootViewController.h&quot;

#import &quot;Person.h&quot;



@implementation RootViewController

@synthesize people, filteredPeople;

@synthesize searchDisplayController;



- (void)dealloc

{

[searchDisplayController release];

[people release];

[filteredPeople release];

    [super dealloc];

}



- (void)viewDidLoad

{

    [super viewDidLoad];



self.title = @&quot;Search People&quot;;



Person *person1 = [Person personWithFirstName:@&quot;Oliver&quot; lastName:@&quot;Drobnik&quot;];

Person *person2 = [Person personWithFirstName:@&quot;Steve&quot; lastName:@&quot;Jobs&quot;];

Person *person3 = [Person personWithFirstName:@&quot;Bill&quot; lastName:@&quot;Gates&quot;];

Person *person4 = [Person personWithFirstName:@&quot;Obiwan&quot; lastName:@&quot;Kenobi&quot;];



people = [[NSArray
alloc] initWithObjects:person1, person2, person3, person4, nil];



// programmatically set up search bar

UISearchBar *mySearchBar = [[UISearchBar alloc] init];

[mySearchBar setScopeButtonTitles:[NSArray
arrayWithObjects:@&quot;First&quot;,@&quot;Last&quot;,nil]];

mySearchBar.delegate = self;

[mySearchBar setAutocapitalizationType:UITextAutocapitalizationTypeNone];

[mySearchBar sizeToFit];

self.tableView.tableHeaderView = mySearchBar;



// programmatically set up search display controller

searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];

[self setSearchDisplayController:searchDisplayController];

[searchDisplayController setDelegate:self];

[searchDisplayController setSearchResultsDataSource:self];



[mySearchBar release];

}



#pragma mark Table view methods

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

if (tableView == self.searchDisplayController.searchResultsTableView)

{

        return [self.filteredPeople count];

    }

else

{

        return [self.people count];

    }

}



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath

{



    static NSString
*CellIdentifier = @&quot;Cell&quot;;



    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }



Person *person;



if (tableView == self.searchDisplayController.searchResultsTableView)

{

        person = [self.filteredPeople objectAtIndex:indexPath.row];

    }

else

{

        person = [self.people objectAtIndex:indexPath.row];

    }



cell.textLabel.text = [NSString
stringWithFormat:@&quot;%@ %@&quot;,

   person.firstName, person.lastName];

    return cell;

}



#pragma mark Content Filtering



- (void)filterContentForSearchText:(NSString
*)searchText scope:(NSString
*)scope

{

NSPredicate
*predicate;



if ([scope isEqualToString:@&quot;First&quot;])

{

predicate = [NSPredicate
predicateWithFormat:

@&quot;firstName CONTAINS[cd] %@&quot;, searchText];

}

else

{

predicate = [NSPredicate
predicateWithFormat:

@&quot;lastName CONTAINS[cd] %@&quot;, searchText];

}



self.filteredPeople = [people filteredArrayUsingPredicate:predicate];

}



#pragma mark UISearchDisplayController Delegate Methods



- (BOOL)searchDisplayController:(UISearchDisplayController *)controller

shouldReloadTableForSearchString:(NSString
*)searchString

{

    [self filterContentForSearchText:searchString scope:

[[self.searchDisplayController.searchBar scopeButtonTitles]

  objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];



    // Return YES to cause the search result table view to be reloaded.

    return YES;

}



- (BOOL)searchDisplayController:(UISearchDisplayController *)controller

shouldReloadTableForSearchScope:(NSInteger)searchOption

{

    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:

[[self.searchDisplayController.searchBar scopeButtonTitles]

  objectAtIndex:searchOption]];



    // Return YES to cause the search result table view to be reloaded.

    return YES;

}



@end


Adding the search display controller is responsible for most of the 
additional code in this example. Just getting the filtered people to 
match our search has become very simple due to NSPredicate as you can 
see in the filterContentForSearchText method. The two 
UISearchDisplayController Delegate methods are called whenever you type
something in the search box or switch between the scope buttons. In
this  case I am showing how to switch between searching in first names
and  last names.

The table view for the search results is actually dynamically created
  when needed. As it’s using the same data source and delegate methods
as  the original table view we need to respond differently based on
which  table view the methods are being called for. This is the reason
for the  IF in each of these methods. If we are in the search results we
take the  filteredPeople array, otherwise we use the original people
array.

NSPredicate was only introduced into the iPhone SDKs as of version 
3.0, so my guess is that there might a few instances in your code where
you could simplify the logic with replacing a big IF tree with a simple
  predicate. Down the road, they are the only method how you can filter
data coming from a fetch in CoreData.

In this article I’ve only used the predicteWithFormat method to 
create them. That’s actually a tremendous shortcut, because internally 
predicates are themselves consisting of several parts, mostly 
NSExpression instances. So if you feel that your code has become way too
  easy to understand by using predicates you can also replace them with
the original composition.

Using expressions the general approach is to define a left hand 
expression and a right hand expression and put these into an 
NSComparisonPredicate. I’m just showing this here so that you can 
appreciate the simplicity of the shortcut method presented earlier.



NSExpression
*lhs = [NSExpression
expressionForKeyPath:@&quot;firstName&quot;];

NSExpression
*rhs = [NSExpression
expressionForConstantValue:@&quot;i&quot;];



NSPredicate
*predicate = [NSComparisonPredicate


   predicateWithLeftExpression:lhs

   rightExpression:rhs

   modifier:NSDirectPredicateModifier

   type:NSContainsPredicateOperatorType

   options:NSCaseInsensitivePredicateOption | NSDiacriticInsensitivePredicateOption];



// same as:

//NSPredicate *predicate = [NSPredicate predicateWithFormat:@&quot;firstName CONTAINS[cd] %@&quot;, @&quot;i&quot;];


Component predicates are achieved the long way in a similar fashion 
by using NSCompoundPredicate, but there is no using going into these 
dark depths when the shortcut is so much more convenient.

Finally another hint without going into details: Predicate Templates.
  You can define any predicate with $Variables instead of an expression.
  Then when you need them you can use [template 
predicateWithSubstitutionVariables:] with a dictionary of values to 
substitute for the $Variables to prep a predicate ready for use.

 

你可能感兴趣的:(OS,J#,UP)