学习目标
1.【理解】NSDictionary
2.【了解】NSDictionary的常用方法
3.【掌握】NSDictionary的遍历
4.【理解】NSMutableDictionary
5.【了解】NSFileManager
6.【了解】Foundation常用结构体
7.【了解】NSNumber
8.【了解】NSValue
9.【了解】NSDate
10.【了解】集合的内存管理
11.【了解】copy和mutableCopy
12.【理解】单例模式
一、NSDictionary
NSArray和NSMutableArray存储数据是将每一个元素挨个存储到数组中,但是NSMutableArray如果删除、新增元素后,这个元素后面的元素对应的下标就会发生变化,所以NSMutableArray的缺点是下标不可靠。为此Apple提供了一个新的类NSDictionary来解决这个问题,在存储数据到数组时,为存储到数组中每个元素取一个固定的别名(key)。NSDictionary字典数组一旦创建完毕,其键值对的个数就固定,并且无法删除、新增键值对(不可变字典集合)。特别要注意的是键值对中的值只能是OC对象,键必须要是实现了NSCoping协议的OC对象。
//创建一个空的字典集合,没有意义,因为无法为这个字典数组新增元素 NSDictionary *dict1 = [NSDictionary dictionary]; //创建字典集合,并指定键值对,注意这里值在前键在后 NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:@"jack",@"name",@"18",@"age",nil]; //快速创建字典的方法,以 键值对 形式创建 NSDictionary *dict3 = @{@"name" : @"jack",@"age" : @"18",}; NSLog(@"%@",dict3);//输出 {age = 18;name = jack;}
存储原理:字典中的键值对仍然是以数组的形式存储的,但并不是挨个存储。在往字典集合中存储数据的时候,会将key和数组长度根据哈希算法计算出一个下标。取值的时候,也是将key和数组长度根据哈希算法计算出一个下标,然后根据下标取值。
存的效率:NSArray效率更高。
取的效率:如果把所有数据一次性取出使用NSArray,如果只取出特定的元素使用NSDictionary。
二、NSDictionary的常用方法
//运行存在键相同的键值对,但后面存的存不进去 NSDictionary *dict3 = @{@"name" : @"jack",@"age" : @"18"}; //得到字典数组中的键值对的个数 NSUInteger count = dict3.count; //简洁取值,如果键不存在取出来的值就是nil NSString *age = dict3[@"age"]; //利用对象方法取值,如果键不存在取出来的值就是nil NSString *name = [dict3 objectForKey:@"name"]; NSLog(@"age = %@ , name = %@",age,name);//age = 18 , name = jack
三、NSDictionary的遍历
使用增强for循环遍历字典数组
NSDictionary *dict3 = @{@"name" : @"jack",@"age" : @"18"}; //使用增强for循环遍历字典数组 for (NSString *key in dict3) { //遍历出来的每一个键,再通过键得到对应的值 NSLog(@"%@ = %@",key,dict3[key]); }
使用block遍历字典数组
NSDictionary *dict3 = @{@"name" : @"jack",@"age" : @"18"}; //使用block遍历字典数组 [dict3 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { //key:键 //obj:值 //stop:控制循环停止 NSLog(@"%@ = %@",key,obj); }];
四、NSMutableDictionary
NSMutableDictionary是NSDictionary的子类,因为是子类所以用法和NSDictionary差不多。唯一区别就是用NSMutableDictionary创建的字典集合中的元素是可以新增、删除的(可变字典集合)。
//创建可变字典对象 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; //错误的创建方法 //NSMutableDictionary *dict1 = @{@"name":@"6ag",@"18":@"age"}; //这种方式是可以的,会覆盖旧数据,一般用于初始化字典数据 //dict.dictionary = @{@"name":@"6ag",@"age":@"18"}; //新增键值对,如果键相同,后面新增的会将前面的覆盖 [dict setObject:@"六阿哥" forKey:@"name"]; [dict setObject:@"6ag" forKey:@"name"]; [dict setObject:@"18" forKey:@"age"]; NSLog(@"%@",dict);//{age = 18; name = 6ag;} //删除对应键值对 [dict removeObjectForKey:@"age"]; NSLog(@"%@",dict);//{name = 6ag;} //删除所有键值对 [dict removeAllObjects];
五、NSFileManager
NSFileManager是Foundation框架提供的一个文件管理类,用来操作磁盘上的文件、目录。这个类的对象是以单例模式(只能有一个对象)创建的。在对文件、目录进行操作之前,建议先判断文件、目录的各种权限再操作。
常见方法之判断
//调用类方法得到这个类的单例对象 NSFileManager *fileManager = [NSFileManager defaultManager]; //将路径存储起来 NSString *path = @"/Users/itcast/Desktop/6ag.txt"; //判断指定文件、目录在磁盘上是否存在 BOOL res = [fileManager fileExistsAtPath:path]; /* 判断指定路径是否真实存在,并且判断是目录还是文件路径 返回值:YES路径存在、NO路径不存在 返回指针:YES路径是目录、NO路径是文件 */ BOOL flag = NO;//是否是目录 BOOL res1 = [fileManager fileExistsAtPath:path isDirectory:&flag]; //判断文件、目录是否有权限读取 BOOL res2 = [fileManager isReadableFileAtPath:path]; //判断文件、目录是否有权限写入 BOOL res3 = [fileManager isWritableFileAtPath:path]; //判断文件、目录是否有权限删除 BOOL res4 = [fileManager isDeletableFileAtPath:path];
常见方法之获取信息
//调用类方法得到这个类的单例对象 NSFileManager *fileManager = [NSFileManager defaultManager]; //将路径存储起来 NSString *path = @"/Users/itcast/Desktop/6ag"; //获取文件、目录的属性 NSDictionary *dict = [fileManager attributesOfItemAtPath:path error:nil]; //获取文件、目录创建的时间。其他信息同理,从字典里取出 NSString *date = dict[NSFileModificationDate]; //获取指定目录下的所有文件和目录(所有内层的文件、目录) NSArray *array = [fileManager subpathsAtPath:path]; //获取指定目录下的当前目录的所有文件、目录(不包括内层文件、目录) NSArray *array1 = [fileManager contentsOfDirectoryAtPath:path error:nil];
常见方法之创建文件、目录
//调用类方法得到这个类的单例对象 NSFileManager *fileManager = [NSFileManager defaultManager]; //将路径存储起来 NSString *FilePath = @"/Users/itcast/Desktop/6ag.txt"; NSString *DirectoryPath = @"/Users/itcast/Desktop/6ag"; /*在指定目录创建文件 第一个参数(NSString *):要创建的文件,给路径和文件名 第二个参数(NSData *):这个文件的内容,要传递这个文件的二进制格式。以NSData对象封装别的格式的数据为二进制数据,nil为空文件 第三个参数(NSDictionary *):指定创建的文件的属性,nil为默认属性 */ NSString *str = @"北京我爱你!"; //将str以UTF8编码的格式转为二进制数据存储在NSData对象 NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; //创建文件存入内容 BOOL res = [fileManager createFileAtPath:FilePath contents:data attributes:nil]; /* 第一个参数:要创建的目录 第二个参数:YES,一路创建,没有就创建。NO,只会在指定目录下创建指定目录 第三个参数:指定创建的目录的属性,nil为默认属性 第四个参数:错误信息,nil为忽略错误信息 */ BOOL res1 = [fileManager createDirectoryAtPath:DirectoryPath withIntermediateDirectories:YES attributes:nil error:nil];
常见方法之拷贝、移动、删除
NSFileManager *fileManager = [NSFileManager defaultManager]; //封装两个路径 NSString *path = @"/Users/itcast/Desktop/6ag.txt"; NSString *path1 = @"/Users/itcast/Desktop/6.txt"; //拷贝文件、目录 BOOL res = [fileManager copyItemAtPath:path toPath:path1 error:nil]; //移动文件、目录,也可用于重命名 BOOL res1 = [fileManager moveItemAtPath:path toPath:path1 error:nil]; //删除文件、目录 BOOL res2 = [fileManager removeItemAtPath:path error:nil];
六、Foundation常用结构体
在Foundation中定义了很多常用结构体类型来简化我们的日常开发,这些结构体和我们自己定义的结构体没有任何区别。常用的结构体有CGPoint(NSPoint)、CGSize(NSSize)、CGRect(NSRect)等,这几个结构体用于保存IOS界面上控件的位置信息(坐标、大小),并且这几个结构体建议使用CG前缀的,因为跨平台。
CGPoint(NSPoint)保存控件坐标(x、y坐标)
//结构体的定义 struct CGPoint { CGFloat x;//x坐标 CGFloat y;//y坐标 }; typedef struct CGPoint CGPoint; //常规创建CGPoint变量方法 CGPoint p1; p1.x = 10; p1.y = 20; CGPoint p2 = {10,20}; CGPoint p3 = {.x = 10,.y = 20}; //使用Foundation框架提供的函数创建 CGPoint p4 = CGPointMake(10, 20); CGPoint p5 = NSMakePoint(10, 20);
CGSize(NSSize)保存控件大小(长、高)
//结构体的定义 struct CGSize { CGFloat width;//长 CGFloat height;//高 }; typedef struct CGSize CGSize; //常规创建CGSize变量方法 CGSize s1; s1.width = 50; s1.height = 30; CGSize s2 = {50,30}; CGSize s3 = {.width = 50,.height = 30}; //使用Foundation框架提供的函数创建 CGSize s4 = CGSizeMake(50, 30); CGSize s5 = NSMakeSize(50, 30);
CGRect(NSRect)保存一个控件的坐标、大小
//结构体的定义 struct CGRect { CGPoint origin; CGSize size; }; typedef struct CGRect CGRect; //常规创建CGRect变量方法 CGRect r1; r1.origin = CGPointMake(10, 20); r1.size = CGSizeMake(50, 30); CGRect r2 = {10,20,50,30}; CGRect r3 = {CGPointMake(10, 20),CGSizeMake(50, 30)}; //使用Foundation框架提供的函数创建 CGRect r4 = CGRectMake(10, 20, 50, 30); CGRect r5 = NSMakeRect(10, 20, 50, 30);
七、NSNumber
NSNumber是Foundation框架中提供的一个包装类,用于将基本数据类型封装为一个对象,然后就可以将这些对象存储到集合中。
//创建可变数组对象 NSMutableArray *arr = [NSMutableArray array]; //封装基本数据类型 NSNumber *num = [NSNumber numberWithInt:100]; //极简封装基本数据类型 NSNumber *num1 = @101; int num2 = 102; //如果是变量需要加小括号 NSNumber *num3 = @(num2); //将包装后的对象存储到数组中 [arr addObject:num]; [arr addObject:num1]; [arr addObject:num3]; //输出数组中的元素 NSLog(@"%@",arr); /* ( 100, 101, 102 ) */
八、NSValue
NSValue是Foundation框架中提供的一个包装类,专门用于包装CGPoint、CGSize、CGRect、NSRange等结构体为对象,然后就可以将这些对象存储到集合中。如果自己的结构体想要存储到集合中,必须要自己写包装类。
//存储坐标 CGPoint p1 = CGPointMake(10, 20); CGPoint p2 = CGPointMake(10, 20); CGPoint p3 = CGPointMake(10, 20); CGPoint p4 = CGPointMake(10, 20); //包装结构体 NSValue *v1 = [NSValue valueWithPoint:p1]; NSValue *v2 = [NSValue valueWithPoint:p2]; NSValue *v3 = [NSValue valueWithPoint:p3]; NSValue *v4 = [NSValue valueWithPoint:p4]; //存储到数组 NSArray *arr = @[v1,v2,v3,v4]; //输出数组中的元素 NSLog(@"%@",arr); /* ( "NSPoint: {10, 20}", "NSPoint: {10, 20}", "NSPoint: {10, 20}", "NSPoint: {10, 20}" ) */
自己写包装类示例
//JFStudentValue.h文件 #import <Foundation/Foundation.h> //定义要包装的结构体类型 typedef struct { char *name; int age; }Person; @interface JFStudentValue : NSObject @property (nonatomic, assign) Person person; //声明快速包装结构体的对象方法和类方法 - (instancetype)initWithPerson:(Person)person; + (instancetype)personWithPerson:(Person)person; @end //JFStudentValue.m文件 #import "JFStudentValue.h" @implementation JFStudentValue //实现快速包装结构体的对象方法和类方法 - (instancetype)initWithPerson:(Person)person { if (self = [super init]) { self.person = person; } return self; } + (instancetype)personWithPerson:(Person)person { return [[self alloc] initWithPerson:person]; } @end //main.m文件 #import <Foundation/Foundation.h> #import "JFStudentValue.h" int main(int argc, const char * argv[]) { //将结构体封装成对象 JFStudentValue *stu1 = [JFStudentValue personWithPerson:(Person){"6ag",18}]; JFStudentValue *stu2 = [JFStudentValue personWithPerson:(Person){"7ag",17}]; JFStudentValue *stu3 = [JFStudentValue personWithPerson:(Person){"8ag",16}]; //将封装的对象存储到数组 NSArray *arr = @[stu1,stu2,stu3]; //输出数组中的元素 NSLog(@"%@",arr); /* ( "<JFStudentValue: 0x100206810>", "<JFStudentValue: 0x100206870>", "<JFStudentValue: 0x100206890>" ) */ return 0; }
九、NSDate
NSDate是Foundation框架提供的一个日期处理类,可以获取时间并以各种形式输出。
//得到系统格林威治时间,0时区的时间 NSDate *date = [NSDate date]; NSLog(@"%@",date);//2015-07-30 08:55:03 +0000 /*格式化输出日期,指定日期输出的格式 默认的格式 年-月-日 时:分:秒 +时区 *///创建NSDateFormatter对象 NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; //定义格式 formatter.dateFormat = @"yyyy年MM月dd日 HH时mm分ss秒"; //将时间转为指定格式存储到字符串 NSString *dateNew = [formatter stringFromDate:date]; NSLog(@"%@",dateNew);//2015年07月30日 16时55分03秒 //将字符串转为日期 NSDate *dateNew1 = [formatter dateFromString:@"2015-07-30 16:45:36"]; NSLog(@"%@",dateNew1);//2015-07-30 08:45:36 +0000 //拿到当前时间(东八区),8 * 60 * 60 是增加的秒 NSDate *dateNew2 = [NSDate dateWithTimeIntervalSinceNow:8*60*60]; NSLog(@"%@",dateNew2);//2015-07-30 16:55:03 +0000
十、集合的内存管理
当我们将一个对象存储到集合之中的时候,会先将这个对象的引用计数器加1。当集合被销毁的时候,就会将集合中存储的对象的引用计数器减1。
绝大多数情况下,Foundation框架中的类,如果是以类名开头的类方法创建出来的,那么这个对象99.999999%的情况下已经被aurorelease过的。特别注意在ARC模式下,数组的指针是一个强类型指针。
十一、copy和mutableCopy
copy和mutableCopy都是定义在NSObject类中的方法,用于拷贝对象。copy常作为@property的参数,当@property定义的类型为NSString类型时,无论是在MRC还是ARC下,都使用copy参数。
浅拷贝:指针拷贝(地址),不会产生新对象,但是原对象的引用计数器加1。
深拷贝:内容拷贝,会产生新对象,旧对象的引用计数器不变,新对象的引用计数器为1。
//copy拷贝不可变字符串对象后结果还是不可变字符串对象,并且不会产生新对象,叫浅拷贝 NSString *str1 = @"jack"; NSString *str2 = [str1 copy];//调用copy方法拷贝对象 NSLog(@"str1 = %p",str1);//str1 = 0x100001030 NSLog(@"str2 = %p",str2);//str2 = 0x100001030 //copy拷贝可变字符串对象后结果是不可变字符串对象,并且会产生新对象,叫深拷贝 NSMutableString *str3 = [NSMutableString stringWithFormat:@"jack"]; NSString *str4 = [str3 copy];//调用copy方法拷贝对象 NSLog(@"str3 = %p",str3);//str3 = 0x100106800 NSLog(@"str4 = %p",str4);//str4 = 0x6b63616a45 //mutableCopy拷贝不可变字符串对象后结果是可变字符串对象,并且会产生新对象,叫深拷贝 NSString *str5 = @"jack"; NSMutableString *str6 = [str1 mutableCopy]; NSLog(@"str5 = %p",str5);//str5 = 0x100001030 NSLog(@"str6 = %p",str6);//str6 = 0x10010e1d0 //mutableCopy拷贝可变字符串对象后结果也是可变字符串对象,也会产生新对象,叫深拷贝 NSMutableString *str7 = [NSMutableString stringWithFormat:@"jack"]; NSMutableString *str8 = [str1 mutableCopy]; NSLog(@"str8 = %p",str7);//str8 = 0x10010e210 NSLog(@"str8 = %p",str8);//str8 = 0x10010e250
copy方法内部调用了另外一个方法copyWithZone,这个方法定义在NScoping协议中。所以一个对象想要有拷贝能力,就必须遵守这个协议并实现方法。
自定义类要实现copy的方法
//Person.h文件 #import <Foundation/Foundation.h> @interface Person : NSObject <NSCopying>//让类遵守NSCoping协议 @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) int age; @end //Person.m文件 #import "Person.h" @implementation Person //实现协议中copyWithZone方法 - (id)copyWithZone:(NSZone *)zone { //在方法中创建一个和当前对象各个属性相同的对象并返回 id p = [[self.class alloc] init]; [p setName:self.name]; [p setAge:self.age]; return p; } @end //main.m文件 #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; p.name = @"6ag"; p.age = 18; Person *pNew = [p copy]; NSLog(@"p = %p pNew = %p",p,pNew); NSLog(@"pNew.name = %@ pNew.age = %d",pNew.name,pNew.age); } return 0; }
十二、单例模式
单例模式顾名思义就是一个类在程序中只能实例化一个对象,例如NSFileManager类。我们实例化一个对象,必须调用alloc方法,而alloc方法是调用了allocWithZone:方法。所以我们只需要重写allocWithZone:方法就能让类实现单例模式。那什么时候使用单例模式呢?当类的对象需要被共享的时候我们就可以使用单例模式让对象具有唯一性。
让自定义的类实现单例模式
//Person.h文件 #import <Foundation/Foundation.h> @interface Person : NSObject + (instancetype)sharedPerson; @end //Person.m文件 #import "Person.h" @implementation Person //重写allocWithZone:方法 + (instancetype)allocWithZone:(struct _NSZone *)zone { //定义一个镜头类型的id指针赋值nil static id instance = nil; //判断这个指针是否为nil if (instance == nil) { //如果为nil就创建对象赋值给id指针 instance = [super allocWithZone:zone]; } //返回id指针的值 return instance; } //类方法创建对象 + (instancetype)sharedPerson { return [[self alloc] init]; } @end //main.m文件 #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p1 = [[Person alloc] init]; Person *p2 = [[Person alloc] init]; Person *p3 = [[Person alloc] init]; //调用单例的规范类方法创建对象 Person *p4 = [Person sharedPerson]; NSLog(@"\np1 = %p\np2 = %p\np3 = %p\np4 = %p",p1,p2,p3,p4); /* p1 = 0x1002067c0 p2 = 0x1002067c0 p3 = 0x1002067c0 p4 = 0x1002067c0 */ } return 0; }