【问题标题】:How to use a predicate on NSTimeInterval?如何在 NSTimeInterval 上使用谓词?
【发布时间】:2013-11-04 23:39:08
【问题描述】:

我想获取当月的所有记录,所以我为该月的第一个日期和该月的最后一天堆叠了两个谓词。由于我使用 CoreData,因此日期实际上存储为 NSTimeInterval。

NSCalendar *calendar = [NSCalendar currentCalendar];

//Get beginning of current month
NSDateComponents *beginningOfCurrentMonthComponents = [calendar components:(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
[beginningOfCurrentMonthComponents setDay:1];
NSDate *beginningOfCurrentMonthDate = [calendar dateFromComponents:beginningOfCurrentMonthComponents];

//Set a single month to be added to the current month
NSDateComponents *oneMonth = [[NSDateComponents alloc] init];
[oneMonth setMonth:1];

//determine the last day of this month
NSDate *beginningOfNextMonthDate = [calendar dateByAddingComponents:oneMonth toDate:beginningOfCurrentMonthDate options:0];

NSMutableArray *parr = [NSMutableArray array];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate >= %d", [beginningOfCurrentMonthDate timeIntervalSince1970]]];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate < %d", [beginningOfNextMonthDate timeIntervalSince1970]]];

//Give me everything from beginning of this month until end of this month
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];

return [allRecords filteredArrayUsingPredicate:predicate];

返回过滤后的数组时,它会崩溃并显示以下错误消息:

2013-10-25 19:09:39.702 [3556:a0b] -[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440
2013-10-25 19:09:39.704 [3556:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440'
*** First throw call stack:
(
    0   CoreFoundation                      0x01aa85e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0182b8b6 objc_exception_throw + 44
    2   CoreFoundation                      0x01b45903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
    3   CoreFoundation                      0x01a9890b ___forwarding___ + 1019
    4   CoreFoundation                      0x01a984ee _CF_forwarding_prep_0 + 14
    5   CoreFoundation                      0x01a7e3e3 -[NSDate compare:] + 67
    6   Foundation                          0x014194fe -[NSComparisonPredicateOperator performPrimitiveOperationUsingObject:andObject:] + 408
    7   Foundation                          0x014b03de -[NSPredicateOperator performOperationUsingObject:andObject:] + 306
    8   Foundation                          0x014b016c -[NSComparisonPredicate evaluateWithObject:substitutionVariables:] + 347
    9   Foundation                          0x014299b6 -[NSCompoundPredicateOperator evaluatePredicates:withObject:substitutionVariables:] + 240
    10  Foundation                          0x01429845 -[NSCompoundPredicate evaluateWithObject:substitutionVariables:] + 294
    11  Foundation                          0x014b0009 -[NSPredicate evaluateWithObject:] + 48
    12  Foundation                          0x014aff89 _filterObjectsUsingPredicate + 418
    13  Foundation                          0x014afd42 -[NSArray(NSPredicateSupport) filteredArrayUsingPredicate:] + 328

我也使用这个循环来表明 recordDate 确实存在于数组中:

for (FTRecord *r in allRecords) {
    NSLog(@"%f", [r recordDate]);
}

2013-10-25 19:09:35.860 [3556:a0b] 1380582000.000000
2013-10-25 19:09:36.556 [3556:a0b] 1380754800.000000

【问题讨论】:

  • 堆栈跟踪显示谓词中涉及NSDate 对象。尝试更改您的 NSPredicate 以将 recordDateNSDate 进行比较,而不是时间间隔。
  • 我只是仔细检查了所有内容。但是您会看到两个日期都转换为 NSTimeInterval 。我什至按照您的建议做了,删除了timeIntervalSince1970,但它仍然崩溃并显示相同的消息。顺便说一句,我现在已经添加了日期的创建方式,也许它有助于追查问题。

标签: ios nsdate nspredicate unrecognized-selector


【解决方案1】:

您选择了“使用原始数据类型的标量属性”选项 创建托管对象子类,以便将 recordDate 表示为 NSTimeIntervalFTRecord 类中。

但是在像"recordDate &gt;= 123.45" 这样的谓词中,左侧是存储的 作为NSKeyPathExpression,并使用valueForKeyPath:@"recordDate" 访问该属性,该属性返回一个NSDate 对象。 该谓词的右侧存储为NSConstantValueExpression 引用 NSNumber 对象。 因此,NSDateNSNumber 进行比较,这恰好导致 你得到的例外。

要解决此问题,您必须将该属性与NSDate 进行比较 (这是@rmaddy 最初建议的):

[parr addObject:[NSPredicate predicateWithFormat:@"recordDate >= %@", beginningOfCurrentMonthDate]];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate < %@", beginningOfNextMonthDate]];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];

我对此进行了测试,它似乎产生了预期的结果。

但请注意,Core Data 使用 timeIntervalSinceReferenceDatedateWithTimeIntervalSinceReferenceDate 在标量之间转换 值和NSDate,而不是timeIntervalSince1970

【讨论】:

  • 谢谢。我只是仔细检查了一下,确实现在谓词不会崩溃。但是,过滤后的数组也没有显示任何条目。现在有点乱我想知道是否应该在不使用原始数据类型的标量属性的情况下重新生成模型。您对此有何建议?
  • @Kave:看我最后的评论。我假设您使用timeIntervalSince1970 创建了对象,但您必须使用timeIntervalSinceReferenceDate(比较stackoverflow.com/a/15885204/1187415)。如果您不使用标量属性,那么您将摆脱所有这些转换问题。但除此之外,对于标量属性是否更好,我没有真正的建议。
  • Martin 你是对的。删除应用程序并将其更改为timeIntervalSinceReferenceDate 解决了这个问题。感谢您的精彩分析。最终,我将不得不将时间戳(与记录日期无关)与来自 REST 服务的传入数据进行比较。那里的时间戳将保存为 unix 时间戳 (1970)。这就是我首先选择timeIntervalSince1970 来规避不兼容性的原因。但似乎我终究做不到。我想这将是一个新问题。
  • @Kave:我没有 REST 经验,所以是的,一个新问题会更好。
【解决方案2】:

将在谓词本身中添加 timeIntervalSince1970 。会的

NSPredicate* 谓词 = PREDICATE(@"(lastUpdatedTime == nil OR lastUpdatedTime.timeIntervalSince1970

【讨论】:

    【解决方案3】:

    对于 Swift 3 中的解决方案:

    let date = NSDate()
    let calender = NSCalendar.current
    var beginningOfCurrentMonthComponents = calender.dateComponents([.year, .month, .day], from: date as Date)
    
    beginningOfCurrentMonthComponents.day = 1
    
    let beginningOfCurrentMonthDate = calender.date(from: beginningOfCurrentMonthComponents)
    let beginningOfNextMonth = calender.date(byAdding: .month, value: 1, to: beginningOfCurrentMonthDate!)
    
    let pred1 = NSPredicate(format: "date >= %@", beginningOfCurrentMonthDate! as NSDate)
    let pred2 = NSPredicate(format: "date < %@", beginningOfNextMonth! as NSDate)
    
    let predicate = NSCompoundPredicate.init(andPredicateWithSubpredicates: [pred1, pred2])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多