NSPredicate:无法解析字符串

NSPredicate: Unable To Parse String

我正在做谓词以根据交易类型获取特定日期范围内的所有交易。

这是谓词:

NSString *predicate = [NSString stringWithFormat:@"(date >= %@ AND date < %@) AND (%@)", from, to, defaultType];

完整代码如下:

- (NSArray *)transactionFromMonth:(NSInteger)fromMonth toMonth:(NSInteger)toMonth ofTypes:(NSArray *)transactionTypes
{
   // get the current calendar of the device
   NSCalendar *calendar = [NSCalendar currentCalendar];

   // create date components using current calendar
   NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay
                                | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];

   // get year
   NSInteger currentYear = [components year];

   // create fromDate (The first day of the month) using the current year, passing month, and 1 as the day
   // to form the starting day of the month
   // e.g when toMonth has a value of 1, then the resulting
   // date will be January 1, (current year of the device's calendar)
   NSDateComponents *comps = [[NSDateComponents alloc] init];
   [comps setDay:1];
   [comps setMonth:fromMonth];
   [comps setYear:currentYear];
   NSDate *from = [[NSCalendar currentCalendar] dateFromComponents:comps];


   // create toDate
   //
   // Set your month here
   [comps setMonth:toMonth];

   // get the number of days of "to" month
   NSRange range = [calendar rangeOfUnit:NSDayCalendarUnit
                               inUnit:NSMonthCalendarUnit
                              forDate:[calendar dateFromComponents:comps]];

   [comps setDay:range.length+1];

   NSDate *to = [[NSCalendar currentCalendar] dateFromComponents:comps];

   NSLog(@"to Months %@", to);
   NSString *defaultType = [NSString stringWithFormat:@"type = %d", [[transactionTypes objectAtIndex:0] integerValue]];

   NSMutableString *types = [[NSMutableString alloc] initWithString: defaultType ];
   for  ( int i = 1; i < transactionTypes.count; i++ )
   {
      [defaultType stringByAppendingFormat:@" OR type = %d", [[transactionTypes objectAtIndex:i] integerValue] ];
   }

   // here is the predicate causing the error
   // 
   //
   NSString *predicate = [NSString stringWithFormat:@"(date >= %@ AND date < %@) AND (%@)", from, to, defaultType];
   return [self.transaction filteredSetUsingPredicate: [NSPredicate predicateWithFormat:predicate]].allObjects;
}

交易模型声明如下: 注意:以下模型的.h和.m是自动生成的。

调用示例如下:

// I want to retrieve all transaction from January 1 to January 31 in device's current calendar year of type kTransactionTypeAddFund (which has the value of 0)
[self transactionFromMonth:1 toMonth:1 ofTypes:@[ @(kTransactionTypeAddFund) ]];

结果:

reason: 'Unable to parse the format string "(date >= 2014-12-31 16:00:00 +0000 AND date < 2015-01-31 16:00:00 +0000) AND (type = 0)"'

我建议你做的是 (1) 将最后一行分成更多的语句,并且 (2) 删除整个 stringWithFormat: 东西,因为 predicateWithFormat: 格式字符串,因此您只是将自己与额外的间接级别混淆了。这个测试编译并运行得很好:

NSPredicate *predicate = 
    [NSPredicate predicateWithFormat:
         @"(date >= %@ AND date < %@) AND (type = %@)", 
         [NSDate new], [NSDate new], @0];

这表明,如果您在可以理解和调试的简单阶段进行简化和完成,您应该能够完成您想要做的事情。做我在这里做的事情——在一行中形成 谓词 ,按预期使用 predicateWithFormat:。一旦你有了要编译的谓词,剩下的就很简单了。

请注意,顺便说一句,我正在传递一个 object(一个 NSNumber),其中 %@ 是预期的。你不能在这里使用标量(比如普通的 0)。如果你想通过 0 你需要像 %d.

这样的东西

最后一个建议:看起来您正在尝试形成一个复杂的谓词,其条件是有条件的,例如有时添加额外的 OR 条件。这样做的方法是学习 NSCompoundPredicate 之类的东西——这就是它的用途。不要试图欺骗并用一个字符串完成所有操作。

您的问题是 stringWithFormat:predicateWithFormat: 的功能不同。具体来说,predicateWithFormat: 将根据需要转义 and/or 引号字符串,而 stringWithFormat 则不会。您需要使用 predicateWithFormat: 创建单个谓词,然后使用连词谓词创建连词:

NSMutableArray* types = [NSMutableArray new];

for  ( int i = 0; i < transactionTypes.count; i++ )
{
    [types addObject:[NSPredicate predicateWithFormat:@"type = %@", [transactionTypes objectAtIndex:i]]];
}

// Make a compound predicate with "or" for all the types
NSPredicate*    typePredicate = [NSCompoundPredicate orPredicateWithSubpredicates:types];

// Create the date part of the predicate
NSPredicate*    datePredicate = [NSPredicate predicateWithFormat:@"(date >= %@ AND date < %@)", from, to];

// merge the type and date parts of the predicate into a single
// and predicate
NSPredicate*    predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[ typePredicate, datePredicate]];

或者,更简单的方法是利用谓词 IN 运算符...

NSPredicate*    predicate = [NSPredicate predicateWithFormat:@"date >= %@ AND date < %@ AND type IN %@", from, to, transactionTypes];