文章目录
前言
本周学习了OC中的foundation框架,下面我将总结一下本周的学习成果。
一、字符串(NSString与NSMutableString)
OC中有两种字符串:NSString与NSMutableString,其中NSString代表字符序列不可变的字符串,NSMutableString代表字符序列可变的字符串。
1.1.创建字符串
创建字符串的方法有三种,第一种使用init开头的实例方法,第二种使用string开头的类方法,第三种使用@""的形式给出的字符串直接量。
示例代码如下:
NSStringTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//使用unicode数值数组初始化字符串
unichar data[6] = {97, 98, 99, 100, 101, 102};
NSString* str = [[NSString alloc] initWithCharacters:data length:6];
NSLog(@"%@", str);
//将C风格的字符串转换为NSString对象
char* cstr = "Hello, world!";
NSString* str2 = [NSString stringWithUTF8String: cstr];
NSLog(@"%@", str2);
//将字符串写入指定文件
[str2 writeToFile:@"myFile.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
//读取文件内容,用文件内容初始化字符串
NSString* str3 = [NSString stringWithContentsOfFile:@"NSStringTest.m" encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@", str3);
}
return 0;
}
1.2.NSString的常用功能
示例代码如下:
NSStringTest2.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str = @"Hello";
NSString* str1 = @"NI HAO";
NSString* person = @"张三说";
NSString* game = @"原神";
//在原来的字符串后面追加固定的字符串
str = [str stringByAppendingString:@" world!"];
NSLog(@"%@", str);
//获取字符串对应的C风格字符串
const char* cstr = [str UTF8String];
NSLog(@"获取的C字符串:%s", cstr);
//在person后面追加带变量的字符串
person = [person stringByAppendingFormat:@"%@是一款非常好玩的开放世界冒险游戏", game];
NSLog(@"%@", person);
NSLog(@"str的字符个数为:%lu", [str length]);
NSLog(@"str按UTF-8字符集解码后的字节数为:%lu", [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
//获取person的前10个字符组成的字符串
NSString* s1 = [person substringToIndex:10];
NSLog(@"%@", s1);
//获取person从第五个字符开始,与后面所有字符组成的字符串
NSString* s2 = [person substringFromIndex:5];
NSLog(@"%@", s2);
//获取从第五个字符开始,与后面十五个字符所组成的字符串
NSString* s3 = [person substringWithRange:NSMakeRange(5, 15)];
NSLog(@"%@", s3);
//获取原神在perosn中出现的位置
NSRange pos = [person rangeOfString:@"原神"];
NSLog(@"原神在person出现的起始位置是%ld, 长度为:%ld", pos.location, pos.length);
// 将str中所有字符转换为大写
str = [str uppercaseString];
NSLog(@"%@", str);
// 将str1中所有字符转换为小写
str1 = [str1 lowercaseString];
NSLog(@"%@", str1);
//将str1中首字母大写
str1 = [str1 capitalizedString];
NSLog(@"%@", str1);
}
return 0;
}
运行结果如下:
1.3.可变字符串(NSMutableString)
NSMutableString是NSString的字类,因此前面介绍的NSString所包含的方法,NSMutableString都可以使用,NSMutableString对象也可以直接当成NSString对象使用。
下面介绍NSMutableString的另外一些方法。
示例代码如下:
NSMutableStringTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* person = @":你好,世界";
NSMutableString* str = [NSMutableString stringWithString:@"hello"];
//追加固定字符串
[str appendString:@" wrold"];
NSLog(@"%@", str);
//追加带变量的字符串
[str appendFormat:@"%@", person];
NSLog(@"%@", str);
//在指定位置插入字符串
[str insertString:@"的意思" atIndex:17];
NSLog(@"%@", str);
//删除从位置6开始、长度为12的所有字符
[str deleteCharactersInRange:NSMakeRange(6, 12)];
NSLog(@"%@", str);
//将从位置2开始、长度为2的所有字符替换成原神最好玩了
[str replaceCharactersInRange:NSMakeRange(2, 2) withString:@"原神最好玩了"];
NSLog(@"%@", str);
}
return 0;
}
运行结果如下:
二、日期与时间
OC提供NSDate、NSCalendar对象来处理日期、时间,还提供了日期格式器来处理日期与字符串之间的转换。
2.1.日期与时间(NSDate)
OC提供类方法来创建NSDate对象,也提供许多以init开头的方法来初始化NSDate对象。
下面介绍一些有关NSDate类的方法。
示例代码如下:
NSDateTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//获取当前日期
NSDate* date1 = [NSDate date];
NSLog(@"当前日期为:%@", date1);
//获取从当前时间开始,一天之后的日期
NSDate* date2 = [[NSDate alloc] initWithTimeIntervalSinceNow:3600 * 24];
NSLog(@"%@", date2);
//获取从当前时间开始,三天之前的日期
NSDate* date3 = [[NSDate alloc] initWithTimeIntervalSinceNow:-3600 * 24];
NSLog(@"%@", date3);
//获取从1970年1月1日开始,20年之后的日期
NSDate* date4 = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
NSLog(@"%@", date4);
//获取系统当前的NSLocale
NSLocale* cn = [NSLocale currentLocale];
NSLog(@"%@", cn);
//获取NSDate在当前NSLocade下对应的字符串
NSLog(@"%@", [date1 descriptionWithLocale:cn]);
//获取两个日前之间较早的日期
NSDate* earlier = [date1 earlierDate:date2];
NSLog(@"较早的日期是:%@", earlier);
//获取两个日期之间较晚的日期
NSDate* later = [date1 laterDate:date2];
NSLog(@"较晚的日期是:%@", later);
//比较两个日期,compare:方法返回NSCompareisonResult枚举值
//该枚举类型包含NSOrderedAscending、NSOrderSame和NSOrderedDescending值
//分别代表调用compare:的日期位于被比较日期之前、相同、之后
switch ([date1 compare:date3]) {
case NSOrderedAscending:
NSLog(@"date1位于date3之前");
break;
case NSOrderedSame:
NSLog(@"date1与date3相等");
break;
case NSOrderedDescending:
NSLog(@"date1位于date3之后");
break;
}
//获取两个时间之间的时间差
NSLog(@"date1与date3之间时间差%g秒", [date1 timeIntervalSinceDate:date3]);
//获取指定时间与现在的时间差
NSLog(@"date2与现在时间差为%g秒", [date2 timeIntervalSinceNow]);
}
return 0;
}
运行结果如下:
2.2.日期格式器(NSDateFormatter)
NSDateFormatter的功能室完成NSDate与NSString之间的转换。
使用步骤如下:
1.创建一个NSDateFormatter对象
2.调用NSDateFormatter的setDateStyle: setTimeStyle:方法设置格式化日期、时间的风格。
日期、时间风格支持如下几个枚举值。
如果想使用自己的格式模版则调用NSDateFormatter的setDateFormat:方法。
如果需要将NSDate转换为NSString,则调用NSDateFormatter的stringFromDate:方法,如果需要将NSString转换为NSDate,则调用NSDateFormatter的DateFromString:方法。
下面介绍NSDateFormatter的功能和用法
示例代码如下:
NSDateFormatterTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//需要被格式化的时间
//获取从1970年1月1日开始,20年后的日期
NSDate* dt = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
//创建两个NSLocate分别代表中国和美国
NSLocale* locales[2] = {[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"], [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]};
//为上面两个NSLocale创建8个NSDateFormatter对象
NSDateFormatter* df[8];
for (int i = 0; i < 2; i++) {
df[i * 4] = [[NSDateFormatter alloc] init];
//设置NSDateFormatter的日期、时间风格
[df[i * 4] setDateStyle:NSDateFormatterShortStyle];
[df[i * 4] setTimeStyle:NSDateFormatterShortStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4] setLocale:locales[i]];
df[i * 4 + 1] = [[NSDateFormatter alloc] init];
//设置NSDateFormatter的日期、时间风格
[df[i * 4 + 1] setDateStyle:NSDateFormatterMediumStyle];
[df[i * 4 + 1] setTimeStyle:NSDateFormatterMediumStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 1] setLocale:locales[i]];
df[i * 4 + 2] = [[NSDateFormatter alloc] init];
//设置NSDateFormatter的日期、时间风格
[df[i * 4 + 2] setDateStyle:NSDateFormatterLongStyle];
[df[i * 4 + 2] setTimeStyle:NSDateFormatterLongStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 2] setLocale:locales[i]];
df[i * 4 + 3] = [[NSDateFormatter alloc] init];
//设置NSDateFormatter的日期、时间风格
[df[i * 4 + 3] setDateStyle:NSDateFormatterFullStyle];
[df[i * 4 + 3] setTimeStyle:NSDateFormatterFullStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 3] setLocale:locales[i]];
}
for (int i = 0; i < 2; i++) {
switch (i) {
case 0:
NSLog(@"-----中国日期格式-----");
break;
case 1:
NSLog(@"-----美国日期格式-----");
break;
}
NSLog(@"SHORT格式的日期格式:%@", [df[i * 4] stringFromDate:dt]);
NSLog(@"MEDIUM格式的日期格式:%@", [df[i * 4 + 1] stringFromDate:dt]);
NSLog(@"LONG格式的日期格式:%@", [df[i * 4 + 2] stringFromDate:dt]);
NSLog(@"FULL格式的日期格式:%@", [df[i * 4 + 3] stringFromDate:dt]);
}
NSDateFormatter* df2 = [[NSDateFormatter alloc] init];
//设置自定义的格式模版
[df2 setDateFormat:@"公元yyyy年MM月DD日HH时mm分"];
//执行格式化
NSLog(@"%@", [df2 stringFromDate:dt]);
NSString* datestr = @"2000-12-25";
NSDateFormatter* df3 = [[NSDateFormatter alloc] init];
//根据日期字符串的格式设置格式模版
[df3 setDateFormat:@"yyyy-MM-dd"];
//将字符串转换为NSDate对象
NSDate* date2 = [df3 dateFromString:datestr];
NSLog(@"%@", date2);
}
return 0;
}
运行结果如下:
2.3.日历(NSCalendar)与日期组件(NSDateComponents)
为了能分开处理NSDate对象所包含的各个字段的数据,Foundation框架提供了NSCalendar对象,这个对象包含两个常用方法。
(NSDateComponents*)components:fromDate:方法从NSDate中提取年、月、日、时、分、秒等信息。
dateFromComponents:(NSDateComponents*)comps:方法使用comps对象包含的年、月、日、时、分、秒等信息来创建NSDate对象。
从NSDate获取数值的步骤如下:
下面介绍如何利用NSCalendar和NSDateComponents分开处理NSDate中各时间字段的数值。
示例代码如下:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 获取代表公历的NSCalendar对象
NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
//获取当前日期
NSDate* dt = [NSDate date];
//定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday;
//获取不同时间字段的信息
NSDateComponents* comp = [gregorian components: unitFlags fromDate:dt];
//获取各时间字段的数值
NSLog(@"现在是%ld年", comp.year);
NSLog(@"现在是%ld月", comp.month);
NSLog(@"现在是%ld日", comp.day);
NSLog(@"现在是%ld时", comp.hour);
NSLog(@"现在是%ld分", comp.minute);
NSLog(@"现在是%ld秒", comp.second);
NSLog(@"现在是星期%ld", comp.weekday);
NSDateComponents* comp2 = [[NSDateComponents alloc] init];
//设置各时间字段的数值
comp2.year = 2077;
comp2.month = 12;
comp2.day = 25;
comp2.hour = 6;
comp2.minute = 18;
//通过NSDateComponents所包含的各时间字段的数值来恢复NSDate对象
NSDate* date = [gregorian dateFromComponents:comp2];
NSLog(@"获取的日期为:%@", date);
}
return 0;
}
运行结果如下:
三、对象复制
NSObject类提供了copy和mutablecopy方法,来完成复制已有对象 的副本的操作。
3.1.copy与mutablecopy方法
copy方法总是返回对象的不可修改的副本即使该对象本身是可修改的。
mutablecopy方法总是返回对象的可修改的副本即使该对象本身是不可修改的。
当程序对副本进行修改时原对象不会受影响。
示例代码如下:
CopyTest.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString* game = [NSMutableString stringWithString:@"原神"];
//复制game字符串的可变副本
NSMutableString* gameCopy = [game mutableCopy];
[gameCopy replaceCharactersInRange:NSMakeRange(0, 1) withString:@"最好玩了"];
//原字符串并没有改变
NSLog(@"game的值为:%@", game);
//字符串副本发生改变
NSLog(@"gameCopy的值为:%@", gameCopy);
NSString* str = @"星穹铁道";
NSMutableString* strCopy = [str mutableCopy];
[strCopy appendString: @"最好玩了"];
NSLog(@"%@", strCopy);
//调用game(可变字符串)的copy方法,程序返回一个不可修改的副本
NSMutableString* gameCopy2 = [game copy];
//由于gameCopy2是不可修改的,因此下面的代码将会出现错误
[gameCopy2 appendString:@"太好玩了"];
NSLog(@"%@", gameCopy2);
}
return 0;
}
运行结果如下:
需要注意的是当对不可变字符串的可变副本进行修改时不会导致错误,而对可变字符串的不可修改的副本进行修改时会导致错误。
3.2.NSCopying与NSMutableCopying协议
自定义类不能直接调用copy和mutablecopy方法来复制自身,如果想实现自定义类调用copy和mutablecopy方法来复制自身的话,通常需要做如下事情:
示例代码如下:
Person1.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person1 : NSObject <NSCopying>
@property (nonatomic, strong)NSString* name;
@property (nonatomic, assign)int age;
- (id)copyWithZone:(NSZone *)zone;
@end
NS_ASSUME_NONNULL_END
Person1.m
#import "Person1.h"
@implementation Person1
@synthesize name = _name;
@synthesize age;
- (id)copyWithZone:(NSZone *)zone {
NSLog(@"--执行copyWithZone:--");
//使用zone参数创建Person1对象
Person1* person = [[[self class] allocWithZone:zone] init];
person.name = self.name;
(void)(person),age = self.age;
return person;
}
@end
上面程序让Person类实现了NSCopying协议与copyWithZone:方法,在该方法中重新创建了一个Person对象并让该对象的所有属性值与被复制对象的属性值相等,最后返回这个新创建的对象。[self class] 表示当前对象所属的类。它返回的是一个 Class 类型的指针,指向当前对象的类。copyWithZone 方法通常使用 allocWithZone 方法来创建新的对象,而不是使用 alloc 方法。这是因为 allocWithZone 方法可以指定新对象分配内存的位置。如果没有指定分配内存的位置,allocWithZone 方法就会使用默认的分配器来分配内存。
main.m
#import <Foundation/Foundation.h>
#import "Person1.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person1* person = [[Person1 alloc] init];
person.name = [NSMutableString stringWithString:@"张三"];
person.age = 18;
Person1* person2 = [person copy];
person2.name = [NSMutableString stringWithString:@"原神"];
person2.age = 10;
NSLog(@"person的名字:%@", person.name);
NSLog(@"person的年龄:%d", person.age);
NSLog(@"person2的名字:%@", person2.name);
NSLog(@"person2的年龄:%d", person2.age);
}
return 0;
}
运行结果如下:
从结果可以看出通过copy方法复制的不可变副本可以被修改这与前面提到的copy方法的注意事项相矛盾这是为什么呢?原来此处的Person1类没有提供对应的不可变类,自然就无法复制不可变的Person1对象,如果提供了当然就返回不可变的副本了。
示例代码如下:
Father.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Father : NSObject<NSCopying>
@property(nonatomic, strong)NSMutableString* name;
@property(nonatomic, assign)int age;
- (id)copyWithZone:(NSZone*)zone;
@end
NS_ASSUME_NONNULL_END
Father.m
#import "Father.h"
@implementation Father
- (id)copyWithZone:(NSZone *)zone {
Father* father = [[[self class] allocWithZone:zone] init];
father.name = self.name;
father.age = self.age;
return father;
}
@end
Son.h
#import <Foundation/Foundation.h>
#import "Father.h"
NS_ASSUME_NONNULL_BEGIN
@interface Son : Father
@property(nonatomic, strong)NSMutableString* sex;
- (id)copyWithZone:(NSZone*)zone;
@end
NS_ASSUME_NONNULL_END
Son.m
#import "Son.h"
#import "Father.h"
@implementation Son
- (id)copyWithZone:(NSZone *)zone {
id obj = [super copyWithZone:zone];
Son *son = (Son *)obj;//将obj强制转换为Son类型是为了访问Son类特有的属性和方法。
son.name = [self.name mutableCopy];//将原对象的name属性复制一份可变副本再将可变副本的值给新对象的name属性值
son.sex = [self.sex mutableCopy];
son.age = self.age;
return obj;
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Son.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Son* son1 = [[Son alloc] init];
son1.name = [NSMutableString stringWithString:@"原神"];
son1.sex = [NSMutableString stringWithString:@"男"];
NSLog(@"son1姓名:%@", son1.name);
NSLog(@"son1性别:%@", son1.sex);
Son* son2 = [son1 copy];
NSLog(@"son2姓名:%@", son2.name);
NSLog(@"son2性别:%@", son2.sex);
son2.sex = [NSMutableString stringWithString:@"女"];
NSLog(@"son2修改性别后为:%@", son2.sex);
}
return 0;
}
运行结果如下:
3.3.浅复制与深复制
为了更好地理解浅复制与深复制,先看下面的程序:
#import <Foundation/Foundation.h>
#import "Person1.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person1* person = [[Person1 alloc] init];
person.name = [NSMutableString stringWithString:@"张三"];
person.age = 18;
Person1* person2 = [person copy];
// person2.name = [NSMutableString stringWithString:@"原神"];
// person2.age = 10;
[person2.name replaceCharactersInRange: NSMakeRange(0, 2) withString:@"星穹铁道"];
NSLog(@"person的名字:%@", person.name);
// NSLog(@"person的年龄:%d", person.age);
NSLog(@"person2的名字:%@", person2.name);
// NSLog(@"person2的年龄:%d", person2.age);
}
return 0;
}
运行结果如下:
我们只是修改了person2的name属性值,person的name属性值同样被修改。这是因为name只是个指针变量并且该变量中存放的只是字符串的地址,这样的话Person对象的name属性和被复制对象的name属性指向的就是同一字符串,我们创建了两个Person对象并分别用两个指针指向这两个对象(这两个对象的name属性指向同一NSMutableString对象)当修改任意一个Person的name属性值时,另一个对象的name属性值也会被修改。当对象属性为指针变量如果程序只复制改指针的地址而不是指针所指的对象则称“浅复制”。 深复制则是不仅复制对象本身还会“递归”复制每个指针类型的属性直到两个对象没有共用的部分。
将上面的copyWtihZone:方法改成下面的形式即可实现深复制
//为深复制实现的copyWithZone方法
- (id)copyWithZone:(NSZone *)zone {
NSLog(@"--执行copyWithZone:--");
//使用zone参数创建Person1对象
Person1* person = [[[self class] allocWithZone:zone] init];
person.name = [self.name mutableCopy];
person.age = self.age;
return person;
}
运行结果如下:
上面程序现将原对象的name属性复制一份可变副本再将该可变副本的值赋给新对象的name属性,这样就保证了原对象与新对象之间没有共用的部分,它们在内存中占据不同的位置,修改一个对象的属性不会影响另一个对象的属性。
3.4.setter方法的复制选项
copy指示符就是指定当程序调用setter方法复制时,实际上是将传入参数的副本给程序的实例变量。
示例代码如下:
Item.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Item : NSObject
@property(nonatomic, copy)NSMutableString* name;
@end
NS_ASSUME_NONNULL_END
Item.m
#import "Item.h"
@implementation Item
@end
main.m
#import <Foundation/Foundation.h>
#import "Item.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Item* item = [Item new];
item.name = [NSMutableString stringWithString:@"原神"];
[item.name appendString:@"最好玩了"];
}
return 0;
}
运行结果如下:
copy方法默认复制该对象的不可变副本因此当修改时会发生错误。
四、OC集合概述
OC中集合可分为:NSArray、NSSet和NSDictionary三种体系,其中NSArray代表有序、可重复的集合;NSSet代表无序、不可重复的集合;NSDictionary代表具有映射关系的集合。
集合类和数组不一样,数组元素可以是基本类型的值也可以是对象,而集合只能保存对象(实际上是保存对象的指针变量)。
访问NSArray集合中的元素可以根据索引,访问NSDictionary中的元素可以根据每项元素的key值来访问,访问NSSet集合的元素只能根据元素本身访问。
总结
以上就是本篇文章的所有内容如果对你有帮助的话请点赞支持一下~