IOS基础使用:OC的Foundation语法(下)

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 七、NSTimer
    • 1、不重复的timer
    • 2、重复的timer
    • 3、Invocation + RunLoop
    • 4、Fire Date + RunLoop
  • 八、NSPredicate
    • 1、谓词语法
    • 2、正则表达式语法
    • 3、包含关系
    • 4、数据过滤
    • 5、占位符
    • 6、正则表达式
    • 7、空值
    • 8、复合谓词
    • 9、Core Data中的使用
  • Demo
  • 参考文献

六、NSDate

1、Date

a、创建日期
// 使用时间间隔创建日期
NSTimeInterval secondsPerDay = 24 * 60 * 60;// 一天
NSDate *tomorrow1 = [[NSDate alloc] initWithTimeIntervalSinceNow:secondsPerDay];// 明天
NSDate *yesterday1 = [[NSDate alloc] initWithTimeIntervalSinceNow:-secondsPerDay];// 昨天
NSLog(@"使用时间间隔创建日期,昨天:%@,明天:%@",yesterday1, tomorrow1);

// 通过添加时间间隔创建日期
NSDate *today = [NSDate date];
NSDate *tomorrow2 = [today dateByAddingTimeInterval:secondsPerDay];// 明天
NSDate *yesterday2 = [today dateByAddingTimeInterval:-secondsPerDay];// 昨天
NSLog(@"通过添加时间间隔创建日期,昨天:%@,明天:%@",yesterday2, tomorrow2);

// 从1970年1月1日开始,20年之后的日期
NSDate *twentyYearsDate = [NSDate dateWithTimeIntervalSince1970:secondsPerDay*366*20];
NSLog(@"从1970年1月1日开始,20年之后的日期:%@",twentyYearsDate);

输出结果为:

2020-09-24 11:05:35.266263+0800 BasicGrammarDemo[97687:17759303] 使用时间间隔创建日期,昨天:2020-09-23 03:05:35 +0000,明天:2020-09-25 03:05:35 +0000
2020-09-24 11:05:35.266383+0800 BasicGrammarDemo[97687:17759303] 通过添加时间间隔创建日期,昨天:2020-09-23 03:05:35 +0000,明天:2020-09-25 03:05:35 +0000
2020-09-24 11:32:29.711559+0800 BasicGrammarDemo[98039:17779918] 从1970年1月1日开始,20年之后的日期:1990-01-16 00:00:00 +0000

b、比较日期
NSDate *today = [NSDate date];// 今天
NSDate *tomorrow = [today dateByAddingTimeInterval:24 * 60 * 60];// 明天

// 看看两个日期是否在一分钟(60秒)内
// fabs:求一个实数的绝对值
if (fabs([tomorrow timeIntervalSinceDate:today]) < 60)
{
    NSLog(@"两个日期在一分钟(60秒)内");
}
else
{
    NSLog(@"两个日期不在一分钟(60秒)内");
}


// 之前、相同、之后
switch ([today compare:tomorrow])
{
    case NSOrderedAscending:
        NSLog(@"之前");
        break;
    case NSOrderedSame:
        NSLog(@"相等");
        break;
    case NSOrderedDescending:
        NSLog(@"之后");
        break;
}

// 返回比较的两个日期中更早的那个
today = [today earlierDate:tomorrow];
NSLog(@"较早的日期:%@",today);

// 返回比较的两个日期中更晚的那个
today = [today laterDate:tomorrow];
NSLog(@"较晚的日期:%@",today);

输出结果为:

2020-09-24 14:40:57.125611+0800 BasicGrammarDemo[99895:17885247] 两个日期不在一分钟(60秒)内
2020-09-24 14:40:57.125685+0800 BasicGrammarDemo[99895:17885247] 之前
2020-09-24 14:40:57.125769+0800 BasicGrammarDemo[99895:17885247] 较早的日期:2020-09-24 06:40:57 +0000
2020-09-24 14:40:57.125844+0800 BasicGrammarDemo[99895:17885247] 较晚的日期:2020-09-25 06:40:57 +0000

c、日期格式
// 时间的本地化
NSDate *today = [NSDate date];
NSLocale *locale = [NSLocale currentLocale];// 系统当前的Locale
[today descriptionWithLocale:locale];
NSLog(@"时间的本地化:%@",today);

// 创建两个NSLocale,分别代表中国、美国
NSLocale *locales[] = {[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"], [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]};
    
// 设置NSDateFormatter的日期、时间风格
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];

// NSLocale
[dateFormatter setLocale:locales[0]];
// 设置自定义的格式模板
[dateFormatter setDateFormat:@"公元yyyy年MM月DD日 HH时mm分"];
// stringFromDate
NSString* stringFromDate = [dateFormatter stringFromDate:today];
NSLog(@"stringFromDate 时间的格式化:%@",stringFromDate);

// dateFromString
NSString *dateString = @"2020-01-18 06:50:24";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *dateFromString = [formatter dateFromString:dateString];
NSLog(@"dateFromString 时间的格式化:%@",dateFromString);

输出结果为:

2020-09-24 14:55:48.071631+0800 BasicGrammarDemo[309:17900007] 时间的本地化:2020-09-24 06:55:48 +0000
2020-09-24 14:55:48.072018+0800 BasicGrammarDemo[309:17900007] stringFromDate 时间的格式化:公元2020年09月268日 14时55分
2020-09-24 15:11:59.105726+0800 BasicGrammarDemo[764:17914033] dateFromString 时间的格式化:2020-01-17 22:50:24 +0000

d、时间戳
NSDate *today = [NSDate date];// 今天
NSDate *tomorrow = [today dateByAddingTimeInterval:24 * 60 * 60];// 明天

// 时间差
double interval = [today timeIntervalSinceDate:tomorrow];
NSLog(@"与明天的时间差:%f",interval);

// 与现在的时间差
interval = [today timeIntervalSinceNow];
NSLog(@"与现在的时间差:%f",interval);

输出结果为:

2020-09-24 14:40:57.126282+0800 BasicGrammarDemo[99895:17885247] 与明天的时间差:-86400.000000
2020-09-24 14:40:57.126339+0800 BasicGrammarDemo[99895:17885247] 与现在的时间差:-0.000070

2、Calendar

a、创建日历
// 创建日历对象
NSCalendar *currentCalendar = [NSCalendar currentCalendar];

// local、usersCalendar和currentCalendar是相等的,尽管它们是不同的对象
NSCalendar *usersCalendar = [[NSLocale currentLocale] objectForKey:NSLocaleCalendar];

// 还可以通过为所需日历指定标识符来创建任意日历对象
// 获取代表公历的Calendar对象
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

b、通过日历获取不同时间字段的信息
NSDate *date = [NSDate date];
NSLog(@"现在日期为:%@",date);

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

// 定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday;

// 获取不同时间字段的信息
NSDateComponents *components = [gregorian components:unitFlags fromDate:date];
NSLog(@"年:%ld 月:%ld ",(long)components.year,(long)components.month);

输出结果为:

2020-09-24 11:17:01.315888+0800 BasicGrammarDemo[97860:17769221] 现在日期为:2020-09-24 03:17:01 +0000
2020-09-24 11:17:01.316009+0800 BasicGrammarDemo[97860:17769221] 年:2020 月:9 

c、通过日历设置各时间字段的数值
// 设置各时间字段的数值
NSDateComponents *newComponents = [[NSDateComponents alloc] init];
newComponents.year = 2020;
newComponents.month = 10;
newComponents.day = 1;

// 恢复NSDate
date = [gregorian dateFromComponents:newComponents];
NSLog(@"设置的日期为:%@",date);

输出结果为:

2020-09-24 11:17:01.316112+0800 BasicGrammarDemo[97860:17769221] 设置的日期为:2020-10-17 16:00:00 +0000

d、日期组件
// 从日期获取组件
NSDate *today = [NSDate date];
// 获取代表公历的Calendar对象
NSCalendar *decompose = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
// 指定将会获取指定周、日的信息
NSDateComponents *weekdayComponents = [decompose components:(NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:today];

NSInteger day = [weekdayComponents day];
NSInteger newWeekday = [weekdayComponents weekday];
NSLog(@"今天是:%@",today);
NSLog(@"从日期获取组件 day:%ld, newWeekday:%ld",(long)day,(long)newWeekday);


// 从组件创建日期
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:2];// 星期一
[components setWeekdayOrdinal:1];// 这个月的第一个星期一
[components setMonth:10];
[components setYear:2020];
NSDate *getDate = [decompose dateFromComponents:components];
NSLog(@"从组件创建日期:%@",getDate);

输出结果为:

2020-09-24 15:09:33.838392+0800 BasicGrammarDemo[724:17912053] 今天是:2020-09-24 07:09:33 +0000
2020-09-24 15:09:33.838503+0800 BasicGrammarDemo[724:17912053] 从日期获取组件 day:24, newWeekday:5
2020-09-24 15:09:33.838631+0800 BasicGrammarDemo[724:17912053] 从组件创建日期:2020-10-01 16:00:00 +0000

e、计算日期
NSDate *today = [[NSDate alloc] init];
NSDate *tomorrow = [[NSDate alloc] initWithTimeIntervalSinceNow:24 * 60 * 60];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

// 增加一个半小时后
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setHour:1];
[offsetComponents setMinute:30];
NSDate *endOfWorldWar3 = [gregorian dateByAddingComponents:offsetComponents toDate:today options:0];
NSLog(@"现在的时间是:%@",today);
NSLog(@"一个半小时后就是第三次世界大战的结束时间:%@",endOfWorldWar3);

// 通过减去日期来获取前一周的星期天 9月24日为周四,则上周日为9月20日,具有与原始日期相同的小时、分钟和秒
NSDateComponents *weekdayComponents = [gregorian components:NSCalendarUnitWeekday fromDate:today];
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
// 从当前日期减去的天数,公历中星期日的值是1,所以减去1,如果今天是星期日,则减去0天
[componentsToSubtract setDay:(0 - ([weekdayComponents weekday] - 1))];
NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:today options:0];
NSLog(@"通过减去日期来获取前一周的星期天:%@",beginningOfWeek);

// 获取两个日期之间的差异
NSUInteger unitFlags = NSCalendarUnitDay | NSCalendarUnitMonth;
NSDateComponents *components = [gregorian components:unitFlags fromDate:today  toDate:tomorrow options:0];
NSInteger months = [components month];
// 输出两个日期day差数为0,因为8.5小时不到1天
NSInteger days = [components day];
NSLog(@"获取两个日期之间的差异,days:%ld,months:%ld",(long)days,(long)months);

输出结果为:

2020-09-24 15:27:18.050866+0800 BasicGrammarDemo[1024:17927076] 现在的时间是:2020-09-24 07:27:18 +0000
2020-09-24 15:27:18.050986+0800 BasicGrammarDemo[1024:17927076] 一个半小时后就是第三次世界大战的结束时间:2020-09-24 08:57:18 +0000
2020-09-24 15:27:18.051097+0800 BasicGrammarDemo[1024:17927076] 通过减去日期来获取前一周的星期天:2020-09-20 07:27:18 +0000
2020-09-24 15:27:18.051178+0800 BasicGrammarDemo[1024:17927076] 获取两个日期之间的差异,days:1,months:0

七、NSTimer

1、不重复的timer

// 在默认模式(NSDefaultRunLoopMode)下,用当前NSRunLoop对象自动注册新计时器
- (void)startTimer
{
    // 计时器在2秒后由运行循环自动触发,然后从运行循环中删除
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(targetMethod:) userInfo:self.userInfo repeats:NO];
}

- (void)targetMethod:(NSTimer *)theTimer
{
    NSDate *startDate = [[theTimer userInfo] objectForKey:@"StartDate"];
    NSLog(@"开始日期:%@",startDate);
}

- (NSDictionary *)userInfo
{
    return @{@"StartDate": [NSDate date]};
}

如果创建非重复计时器,它会自动停止。输出结果为:

2020-09-24 10:31:01.237970+0800 BasicGrammarDemo[97131:17724123] 开始日期:2020-09-24 02:30:59 +0000

2、重复的timer

- (void)startRepeatingTimer
{
    // 取消之前存在的timer
    [_repeatingTimer invalidate];
    
    // 创建新的timer
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(targetMethod:) userInfo:self.userInfo repeats:YES];
    _repeatingTimer = timer;
}

如果创建重复计时器,将计数器的repeats设置为YES的时候,self的引用计数会加1,因此可能会导致self(即viewController)不能释放,所以必须在viewDidDisappear方法里将计数器timer停止,否则可能会导致内存泄露,停止后,一定要将timer赋空,否则还是没有释放,会造成不必要的内存开销。

- (void)viewDidDisappear:(BOOL)animated
{
    NSLog(@"退出了当前页面,销毁了定时器");
    [self stopRepeatingTimer];
}

- (void)stopRepeatingTimer
{
    // 将计数器timer停止,否则可能会导致内存泄露
    [self.repeatingTimer invalidate];
    
    // 停止后,一定要将timer赋空,否则还是没有释放,会造成不必要的内存开销
    self.repeatingTimer = nil;
}

当再次进入定时器页面,重新开启定时器。

- (void)viewWillAppear:(BOOL)animated
{
    NSLog(@"进入定时器页面,重新开启定时器");
    
    // 开启定时器
    [self.repeatingTimer setFireDate:[NSDate distantPast]];//很远的过去
}

输出结果为:

2020-09-24 10:39:10.396329+0800 BasicGrammarDemo[97268:17732297] 开始日期:2020-09-24 02:39:06 +0000
2020-09-24 10:39:10.896330+0800 BasicGrammarDemo[97268:17732297] 开始日期:2020-09-24 02:39:06 +0000
2020-09-24 10:39:11.243983+0800 BasicGrammarDemo[97268:17732297] 退出了当前页面,销毁了定时器
2020-09-24 10:39:13.011582+0800 BasicGrammarDemo[97268:17732297] 进入定时器页面,重新开启定时器

发现定时器被销毁了,无法重新开启,所以可以使用viewWillDisappear替代viewDidDisappear里的实现。

// 在页面消失的时候关闭定时器,然后等页面再次打开的时候,又开启定时器
- (void)viewWillDisappear:(BOOL)animated
{
    // 关闭定时器
    [self.repeatingTimer setFireDate:[NSDate distantFuture]]; //很远的将来
    NSLog(@"退出了当前页面,只是关闭定时器未销毁,再次进入可重新启动");
}

输出结果为:

2020-09-24 10:41:46.070295+0800 BasicGrammarDemo[97329:17735552] 开始日期:2020-09-24 02:41:44 +0000
2020-09-24 10:41:46.570290+0800 BasicGrammarDemo[97329:17735552] 开始日期:2020-09-24 02:41:44 +0000
2020-09-24 10:41:46.879144+0800 BasicGrammarDemo[97329:17735552] 退出了当前页面,只是关闭定时器未销毁,再次进入可重新启动
2020-09-24 10:41:48.834727+0800 BasicGrammarDemo[97329:17735552] 进入定时器页面,重新开启定时器
2020-09-24 10:41:48.837701+0800 BasicGrammarDemo[97329:17735552] 开始日期:2020-09-24 02:41:44 +0000

3、Invocation + RunLoop

- (void)createUnregisteredTimer
{
    // 1、根据方法来初始化NSMethodSignature
    // 方法签名中保存了方法的名称/参数/返回值,协同NSInvocation来进行消息的转发
    NSMethodSignature *methodSignature = [self methodSignatureForSelector:@selector(invocationMethod:)];
    
    // 2、其实NSInvocation就是将一个方法变成一个对象
    // NSInvocation中保存了方法所属的对象/方法名称/参数/返回值
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    
    // 设置方法调用者
    [invocation setTarget:self];
    
    // 设置方法名称,这里的方法名一定要与方法签名类中的方法一致
    [invocation setSelector:@selector(invocationMethod:)];
    
    NSDate *startDate = [NSDate date];
    // 设置方法参数,这里的Index要从2开始,因为0跟1已经被占据了,分别是self(target),selector
    [invocation setArgument:&startDate atIndex:2];
    
    // 3、调用invoke方法
    [invocation invoke];
    
    // 使用invocation创建计时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 invocation:invocation repeats:YES];
    self.unregisteredTimer = timer;
    //[self addTimerToRunLoop];// 未添加到runloop中时
}

- (void)invocationMethod:(NSDate *)date
{
    NSLog(@"调用日期为:%@", date);
}

输出结果显示尽管计时器被配置为重复,但在触发一次之后,即被停止。

2020-09-24 10:45:21.267890+0800 BasicGrammarDemo[97383:17739828] 调用日期为:2020-09-24 02:45:21 +0000

现在将timer添加到run loop中。当前线程是主线程时,某些UI事件,比如ScrollView正在拖动,将会RunLoop切换成 NSEventTrackingRunLoopMode 模式,添加到RunLoop中的Timer就不会执行。

为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用RunLoopaddTimer:forMode:方法来把Timer按照指定的模式加入到RunLoop中。这里使用 NSRunLoopCommonModes 模式,这个模式相当于 NSDefaultRunLoopModeNSEventTrackingRunLoopMode 的结合。

// 将timer添加到run loop中
- (void)addTimerToRunLoop
{
    if (self.unregisteredTimer != nil)
    {
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addTimer:self.unregisteredTimer forMode:NSDefaultRunLoopMode];
    }
}

打开[self addTimerToRunLoop]语句的注释,输出结果为:

2020-09-24 10:49:05.506717+0800 BasicGrammarDemo[97429:17743337] 调用日期为:2020-09-24 02:49:04 +0000
2020-09-24 10:49:06.006756+0800 BasicGrammarDemo[97429:17743337] 调用日期为:2020-09-24 02:49:04 +0000
2020-09-24 10:49:06.506797+0800 BasicGrammarDemo[97429:17743337] 调用日期为:2020-09-24 02:49:04 +0000
.......

4、Fire Date + RunLoop

- (void)startFireDateTimer
{
    // 用日期初始化计时器
    NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
    
    // 尽管计时器被配置为重复,但在countedTimerFireMethod触发三次之后,它将停止
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate interval:0.5 target:self selector:@selector(countedTimerFireMethod:) userInfo:self.userInfo repeats:YES];
   
    self.timerCount = 1;
    
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
}

// 给计时器计算调用次数
- (void)countedTimerFireMethod:(NSTimer *)theTimer
{
    NSDate *startDate = [[theTimer userInfo] objectForKey:@"StartDate"];
    NSLog(@"开始日期: %@,调用次数: %lu", startDate, (unsigned long)self.timerCount);
    
    // 这将使计时器在触发三次后失效
    self.timerCount++;
    if (self.timerCount > 3)
    {
        [theTimer invalidate];
    }
}

输出结果为:

2020-09-24 10:50:43.824386+0800 BasicGrammarDemo[97467:17746749] 开始日期: 2020-09-24 02:50:42 +0000,调用次数: 1
2020-09-24 10:50:44.323420+0800 BasicGrammarDemo[97467:17746749] 开始日期: 2020-09-24 02:50:42 +0000,调用次数: 2
2020-09-24 10:50:44.823579+0800 BasicGrammarDemo[97467:17746749] 开始日期: 2020-09-24 02:50:42 +0000,调用次数: 3

八、NSPredicate

1、谓词语法

下面方法都是区分大小写的,如果要不区分大小写,则可以在运算符后面使用[c]

AND、&&:逻辑与
OR ||                     逻辑或
NOT !                    逻辑非

字符串比较运算符:
BEGINSWITH                检查某个字符串是否以指定的某个子串开头
ENDSWITH                  检查某个字符串是否以指定的某个子串结尾
CONTAINS                  检查某个字符串是否包含指定的某个子串
LIKE                      检查某个字符串是否匹配指定的字符串模板
MATCHES                   检查某个字符串是否匹配指定的正则表达式

操作集合的运算符:
ANY SOME                  指定只要集合中任意一个元素满足条件,即可返回YES。
ALL                       指定所有元素满足才返回YES。
NONE                      指定没有任何元素满足条件才返回YES。
IN                        只有当左边的表达式或值出现在右边的集合中才会返回YES。

数组:
array[index]              返回array数组中索引为index处的元素
array[FIRST]              返回array数组中第一个元素
array[LAST]               返回array数组中最后一个元素
array[SIZE]               返回array数组中元素的个数。

直接量:
FALSE NO                  逻辑假
TRUE YES                  逻辑真
NULL NIL                  代表一个空值
SELF                      代表正在被判断的对象
"text"或'text'            代表字符串
数组                      数组元素用英文字符隔开。eg:{'keli','zhangsan','lisi','wangwu'}
数值直接量                 包括整数、小数、科学计数法

2、正则表达式语法

^             指出一个字符串的开始
$             指出一个字符串的结束
"^iOS"        以iOS开头
"iOS$"        以iOS结尾
"^apple$"     开始和结尾都是apple的字符串,这个是唯一的,实际上就是apple
"apple"       包含apple
*  +  ?       重复出现的次数
?             0~1次
+             1~n次
*             0~n次
"ab*"         一个a后面跟着0~n个b
"ab+"         一个a后面跟着至少一个b
"ab?"         一个a后面跟着0~1个b
"a?b+$"       末尾有0~1个a跟着1~n个b
{}            表示重复的具体范围
"ab{4}"       一个a跟着4个b
"ab{1,}"      一个a跟着至少1个b
"ab{3,5}"     一个a跟着3~5个b
*             可以用{0,}表示
+             可以用{1,}表示
?             可以用{0,1}表示
|             或
"a|b"         一个字符串里有a或b
"(a|bcd)ef"   aef或bcdef
"(a|b)+c"     一串a和b混合的字符串后面跟一个c;
[ ]           表示在括号内的众多字符中,选择1-n个括号内的符合语法的字符作为结果
"[ab]"        一个a或b(相当于"a|b");
"[a-d]"       a到d中的一个(相当于"a|b|c|d"或者"[abcd]")
"^[a-zA-Z]"   以字母开头
"[0-9]a"      a前有一位数字
"[a-zA-Z0-9]$"以一个字母或数字结束
.             任意字符
"a.[a-z]"     a后面跟一个任意字符和一个小写字母
"^.{5}$"      长度为5的字符串
"@[^a-zA-Z]@" 在方括号里用^表示不希望出现的字符,^应在方括号里的第一位。表示两个@中不应该出现字母
"\d"          一个数字字符
"\D"          一个非数字字符
"\w "         包括下划线的任何单词字符
"\W"          匹配任何非单词字符
\             iOS中书写正则表达式,碰到转义字符,多加一个\

3、包含关系

IN

在集合中筛选出共同部分:

- (void)inArrayDemo
{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@",@[@"先生女士",@"妙不可言",@"哈哈哈哈哈"]];
    BOOL result = [predicate evaluateWithObject:@"哈哈哈哈哈"];
    if (result)
    {
        NSLog(@"哈哈哈哈哈在数组中");
    }
}

输出结果为:

2020-09-24 16:59:50.674351+0800 ChecksumsWarning[2412:18002210] 哈哈哈哈哈在数组中

筛选出set1中与set2中相同的元素:

NSSet<NSString *> *set1 = [NSSet setWithObjects:@"one", @"two", @"three", @"four", nil];
NSSet<NSString *> *set2 = [NSSet setWithObjects:@"three", @"four", @"five", nil];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", set2];
NSSet<NSString *> *commonElements = [set1 filteredSetUsingPredicate:predicate];
NSLog(@"commonElements == %@", commonElements);
NOT

筛选出array1array2不相同的元素:

NSSet<NSString *> *set1 = [NSSet setWithObjects:@"one", @"two", @"three", @"four", nil];
NSSet<NSString *> *set2 = [NSSet setWithObjects:@"three", @"four", @"five", nil];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", set2];
NSSet<NSString *> *commonElements = [set1 filteredSetUsingPredicate:predicate];
NSLog(@"commonElements == %@", commonElements);

筛选出数组中不符合like规则的元素:

NSArray <NSString *> *array = @[@"loading_001.png", @"loading_002.png", @"loading_003.png",@"我是一个老男孩", @"China,加油!", @"I like china"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF LIKE %@)", @"loading_*.png"];
for (NSString * ele in array) {
    NSLog(@"[%@]%@形如[loading_*.png]条件", ele, [predicate evaluateWithObject:ele] ? @"不符合" : @"符合");
}
CONTAINS

是否包含指定字串:

NSArray <NSString *> *array = @[@"ABC", @"我是一个老男孩", @"China,加油!", @"I like china"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"China"];
for (NSString * ele in array) {
    NSLog(@"[%@]中%@[China]", ele, [predicate evaluateWithObject:ele] ? @"包含" : @"不包含");
}

匹配字符串中是否包含指定的子串(忽略大小写)。如果需要忽略大小写可以使用[c]来做筛选选项。

NSArray <NSString *> *array = @[@"ABC", @"我是一个老男孩", @"China,加油!", @"I like china"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[c] %@", @"China"];
for (NSString * ele in array) {
    NSLog(@"[%@]中%@[China]", ele, [predicate evaluateWithObject:ele] ? @"包含" : @"不包含");
}
LIKE

筛选具备某一类相似特征的字符串,通常使用*作为通配符:

- (void)characterMatch
{
    NSString *prefix = @"prefix";
    NSString *suffix = @"suffix";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF like[c] %@",[[prefix stringByAppendingString:@"*"] stringByAppendingString:suffix]];
    NSLog(@"predicate = %@", [predicate predicateFormat]);
    BOOL result = [predicate evaluateWithObject:@"prefixxxxxxsuffix"];
    if (result)
    {
        NSLog(@"字符匹配成功");
    }
    else
    {
        NSLog(@"字符匹配失败");
    }
}

输出结果为:

2020-09-24 17:23:59.032000+0800 ChecksumsWarning[2770:18022215] predicate = SELF LIKE[c] "prefix*suffix"
2020-09-24 17:23:59.033539+0800 ChecksumsWarning[2770:18022215] 字符匹配成功
MATCH

匹配全是中文的字符串:

NSArray <NSString *> *array = @[@"ABC", @"我是一个老男孩", @"China,加油!", @"I like china"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"^[\\u4e00-\\u9fa5]+$"];
for (NSString * ele in array) {
    NSLog(@"[%@]中%@都是中文字符", ele, [predicate evaluateWithObject:ele] ? @"全部" : @"非全部");
}

匹配字符串中是否包含中文:

NSArray <NSString *> *array = @[@"ABC", @"我是一个老男孩", @"China,加油!"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @".*[\\u4e00-\\u9fa5]+.*"];
for (NSString * ele in array) {
    NSLog(@"[%@]中%@中文字符", ele, [predicate evaluateWithObject:ele] ? @"包含" : @"不包含");
}

4、数据过滤

a、比较大小
- (void)greaterThanDemo
{
    NSArray* array = @[@101,@200,@50,@5,@25,@12];
    NSLog(@"原数组:%@",array);
    // 创建谓词,要求该对象自身的值大于50
    NSPredicate *predicateThan50 = [NSPredicate predicateWithFormat:@"SELF > 50"];
    // 使用谓词执行过滤,过滤后只剩下值大于50的集合元素
    NSArray *filteredArray = [array filteredArrayUsingPredicate:predicateThan50];
    NSLog(@"数组过滤后只剩下值大于50的集合元素:%@",filteredArray);
}

输出结果为:

2020-09-24 16:18:24.591968+0800 ChecksumsWarning[1783:17966853] 原数组:(
    101,
    200,
    50,
    5,
    25,
    12
)
2020-09-24 16:18:24.592186+0800 ChecksumsWarning[1783:17966853] 数组过滤后只剩下值大于50的集合元素:(
    101,
    200
)
b、以特定字符开始和结尾
- (void)beginswithLetterDemo
{
    // 过滤掉不是以b开始的字符串
    NSMutableArray *letters = [@[@"Abc",@"Bbb",@"Ca"] mutableCopy];
    NSPredicate *predictForArray = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
    NSArray *beginWithB = [letters filteredArrayUsingPredicate:predictForArray];
    NSLog(@"过滤掉不是以b开始的字符串:%@",beginWithB);
}

输出结果为:

2020-09-24 17:04:15.974855+0800 ChecksumsWarning[2479:18006076] 过滤掉不是以b开始的字符串:(
    Bbb
)

筛选以China结尾的字符串(不区分大小写):

NSArray <NSString *> *array = @[@"ABC", @"我是一个老男孩", @"China,加油!", @"I like china"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF ENDSWITH[c] %@", @"China"];
for (NSString * ele in array) {
    NSLog(@"[%@]%@以%@结尾", ele, [predicate evaluateWithObject:ele] ? @"是" : @"不是", @"China");
}
c、逻辑运算符号

还可以使用诸如==, >, <, >=, <=,以及AND, OR等逻辑运算符号进行条件筛选。使用属性进行筛选时,可以省略SELF直接使用属性名进行筛选,简化书写。例如筛选成绩小于60,大于90的同学时可以直接使用score < 60 OR score >90

筛选出name="Dong"的同学:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.name == %@ ", @"Dong"];
NSArray<Student *> *result =  [students filteredArrayUsingPredicate:predicate];
NSLog(@"result == %@, name == %@", result, result.firstObject.name);

筛选出成绩大于80且小于90的同学:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score >= 80 and score <= 90"];
NSArray<Student *> *result =  [students filteredArrayUsingPredicate:predicate];
for (Student *stu in result) {
    NSLog(@"name == %@, score == %lf", stu.name, stu.score);
}

需要注意的一点是,当属性名作为参数传入匹配范型的时候,不能使用%@进行参数格式限定,而应该使用%K进行格式限定。因为在默认的情况下,使用%@时,系统会给参数添加引号例如:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@ ", @"Dong"];

编译之后会变成形如:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Dong' "];

而不是:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'name' == 'Dong' "];

所以当参数名需要使用参数进行传递时,需要使用:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@ ", @"name", @"Dong"];

而不是:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %@ ", @"name", @"Dong"];

5、占位符

a、占位符参数
- (void)placeholderDemo
{
    NSString *name = @"name";
    NSString *value = @"齐天大圣";
    NSDictionary *ditc = @{@"name":@"我是齐天大圣孙悟空水帘洞花果山大王"};
    // 创建谓词,该谓词中包含了2个占位符,相当于创建了谓词表达式 "name CONTAINS '齐天大圣'" 字典中的键为name的值是否包含"齐天大圣"
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K contains %@",name, value];
    // 使用谓词执行判断
    BOOL isContain = [predicate evaluateWithObject:ditc];
    if (isContain)
    {
        NSLog(@"字典中的键为name的值包含齐天大圣");
    }
    else
    {
        NSLog(@"字典中的键为name的值不包含齐天大圣");
    }
}

输出结果为:

2020-09-24 16:18:24.592350+0800 ChecksumsWarning[1783:17966853] 字典中的键为name的值包含齐天大圣
b、设置变量值
- (void)SUBSTRdemo
{
    // $SUBSTR相当于一个变量,需要我们调用时为它设置值
    NSPredicate* predicateTemplate = [NSPredicate predicateWithFormat:@"%K CONTAINS $SUBSTR" , @"number"];
    // 使用NDDictionary指定SUBSTR的值为'50',这就相当于创建了谓词表达式"pass CONTAINS '50'"
    NSPredicate* predicate = [predicateTemplate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:@"50",@"SUBSTR", nil]];
    // 使用谓词执行判断
    NSDictionary *ditc = @{@"number":@"50000123432425432"};
    BOOL isContain = [predicate evaluateWithObject:ditc];
    if (isContain)
    {
        NSLog(@"字典中的键为number的值包含50");
    }
    else
    {
        NSLog(@"字典中的键为number的值不包含50");
    }
}

输出结果为:

2020-09-24 16:35:53.575536+0800 ChecksumsWarning[1992:17978505] 字典中的键为number的值包含50

6、正则表达式

- (void)regularExpressionsDemo
{
    NSArray *array = @[@"TATACCATGGGCCATCATCATCATCATCATCATCATCATCATCACAG",
                       @"CGGGATCCCTATCAAGGCACCTCTTCG", @"CATGCCATGGATACCAACGAGTCCGAAC",
                       @"CAT", @"CATCATCATGTCT", @"DOG"];
    
    // 查找包含至少3个“CAT”序列重复的字符串,但后面没有“CA”
    NSPredicate *catPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES '.*(CAT){3,}(?!CA).*'"];
    NSArray *filteredArray = [array filteredArrayUsingPredicate:catPredicate];
    NSLog(@"查找包含至少3个CAT序列重复的字符串,但后面没有CA:%@",filteredArray);
}

输出结果为:

2020-09-24 16:40:32.491094+0800 ChecksumsWarning[2084:17983367] 查找包含至少3个“CAT”序列重复的字符串,但后面没有“CA”:(
    CATCATCATGTCT
)

7、空值

- (void)nullPredicateDemo
{
    NSString *firstName = @"Xie";
    NSArray *array = @[ @{ @"lastName" : @"JiaPei" }, @{ @"firstName" : @"Wang", @"lastName" : @"ErDe", @"birthday":[NSDate date]},@{ @"firstName" : @"Xie", @"lastName" : @"LingYun", @"birthday":[NSDate date]}];
    
    // 姓为Xie或者为空
    NSPredicate *predicateForNull = [NSPredicate predicateWithFormat:@"(firstName == %@) || (firstName = nil)", firstName];
    // 使用谓词执行判断
    NSArray *filteredArray = [array filteredArrayUsingPredicate:predicateForNull];
    NSLog(@"过滤后的数组为: %@", filteredArray);
    
    BOOL ok = [predicateForNull evaluateWithObject: [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"firstName"]];
    if (ok)
    {
        NSLog(@"存在姓为Xie或者为空");
    }
    else
    {
        NSLog(@"不存在姓为Xie或者为空");
    }
}

输出结果为:

2020-09-24 16:51:12.268705+0800 ChecksumsWarning[2267:17993506] 过滤后的数组为: (
        {
        lastName = JiaPei;
    },
        {
        birthday = "2020-09-24 08:51:12 +0000";
        firstName = Xie;
        lastName = LingYun;
    }
)
2020-09-24 16:51:12.268825+0800 ChecksumsWarning[2267:17993506] 存在姓为Xie或者为空

8、复合谓词

- (void)compoundPredicateDemo
{
    NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"SELF > 10"];
    NSLog(@"predicate1 = %@", [predicate1 predicateFormat]);
    NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"SELF < 50"];
    NSLog(@"predicate2 = %@", [predicate2 predicateFormat]);
    
    // NSAndPredicateType 多个谓词表达式之间添加AND,NSOrPredicateType 多个谓词表达式之间添加OR
    NSCompoundPredicate *compound = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[predicate1, predicate2]];
    NSLog(@"compound = %@", [compound predicateFormat]);
    
    BOOL result = [compound evaluateWithObject:@20];
    if (result)
    {
        NSLog(@"20在区间(20,50)内");
    }
    else
    {
        NSLog(@"20不在区间(20,50)内");
    }
}

输出结果为:

2020-09-24 17:17:16.746394+0800 ChecksumsWarning[2669:18016834] predicate1 = SELF > 10
2020-09-24 17:17:16.746510+0800 ChecksumsWarning[2669:18016834] predicate2 = SELF < 50
2020-09-24 17:17:16.746617+0800 ChecksumsWarning[2669:18016834] compound = SELF > 10 AND SELF < 50
2020-09-24 17:17:16.746715+0800 ChecksumsWarning[2669:18016834] 20在区间(20,50)内

9、Core Data中的使用

- (void)coreDataPredicate
{
    NSManagedObjectContext *managedObjectContext;
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];
    
    NSInteger salaryLimit = 100;
    // 查找收入超过指定金额的员工
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"salary > %@", salaryLimit];
    [request setPredicate:predicate];
    
    // 查询结果
    NSError *error;
    NSArray *resultArray = [managedObjectContext executeFetchRequest:request error:&error];
}

Demo

Demo在我的Github上,欢迎下载。
BasicsDemo

参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容

  • 一、设计模式是什么?你知道哪些设计模式,并简要叙述? 二、MVC和MVVM的区别 三、#import跟 #incl...
    傲骨天成科技阅读 250评论 0 1
  • 原创:知识点总结性文章创作不易,请珍惜,之后会持续更新,不断完善个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈...
    时光啊混蛋_97boy阅读 1,361评论 0 4
  • 喜欢就关注我呗! 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的...
    iOS白水阅读 1,095评论 0 2
  • 1.OC的类可以多重继承吗?可以实现多个接口吗?要想实现类似多重继承如何实现?答:OC不可以实现多重继承。可以实现...
    欧辰_OSR阅读 1,975评论 0 30
  • 最近一朋友正准备跳槽,就从各处搜索整理一些基础,便于朋友复习,也便于自己复习查看. 1. 回答person的ret...
    smile丽语阅读 1,725评论 0 7