OC学习12——字符串、日期、日历
前面主要学习了OC的基础知识,接下来将主要学习Foundation框架的一些常用类的常用方法。Foubdation框架是Cocoa编程、IOS编程的基础框架,包括代表字符串的NSString(代表字符序列不可变的字符串)、NSMutableString(代表字符序列可变的字符串),以及代表日期、时间的NSDate,关于日历的NSCalendar、NSDateComponents等常用类。
一、字符串
1、NSString代表字符序列不可变的字符串,即一旦NSString对象被创建,包含在这个对象中的字符序列就说不可改变的,直至这个对象被销毁。它的功能主要是处理字符串,主要功能如下:
- 穿件字符串
- 读取文件或网络URL来初始化字符串
- 获取字符串长度
- 获取字符串中的字符或字节
- 连接字符串
- 分割字符串
- 查找字符串内指定的字符和子串
- 替换字符串
- 比较字符串
- 字符串大小比较
- 字符串的大小写转换
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 unichar data[6] = {97 , 98 , 99 , 100 , 101, 102}; 7 // 使用Unicode数值数组初始化字符串 8 NSString* str = [[NSString alloc] 9 initWithCharacters: data length:6]; 10 NSLog(@"%@" , str); 11 char* cstr = "Hello, iOS!"; 12 // 将C风格的字符串转换为NSString对象 13 NSString* str2 = [NSString stringWithUTF8String:cstr]; 14 NSLog(@"%@" , str2); 15 // 将字符串写入指定文件 16 [str2 writeToFile:@"myFile.txt" 17 atomically:YES 18 encoding:NSUTF8StringEncoding 19 error:nil]; 20 // 读取文件内容,用文件内容初始化字符串 21 NSString* str3 = [NSString stringWithContentsOfFile:@"NSStringTest.m" 22 encoding:NSUTF8StringEncoding 23 error:nil]; 24 NSLog(@"%@" , str3); 25 } 26 }
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 NSString* str = @"Hello"; 7 NSString* book = @"《疯狂iOS讲义》"; 8 // 在str后面追加固定的字符串 9 // 原来字符串对象并不改变,只是将新生成的字符串重新赋给str指针变量 10 str = [str stringByAppendingString:@",iOS!"]; 11 NSLog(@"%@" , str); 12 // 获取字符串对应的C风格字符串 13 const char* cstr = [str UTF8String]; 14 NSLog(@"获取的C字符串:%s" , cstr); 15 // 在str后面追加带变量的字符串。 16 // 原来字符串对象并不改变,只是将新生成的字符串重新赋给str指针变量 17 str = [str stringByAppendingFormat:@"%@是一本非常不错的图书." 18 , book]; 19 NSLog(@"%@" , str); 20 NSLog(@"str的字符个数为:%lu" , [str length]); 21 NSLog(@"str按UTF-8字符集解码后字节数为:%lu" , [str 22 lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); 23 // 获取str的前10个字符组成的字符串 24 NSString* s1 = [str substringToIndex:10]; 25 NSLog(@"%@" , s1); 26 // 获取str的从第5个字符开始,与后面字符组成的字符串 27 NSString* s2 = [str substringFromIndex:5]; 28 NSLog(@"%@" , s2); 29 // 获取str从第5个字符开始,到第15个字符组成的字符串 30 NSString* s3 = [str substringWithRange:NSMakeRange(5, 15)]; 31 NSLog(@"%@" , s3); 32 // 获取iOS在str中出现位置 33 NSRange pos = [str rangeOfString:@"iOS"]; 34 NSLog(@"iOS在str中出现的开始位置:%ld, 长度为:%ld" 35 , pos.location , pos.length); 36 // 将str的所有字符转为大写 37 str = [str uppercaseString]; 38 NSLog(@"%@" , str); 39 } 40 }
2、NSMutableString代表字符序列可变的字符串,而且NSMutableString是NSString的子类,因此前面介绍的NSString所包含的方法,NSMutableString都可以直接使用,NSMutableString对象也可以直接当成NSString对象用。此外,NSMutableString还提供了很多可以修改字符串所包含字符序列的方法。
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 NSString* book = @"《疯狂iOS讲义》"; 7 // 创建一个NSMutableString对象 8 NSMutableString* str = [NSMutableString 9 stringWithString:@"Hello"]; 10 // 追加固定字符串 11 // 字符串所包含的字符序列本身发生了改变,因此无需重新赋值 12 [str appendString:@",iOS!"]; 13 NSLog(@"%@" , str); 14 // 追加带变量的字符串 15 // 字符串所包含的字符序列本身发生了改变,因此无需重新赋值 16 [str appendFormat:@"%@是一本非常不错的图书." , book]; 17 NSLog(@"%@" , str); 18 // 在指定位置插入字符串 19 // 字符串所包含的字符序列本身发生了改变,因此无需重新赋值 20 [str insertString:@"fkit.org" atIndex:6]; 21 NSLog(@"%@" , str); 22 // 删除从位置6到位置12的所有字符 23 [str deleteCharactersInRange:NSMakeRange(6, 12)]; 24 NSLog(@"%@" , str); 25 // 将从位置6到位置9的字符串替换成Objective-C 26 [str replaceCharactersInRange:NSMakeRange(6, 9) 27 withString:@"Objetive-C"]; 28 NSLog(@"%@" , str); 29 } 30 }
二、日期与时间
1、NSDate对象代表日期和时间,OC既提供了类方法来创建NSDate对象,也提供了大量以init开头的方法来初始化NSDate对象。
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 // 获取代表当前日期、时间的NSDate 7 NSDate* date1 = [NSDate date]; 8 NSLog(@"%@" , date1); 9 // 获取从当前时间开始,一天之后的日期 10 NSDate* date2 = [[NSDate alloc] 11 initWithTimeIntervalSinceNow:3600*24]; 12 NSLog(@"%@" , date2); 13 // 获取从当前时间开始,3天之前的日期 14 NSDate* date3 = [[NSDate alloc] 15 initWithTimeIntervalSinceNow: -3*3600*24]; 16 NSLog(@"%@" , date3); 17 // 获取从1970年1月1日开始,20年之后的日期 18 NSDate* date4 = [NSDate dateWithTimeIntervalSince1970: 19 3600 * 24 * 366 * 20]; 20 NSLog(@"%@" , date4); 21 // 获取系统当前的Locale 22 NSLocale* cn = [NSLocale currentLocale]; 23 // 获取NSDate在当前Locale下对应的字符串 24 NSLog(@"%@" , [date1 descriptionWithLocale:cn]); 25 // 获取两个日期之间较早的日期 26 NSDate* earlier = [date1 earlierDate:date2]; 27 // 获取两个日期之间较晚的日期 28 NSDate* later = [date1 laterDate:date2]; 29 // 比较两个日期,compare:方法返回NSComparisonResult枚举值 30 // 该枚举类型包含NSOrderedAscending、NSOrderedSame和 31 // NSOrderedDescending三个值,正如它们的名字暗示的。 32 // 分别代表调用compare:的日期位于被比较日期之前、相同、之后。 33 switch ([date1 compare:date3]) 34 { 35 case NSOrderedAscending: 36 NSLog(@"date1位于date3之前"); 37 break; 38 case NSOrderedSame: 39 NSLog(@"date1与date3日期相等"); 40 break; 41 case NSOrderedDescending: 42 NSLog(@"date1位于date3之后"); 43 break; 44 } 45 // 获取两个时间之间的时间差 46 NSLog(@"date1与date3之间时间差%g秒" 47 , [date1 timeIntervalSinceDate:date3]); 48 // 获取指定时间与现在的时间差 49 NSLog(@"date2与现在间时间差%g秒" 50 , [date2 timeIntervalSinceNow]); 51 } 52 }
2、NSDateFormatter代表一个日期格式器,其功能就是完成NSDate和NSString之间的转换。使用NSDateFormatter完成NSDate和NSString之间的转换的步骤如下:
- 创建一个NSDateFormatter对象
- 调用NSDateFormatter的setDateStyle:、setTimeStyle:方法设置格式化日期、时间的风格。其中,日期、时间风格有如下几个枚举值:
- NSDateFormatterNoStyle:不显示日期、时间的风格
- NSDateFormatterShortStyle:显示“短”的日期、时间的风格
- NSDateFormatterMediumStyle:显示“中等”的日期、时间的风格
- NSDateFormatterLongStyle:显示“长”的日期、时间的风格
- NSDateFormatterFullStyle:显示“完整”的日期、时间的风格
- 如果打算使用自己的格式模版,则调用NSDateFormatter的setDateFormat:方法设置日期、时间模版即可。
3、如果需要将NSDate转化为NSString,则调用NSDateFormatter的stringFromDate:方法执行格式化即可;如果需要将NSString转化为NSDate,则调用NSDateFormatter的dateFromString:方法执行格式化即可。
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 // 需要被格式化的时间 7 // 获取从1970年1月1日开始,20年之后的日期 8 NSDate* dt = [NSDate dateWithTimeIntervalSince1970: 9 3600 * 24 * 366 * 20]; 10 // 创建两个NSLocale,分别代表中国、美国 11 NSLocale* locales[] = { 12 [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"] 13 , [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]}; 14 NSDateFormatter* df[8]; 15 //为上面2个NSLocale创建8个DateFormat对象 16 for (int i = 0 ; i < 2 ; i++) 17 { 18 df[i * 4] = [[NSDateFormatter alloc] init]; 19 // 设置NSDateFormatter的日期、时间风格 20 [df[i * 4] setDateStyle:NSDateFormatterShortStyle]; 21 [df[i * 4] setTimeStyle:NSDateFormatterShortStyle]; 22 // 设置NSDateFormatter的NSLocale 23 [df[i * 4] setLocale: locales[i]]; 24 df[i * 4 + 1] = [[NSDateFormatter alloc] init]; 25 // 设置NSDateFormatter的日期、时间风格 26 [df[i * 4 + 1] setDateStyle:NSDateFormatterMediumStyle]; 27 [df[i * 4 + 1] setDateStyle:NSDateFormatterMediumStyle]; 28 // 设置NSDateFormatter的NSLocale 29 [df[i * 4 + 1] setLocale: locales[i]]; 30 df[i * 4 + 2] = [[NSDateFormatter alloc] init]; 31 // 设置NSDateFormatter的日期、时间风格 32 [df[i * 4 + 2] setDateStyle:NSDateFormatterLongStyle]; 33 [df[i * 4 + 2] setTimeStyle:NSDateFormatterLongStyle]; 34 // 设置NSDateFormatter的NSLocale 35 [df[i * 4 + 2] setLocale: locales[i]]; 36 df[i * 4 + 3] = [[NSDateFormatter alloc] init]; 37 // 设置NSDateFormatter的日期、时间风格 38 [df[i * 4 + 3] setDateStyle:NSDateFormatterFullStyle]; 39 [df[i * 4 + 3] setTimeStyle:NSDateFormatterFullStyle]; 40 // 设置NSDateFormatter的NSLocale 41 [df[i * 4 + 3] setLocale: locales[i]]; 42 } 43 for (int i = 0 ; i < 2 ; i++) 44 { 45 switch (i) 46 { 47 case 0: 48 NSLog(@"-------中国日期格式--------"); 49 break; 50 case 1: 51 NSLog(@"-------美国日期格式--------"); 52 break; 53 } 54 NSLog(@"SHORT格式的日期格式:%@" 55 , [df[i * 4] stringFromDate: dt]); 56 NSLog(@"MEDIUM格式的日期格式:%@" 57 , [df[i * 4 + 1] stringFromDate: dt]); 58 NSLog(@"LONG格式的日期格式:%@" 59 , [df[i * 4 + 2] stringFromDate: dt]); 60 NSLog(@"FULL格式的日期格式:%@" 61 , [df[i * 4 + 3] stringFromDate: dt]); 62 } 63 NSDateFormatter* df2 = [[NSDateFormatter alloc] init]; 64 // 设置自定义的格式器模板 65 [df2 setDateFormat:@"公元yyyy年MM月DD日 HH时mm分"]; 66 // 执行格式化 67 NSLog(@"%@" , [df2 stringFromDate:dt]); 68 NSString* dateStr = @"2013-03-02"; 69 NSDateFormatter* df3 = [[NSDateFormatter alloc] init]; 70 // 根据日期字符串的格式设置格式模板 71 [df3 setDateFormat:@"yyyy-MM-dd"]; 72 // 将字符串转换为NSDate对象 73 NSDate* date2 = [df3 dateFromString: dateStr]; 74 NSLog(@"%@" , date2); 75 } 76 }
4、Foundation框架还提供了NSCalendar对象来处理NSDate对象所包含的各个字段的数据,NSCalendar主要包含如下两个方法:
- (NSDateComponents *) components:fromDate::从NSDate中提取年、月、日、时、分、秒各时间字段信息
- dateFromComponents:(NSDateComponents *)comps:使用comps对象包含的年、月、日、时、分、秒各时间字段信息来创建NSDate对象
上面两个方法都用到了NSDateComponents对象,该对象是专门用于封装年、月、日、时、分、秒各时间字段信息的日期组件类。该对象十分简单,它只包含了对year、month、day、hour、minute、second、week、weekday等各字段的setter和getter方法。
从NSDate中分开获取各时间字段的数值的步骤如下:
- 创建NSCalendar对象
- 调用NSCalendar的components:fromDate:方法获取数值,返回一个NSDateComponents对象
- 调用NSDateComponents的getter方法获取个时间字段的数值
使用个时间字段的数值来初始化NSDate对象的步骤如下:
- 创建NSCalendar对象
- 创建一个NSDateComponents对象,并用setter方法对各个字段进行赋值
- 调用NSCalendar的dateFromComponents:方法初始化NSDate对象,该方法返回一个NSDate对象
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 // 获取代表公历的Calendar对象 7 NSCalendar *gregorian = [[NSCalendar alloc] 8 initWithCalendarIdentifier:NSGregorianCalendar]; 9 // 获取当前日期 10 NSDate* dt = [NSDate date]; 11 // 定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息 12 unsigned unitFlags = NSYearCalendarUnit | 13 NSMonthCalendarUnit | NSDayCalendarUnit | 14 NSHourCalendarUnit | NSMinuteCalendarUnit | 15 NSSecondCalendarUnit | NSWeekdayCalendarUnit; 16 // 获取不同时间字段的信息 17 NSDateComponents* comp = [gregorian components: unitFlags 18 fromDate:dt]; 19 // 获取各时间字段的数值 20 NSLog(@"现在是%ld年" , comp.year); 21 NSLog(@"现在是%ld月 " , comp.month); 22 NSLog(@"现在是%ld日" , comp.day); 23 NSLog(@"现在是%ld时" , comp.hour); 24 NSLog(@"现在是%ld分" , comp.minute); 25 NSLog(@"现在是%ld秒" , comp.second); 26 NSLog(@"现在是星期%ld" , comp.weekday); 27 28 // 再次创建一个NSDateComponents对象 29 NSDateComponents* comp2 = [[NSDateComponents alloc] 30 init]; 31 // 设置各时间字段的数值 32 comp2.year = 2013; 33 comp2.month = 4; 34 comp2.day = 5; 35 comp2.hour = 18; 36 comp2.minute = 34; 37 // 通过NSDateComponents所包含的时间字段的数值来恢复NSDate对象 38 NSDate *date = [gregorian dateFromComponents:comp2]; 39 NSLog(@"获取的日期为:%@" , date); 40 } 41 }
三、对象复制
1、NSObject类提供了copy和mutableCopy方法,通过这两个方法即可复制已有对象的副本。
- copy方法用于复制对象的副本。通常来说,copy方法返回的副本对象总是不可修改的,即使该原始对象是可修改。
- mutableCopy方法用于复制对象的可变副本,通常来说,mutableCopy方法返回的副本对象总是可修改的(MutableString等),即使该原始对象是不可修改。
- 无论如何,copy和mutableCopy方法返回的总是原对象的副本,方程序对复制的对象的副本进行修改时,原对象通常不受影响。
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 NSMutableString* book = [NSMutableString 7 stringWithString:@"疯狂iOS讲义"]; 8 // 复制book字符串的可变副本 9 NSMutableString* bookCopy = [book mutableCopy]; 10 // 对副本修改,对原字符串没有任何影响 11 [bookCopy replaceCharactersInRange: 12 NSMakeRange(2, 3) 13 withString:@"Android"]; 14 // 此处看到原字符串的值并没有改变 15 NSLog(@"book的值为:%@" , book); 16 // 字符串副本发生了改变。 17 NSLog(@"bookCopy的值为:%@" , bookCopy); 18 NSString* str = @"fkit"; 19 // 复制str(不可变字符串)的可变副本 20 NSMutableString* strCopy = [str mutableCopy]; //① 21 // 向可变字符串后面追加字符串 22 [strCopy appendString:@".org"]; 23 NSLog(@"%@" , strCopy); 24 // 调用book(可变字符串)的copy方法,程序返回一个不可修改的副本 25 NSMutableString* bookCopy2 = [book copy]; //② 26 // 由于bookCopy2是不可修改的,因此下面代码将会出现错误 27 [bookCopy2 appendString:@"aa"]; 28 } 29 }
2、当程序调用对象的copy或mutableCopy方法时,实际上程序底层需要调用copyWithZone:或mutableCopyWithZone:方法来完成实际的复制工作,copy或mutableCopy方法的返回值实际上就是copyWithZone:或mutableCopyWithZone:方法的返回值。而copyWithZone:和mutableCopyWithZone:方法并不是NSObject的方法,而是NSCopy和NSMutableCopy协议中的方法。所以,对于哦我们自定义的类,如果只是简单地继承自NSObject类,在程序中如果直接调用该自定义类的copy或mutableCopy方法时,编译是没有问题的,但是在运行时会出现问题,原因就在于自定义类没有实现NSCopy和NSMutableCopy协议中copyWithZone:和mutableCopyWithZone:方法。所以,如果我们自定义类需要实现copy或mutableCopy方法,则通常需要做以下事情:
- 让该类实现NSCopy和NSMutableCopy协议
- 让该类实现copyWithZone:和mutableCopyWithZone:方法
1 #import <Foundation/Foundation.h> 2 3 @interface FKDog : NSObject <NSCopying> 4 @property (nonatomic , strong) NSMutableString* name; 5 @property (nonatomic , assign) int age; 6 @end
1 #import "FKDog.h" 2 3 @implementation FKDog 4 - (id)copyWithZone:(NSZone*)zone 5 { 6 NSLog(@"--执行copyWithZone--"); 7 // 使用zone参数创建FKDog对象 8 FKDog* dog = [[[self class] allocWithZone:zone] init]; 9 dog.name = self.name; 10 dog.age = self.age; 11 return dog; 12 } 13 14 @end
1 #import <Foundation/Foundation.h> 2 #import "FKDog.h" 3 4 int main(int argc , char * argv[]) 5 { 6 @autoreleasepool{ 7 // 创建一个FKDog对象 8 FKDog* dog1 = [FKDog new]; 9 dog1.name = [NSMutableString stringWithString:@"旺财"]; 10 dog1.age = 20; 11 // 复制副本 12 FKDog* dog2 = [dog1 copy]; 13 // 复制对象的可变副本 14 // FKDog* dog2 = [dog1 mutableCopy]; 15 dog2.name = [NSMutableString stringWithString:@"snoopy"]; 16 dog2.age = 12; 17 NSLog(@"dog1的名字为:%@" , dog1.name); 18 NSLog(@"dog1的年龄为:%d" , dog1.age); 19 NSLog(@"dog2的名字为:%@" , dog2.name); 20 NSLog(@"dog2的年龄为:%d" , dog2.age); 21 } 22 }
3、深复制和浅复制 Objective-C中的深拷贝和浅拷贝 其实OC中深复制和浅复制的概念与在Java、C++中的概念是一致的。简单来说就是深复制取完全的一个新的副本对象,副本对象与原对象没有任何交集,而浅复制则是原对象与副本对象之间还有交集。所谓交集指的是存在某一个指针变量指向同一个对象的情况。
- 浅复制:当对象的属性是指针变量时,如果程序只是复制该指针的地址,而不是真正赋值指针所指向的对象,这种方式就被称之为浅复制。
- 深复制:深复制不仅会赋值对象本身,还会递归赋值每个指针类型的属性,直到两个对象没有任何共用的部分。