首页 > 代码库 > oc_转_Foundation 框架

oc_转_Foundation 框架

概述

IOS开发中一个重要的框架Foundation,主要内容有:

  1. Foundation概述
  2. 常用结构体
  3. 日期
  4. 字符串
  5. 数组
  6. 字典
  7. 装箱和拆箱
  8. 反射
  9. 拷贝
  10. 文件操作
  11. 归档

Foundation概述

为什么前面说的内容中新建一个类的时候我们都是选择Cocoa Class呢?Cocoa是什么呢?

Cocoa不是一种编程语言(它可以运行多种编程语言),它也不是一个开发工具(通过命令行我们仍然可以开发Cocoa程序),它是创建Mac OS X和IOS程序的原生面向对象API,为这两者应用提供了编程环境。

我们通常称为“Cocoa框架”,事实上Cocoa本身是一个框架的集合,它包含了众多子框架,其中最重要的要数“Foundation”和“UIKit”。前者是框架的基础,和界面无关,其中包含了大量常用的API;后者是基础的UI类库,以后我们在IOS开发中会经常用到。这两个框架在系统中的位置如下图:

技术分享

其实所有的Mac OS X和IOS程序都是由大量的对象构成,而这些对象的根对象都是NSObject,NSObject就处在Foundation框架之中,具体的类结构如下:

技术分享

技术分享

技术分享

通常我们会将他们分为几类:

  1. 值对象 
  2. 集合 
  3. 操作系统服务:文件系统、URL、进程通讯 
  4. 通知 
  5. 归档和序列化 
  6. 表达式和条件判断 
  7. Objective-C语言服务

UIKit主要用于界面构架,这里我们不妨也看一下它的类结构:

技术分享

 

常用结构体

在Foundation中定义了很多常用结构体类型来简化我们的日常开发,这些结构体完全采用Objective-C定义,和我们自己定义的结构体没有任何区别,之所以由框架为我们提供完全是为了简化我们的开发。常用的结构体有NSRange、NSPoint、NSSize、NSRect等

 1 #import <Foundation/Foundation.h> 2  3 /*NSRange表示一个范围*/ 4 void test1(){ 5     NSRange rg={3,5};//第一参数是起始位置第二个参数是长度 6     //NSRange rg; 7     //rg.location=3; 8     //rg.length=5; 9     //NSRange rg={.location=3,.length=5};10     //常用下面的方式定义 NSRange rg2=NSMakeRange(3,5);//使用NSMakeRange定义一个NSRange 11     //打印NSRange可以使用Foundation中方法 NSLog(@"rg2 is %@", NSStringFromRange(rg2));//注意不能直接NSLog(@"rg2 is %@", rg2),因为rg2不是对象(准确的说%@是指针)而是结构体12 }13 /*NSPoint表示一个点*/14 void test2(){15     NSPoint p=NSMakePoint(10, 15);//NSPoint其实就是CGPoint16     //这种方式比较常见 NSPoint p2=CGPointMake(10, 15);17     NSLog(NSStringFromPoint(p2));18 }19 /*NSSize表示大小*/20 void test3(){21     NSSize s=NSMakeSize(10, 15);//NSSize其实就是CGSize22     //这种方式比较常见 CGSize s2=CGSizeMake(10, 15);23     NSLog(NSStringFromSize(s2));24 }25 /*NSRect表示一个矩形*/26 void test4(){27     NSRect r=NSMakeRect(10, 5, 100, 200);//NSRect其实就是CGRect28     //这种方式比较常见 NSRect r2=CGRectMake(10, 5, 100, 200);29     NSLog(NSStringFromRect(r2));30 }31 32 int main(int argc, const char * argv[]) {33     @autoreleasepool {34         test1();35         test2();36         test3();37         test4();38     } return 0;39 }

可以看到对于常用结构体在Foundation框架中都有一个对应的make方法进行创建,这也是我们日后比较常用的操作;而且与之对应的还都有一个NSStringFromXX方法来进行字符串转换,方便我们调试。上面也提到NSSize其实就是CGSize,NSRect其实就是CGRect,我们可以通过查看代码进行确认,例如NSSize定义:

typedef CGSize NSSize;

 继续查看CGSize的代码:

1 /* Sizes. */2 3 struct CGSize{4     CGFloat width;5     CGFloat height;6 };7 typedef struct CGSize CGSize;

 

日期

接下来熟悉一下Foundation框架中日期的操作

 1 #import <Foundation/Foundation.h> 2  3  4 int main(int argc, const char * argv[]) { 5      6     NSDate *date1=[NSDate date];//获得当前日期 7     NSLog(@"%@",date1); //结果:2014-07-16 07:25:28 +0000 8      9     NSDate *date2=[NSDate dateWithTimeIntervalSinceNow:100];//在当前日期的基础上加上100秒,注意在ObjC中多数时间单位都是秒10     NSLog(@"%@",date2); //结果:2014-07-16 07:27:08 +000011     12     NSDate *date3=[NSDate distantFuture];//随机获取一个将来的日期13     NSLog(@"%@",date3); //结果:4001-01-01 00:00:00 +000014     15     NSTimeInterval time=[date2 timeIntervalSinceDate:date1];//日期之差,返回单位为秒16     NSLog(@"%f",time); //结果:100.00883317     18     NSDate *date5=[date1 earlierDate:date3];//返回比较早的日期19     NSLog(@"%@",date5); //结果:2014-07-16 07:25:28 +000020     21     //日期格式化22     NSDateFormatter *formater1=[[NSDateFormatter alloc]init];23     formater1.dateFormat=@"yy-MM-dd HH:mm:ss";24     NSString *datestr1=[formater1 stringFromDate:date1];25     NSLog(@"%@",datestr1); //结果:14-07-16 15:25:2826     //字符串转化为日期27     NSDate *date6=[formater1 dateFromString:@"14-02-14 11:07:16"];28     NSLog(@"%@",date6); //结果:2014-02-14 03:07:16 +000029 30     return 0;31 }

 

字符串

不可变字符串

在ObjC中字符串操作要比在C语言中简单的多,在下面的例子中你将看到字符串的初始化、大小写转化、后缀前缀判断、字符串比较、字符串截取、字符串转换等,通过下面的例子我们基本可以掌握常用的字符串操作(注意这些内容虽然基本,但却是十分常用的操作,需要牢记):

 1 #import <Foundation/Foundation.h> 2  3  4 /**字符串操作*/ 5 void test1(){ 6     char *str1="C string";//这是C语言创建的字符串 7     NSString *str2=@"OC string";//ObjC字符串需要加@,并且这种方式创建的对象不需要自己释放内存 8  9     //下面的创建方法都应该释放内存10     NSString *str3=[[NSString alloc] init];11     str3=@"OC string";12     NSString *str4=[[NSString alloc] initWithString:@"Objective-C string"];13     NSString *str5=[[NSString alloc] initWithFormat:@"age is %i,name is %.2f",19,1.72f];14     NSString *str6=[[NSString alloc] initWithUTF8String:"C string"];//C语言的字符串转换为ObjC字符串15 16     //以上方法都有对应静态方法(一般以string开头),不需要管理内存(系统静态方法一般都是自动释放)17     NSString *str7=[NSString stringWithString:@"Objective-C string"];18 }19 void test2(){20     NSLog(@"\"Hello world!\" to upper is %@",[@"Hello world!" uppercaseString]);21     //结果:"Hello world!" to upper is HELLO WORLD!22     NSLog(@"\"Hello world!\" to lowwer is %@",[@"Hello world!" lowercaseString]);23     //结果:"Hello world!" to lowwer is hello world!24      25     //首字母大写,其他字母小写26     NSLog(@"\"Hello world!\" to capitalize is %@",[@"Hello world!" capitalizedString]);27     //结果:"Hello world!" to capitalize is Hello World!28      29     BOOL result= [@"abc" isEqualToString:@"aBc"];30     NSLog(@"%i",result);31     //结果:032     NSComparisonResult result2= [@"abc" compare:@"aBc"];//如果是[@"abc" caseInsensitiveCompare:@"aBc"]则忽略大小写比较33     if(result2==NSOrderedAscending){34         NSLog(@"left<right.");35     }else if(result2==NSOrderedDescending){36         NSLog(@"left>right.");37     }else if(result2==NSOrderedSame){38         NSLog(@"left=right.");39     }40     //结果:left>right.41 }42 void test3(){43     NSLog(@"has prefix ab? %i",[@"abcdef" hasPrefix:@"ab"]);44     //结果:has prefix ab? 145     NSLog(@"has suffix ab? %i",[@"abcdef" hasSuffix:@"ef"]);46     //结果:has suffix ab? 147     NSRange range=[@"abcdefabcdef" rangeOfString:@"cde"];//注意如果遇到cde则不再往后面搜索,如果从后面搜索或其他搜索方式可以设置第二个options参数48     if(range.location==NSNotFound){49         NSLog(@"not found.");50     }else{51         NSLog(@"range is %@",NSStringFromRange(range));52     }53     //结果:range is {2, 3}54 }55 //字符串分割56 void test4(){57     NSLog(@"%@",[@"abcdef" substringFromIndex:3]);//从第三个索引开始(包括第三个索引对应的字符)截取到最后一位58     //结果:def59     NSLog(@"%@",[@"abcdef" substringToIndex:3]);////从0开始截取到第三个索引(不包括第三个索引对应的字符)60     //结果:abc61     NSLog(@"%@",[@"abcdef" substringWithRange:NSMakeRange(2, 3)]);62     //结果:cde63     NSString *str1=@"12.abcd.3a";64     NSArray *array1=[str1 componentsSeparatedByString:@"."];//字符串分割65     NSLog(@"%@",array1);66      /*结果:67       (68          12,69          abcd,70          3a71       )72       */73  74 }75 //其他操作76 void test5(){77     NSLog(@"%i",[@"12" intValue]);//类型转换78     //结果:1279     NSLog(@"%zi",[@"hello world,世界你好!" length]);//字符串长度注意不是字节数80     //结果:1781     NSLog(@"%c",[@"abc" characterAtIndex:0]);//取出制定位置的字符82     //结果:a83     const char *s=[@"abc" UTF8String];//转换为C语言字符串84     NSLog(@"%s",s);85     //结果:abc86 }87 88 int main(int argc, const char * argv[]) {89     test1();90     test2();91     test3();92     test4();93     test5();94     return 0;95 }

扩展--文件操作

在ObjC中路径、文件读写等操作是利用字符串来完成的,这里通过几个简单的例子来演示(首先在桌面上新建一个test.txt文件,里面存储的内容是”hello world,世界你好!”)

 1 #import <Foundation/Foundation.h> 2  3  4 void test1(){ 5     //读取文件内容 6     NSString *path=@"/Users/kenshincui/Desktop/test.txt"; 7     NSString *str1=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; 8     //注意上面也可以使用gb2312 gbk等,例如kCFStringEncodingGB_18030_2000,但是需要用CFStringConvertEncodingToNSStringEncoding转换 9     NSLog(@"str1 is %@",str1);10     //结果:str1 is hello world,世界你好!11 12     13     14     15     //上面我们看到了读取文件,但并没有处理错误,当然在ObjC中可以@try @catch @finnally但通常我们并不那么做16     //由于我们的test.txt中有中文,所以使用下面的编码读取会报错,下面的代码演示了错误获取的过程17     NSError *error;18     NSString *str2=[NSString stringWithContentsOfFile:path encoding:kCFStringEncodingGB_18030_2000 error:&error];//注意这句话中的error变量是**error,就是指针的指针那就是指针的地址,由于error就是一个指针此处也就是error的地址&error,具体原因见下面补充19     if(error){20         NSLog(@"read error ,the error is %@",error);21     }else{22         NSLog(@"read success,the file content is %@",str2);23     }24     //结果:read error ,the error is Error Domain=NSCocoaErrorDomain Code=261 "The file couldn’t be opened using the specified text encoding." UserInfo=0x100109620 {NSFilePath=/Users/kenshincui/Desktop/test.txt, NSStringEncoding=1586}25 26     27     28     29     //读取文件内容还有一种方式就是利用URl,它除了可以读取本地文件还可以读取网络文件30     //NSURL *url=[NSURL URLWithString:@"file:///Users/kenshincui/Desktop/test.txt"];31     NSURL *url=[NSURL URLWithString:@"http://www.apple.com"];32     NSString *str3=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];33     NSLog(@"str3 is %@",str3);34 }35 void test2(){36     //下面是文件写入37     NSString *path1=@"/Users/kenshincui/Desktop/test2.txt";38     NSError *error1;39     NSString *str11=@"hello world,世界你好!";40     [str11 writeToFile:path1 atomically:YES encoding:NSUTF8StringEncoding error:&error1];//automically代表一次性写入,如果写到中间出错了最后就全部不写入41     if(error1){42         NSLog(@"write fail,the error is %@",[error1 localizedDescription]);//调用localizedDescription是只打印关键错误信息43     }else{44         NSLog(@"write success!");45     }46     //结果:write success!47 }48 //路径操作49 void test3(){50     NSMutableArray *marray=[NSMutableArray array];//可变数组51     [marray addObject:@"Users"];52     [marray addObject:@"KenshinCui"];53     [marray addObject:@"Desktop"];54 55     NSString *path=[NSString pathWithComponents:marray];56     NSLog(@"%@",path);//字符串拼接成路径57     //结果:Users/KenshinCui/Desktop58 59     NSLog(@"%@",[path pathComponents]);//路径分割成数组60     /*结果: 61      (62         Users,63         KenshinCui,64         Desktop65     )66     */67 68     NSLog(@"%i",[path isAbsolutePath]);//是否觉对路径(其实就是看字符串是否以“/”开头)69     //结果:070     NSLog(@"%@",[path lastPathComponent]);//取得最后一个目录71     //结果:Desktop72     NSLog(@"%@",[path stringByDeletingLastPathComponent]);//删除最后一个目录,注意path本身是常量不会被修改,只是返回一个新字符串73     //结果:Users/KenshinCui74     NSLog(@"%@",[path stringByAppendingPathComponent:@"Documents"]);//路径拼接75     //结果:Users/KenshinCui/Desktop/Documents76 }77  //扩展名操作78 void test4(){79     NSString *path=@"Users/KenshinCui/Desktop/test.txt";80     NSLog(@"%@",[path pathExtension]);//取得扩展名,注意ObjC中扩展名不包括"."81     //结果:txt82     NSLog(@"%@",[path stringByDeletingPathExtension]);//删除扩展名,注意包含"."83     //结果:Users/KenshinCui/Desktop/test84     NSLog(@"%@",[@"Users/KenshinCui/Desktop/test" stringByAppendingPathExtension:@"mp3"]);//添加扩展名85     //结果:Users/KenshinCui/Desktop/test.mp386 }87 88 int main(int argc, const char * argv[]) {89     test1();90     test2();91     test3();92     test4();93     return 0;94 }

注意:在上面的例子中我们用到了可变数组,下面会专门介绍。

可变字符串

我们知道在字符串操作过程中我们经常希望改变原来的字符串,当然这在C语言中实现比较复杂,但是ObjC为我们提供了新的可变字符串类NSMutableString,它是NSString的子类。

 1 #import <Foundation/Foundation.h> 2  3  4 int main(int argc, const char * argv[]) { 5      6     /*可变字符串,注意NSMutableString是NSString子类*/ 7     //注意虽然initWithCapacity分配字符串大小,但是不是绝对的不可以超过此范围,声明此变量对性能有好处 8     NSMutableString *str1= [[NSMutableString alloc] initWithCapacity:10]; 9     [str1 setString:@"hello"];//设置字符串10     NSLog(@"%@",str1);11     //结果:hello12 13     [str1 appendString:@",world!"];//追加字符串14     NSLog(@"%@",str1);15     //结果:hello,world!16 17     [str1 appendFormat:@"我的年龄是%i。dear,I love you.",18];18     NSLog(@"%@",str1);19     //结果:hello,world!我的年龄是18。dear,I love you.20     21     //替换字符串22     NSRange range=[str1 rangeOfString:@"dear"];23     [str1 replaceCharactersInRange:range withString:@"Honey"];24     NSLog(@"%@",str1);25     //结果:hello,world!我的年龄是18。Honey,I love you.26     27     //插入字符串28     [str1 insertString:@"My name is Kenshin." atIndex:12];29     NSLog(@"%@",str1);30     //结果:hello,world!My name is Kenshin.我的年龄是18。Honey,I love you.31     32     //删除指定字符串33     [str1 deleteCharactersInRange:[str1 rangeOfString:@"My name is Kenshin."]];//删除指定范围的字符串34     NSLog(@"%@",str1);35     //结果:hello,world!我的年龄是18。Honey,I love you.36     37     return 0;38 }

 

数组

不可变数组

下面将演示常用的数组操作:初始化、数组对象的方法执行、数组元素的遍历、在原有数组基础上产生新数组、数组排序等

  1 #import <Foundation/Foundation.h>  2 #import "Person.h"  3   4   5 void test1(){  6     //NSArray长度不可变所以初始化的时候就赋值,并且最后以nil结尾  7     //此外需要注意NSArray不能存放C语言的基础类型  8     NSObject *obj=[[NSObject alloc]init];  9     //NSArray *array1=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq", nil]; 10     NSArray *array1=[NSArray arrayWithObjects:@"abc",obj,@"cde",@"opq",@25, nil]; 11     NSLog(@"%zi",array1.count);//数组长度,结果:5 12     NSLog(@"%i",[array1 containsObject:@"cde"]);//是否包含某个对象,结果:1 13     NSLog(@"%@",[array1 lastObject]);//最后一个对象,结果:25 14     NSLog(@"%zi",[array1 indexOfObject:@"abc"]);//对象所在的位置:0 15      16     Person *person1=[Person personWithName:@"Kenshin"]; 17     Person *person2=[Person personWithName:@"Kaoru"]; 18     Person *person3=[Person personWithName:@"Rosa"]; 19     NSArray *array2=[[NSArray alloc]initWithObjects:person1,person2,person3, nil]; 20     [array2 makeObjectsPerformSelector:@selector(showMessage:) withObject:@"Hello,world!"];//执行所有元素的showMessage方法,后面的参数最多只能有一个 21     /*结果: 22      My name is Kenshin,the infomation is "Hello,world!". 23      My name is Kaoru,the infomation is "Hello,world!". 24      My name is Rosa,the infomation is "Hello,world!". 25      */ 26 } 27 //数组的遍历 28 void test2(){ 29     NSObject *obj=[[NSObject alloc]init]; 30     NSArray *array=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq",@25, nil]; 31     //方法1 32     for(int i=0,len=array.count;i<len;++i){ 33         NSLog(@"method1:index %i is %@",i,[array objectAtIndex:i]); 34     } 35     /*结果: 36      method1:index 0 is abc 37      method1:index 1 is <NSObject: 0x100106de0> 38      method1:index 2 is cde 39      method1:index 3 is opq 40      method1:index 4 is 25 41      */ 42      43      44     //方法2 45     for(id obj in array){ 46         NSLog(@"method2:index %zi is %@",[array indexOfObject:obj],obj); 47     } 48     /*结果: 49      method2:index 0 is abc 50      method2:index 1 is <NSObject: 0x100602f00> 51      method2:index 2 is cde 52      method2:index 3 is opq 53      method2:index 4 is 25 54      */ 55      56      57     //方法3,利用代码块方法 58     [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 59         NSLog(@"method3:index %zi is %@",idx,obj); 60         if(idx==2){//当idx=2时设置*stop为YES停止遍历 61             *stop=YES; 62         } 63     }]; 64     /*结果: 65      method3:index 0 is abc 66      method3:index 1 is <NSObject: 0x100106de0> 67      method3:index 2 is cde 68      */ 69      70      71     //方法4,利用迭代器 72     //NSEnumerator *enumerator= [array objectEnumerator];//获得一个迭代器 73     NSEnumerator *enumerator=[array reverseObjectEnumerator];//获取一个反向迭代器 74     //NSLog(@"all:%@",[enumerator allObjects]);//获取所有迭代对象,注意调用完此方法迭代器就遍历完了,下面的nextObject就没有值了 75     id obj2=nil; 76     while (obj2=[enumerator nextObject]) { 77         NSLog(@"method4:%@",obj2); 78     } 79     /*结果: 80      method4:25 81      method4:opq 82      method4:cde 83      method4:<NSObject: 0x100106de0> 84      method4:abc 85      */ 86 } 87 //数组派生出新的数组 88 void test3(){ 89     NSArray *array=[NSArray arrayWithObjects:@"1",@"2",@"3", nil]; 90     NSArray *array2=[array arrayByAddingObject:@"4"];//注意此时array并没有变 91     NSLog(@"%@",array2); 92     /*结果: 93      ( 94          1, 95          2, 96          3, 97          4 98      ) 99      */100     101     102     NSLog(@"%@",[array2 arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"5",@"6", nil]]);//追加形成新的数组103     /*结果:104      (105          1,106          2,107          3,108          4,109          5,110          6111      )112      */113     114     115     NSLog(@"%@",[array2 subarrayWithRange:NSMakeRange(1, 3)]);//根据一定范围取得生成一个新的数组116     /*结果:117      (118          2,119          3,120          4121      )122      */123     124     125     NSLog(@"%@",[array componentsJoinedByString:@","]);//数组连接,形成一个字符串126     //结果:1,2,3127     128     //读写文件129     NSString *path=@"/Users/KenshinCui/Desktop/array.xml";130     [array writeToFile:path atomically:YES];131     NSArray *array3=[NSArray arrayWithContentsOfFile:path];132     NSLog(@"%@",array3);133     /*结果:134      (135          1,136          2,137          3138      )139      */140 }141 //数组排序142 void test4(){143     //方法1,使用自带的比较器144     NSArray *array=[NSArray arrayWithObjects:@"3",@"1",@"2", nil];145     NSArray *array2= [array sortedArrayUsingSelector:@selector(compare:)];146     NSLog(@"%@",array2);147     /*结果:148      (149          1,150          2,151          3152      )153      */154     155     156     //方法2,自己定义比较器157     Person *person1=[Person personWithName:@"Kenshin"];158     Person *person2=[Person personWithName:@"Kaoru"];159     Person *person3=[Person personWithName:@"Rosa"];160     NSArray *array3=[NSArray arrayWithObjects:person1,person2,person3, nil];161     NSArray *array4=[array3 sortedArrayUsingSelector:@selector(comparePerson:)];162     NSLog(@"%@",array4);163     /*结果:164      (165          "name=Kaoru",166          "name=Kenshin",167          "name=Rosa"168      )169      */170     171     172     //方法3使用代码块173     NSArray *array5=[array3 sortedArrayUsingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {174         return [obj2.name compare:obj1.name];//降序175     }];176     NSLog(@"%@",array5);177     /*结果:178      (179          "name=Rosa",180          "name=Kenshin",181          "name=Kaoru"182      )183      */184     185     186     //方法4 通过描述器定义排序规则187     Person *person4=[Person personWithName:@"Jack"];188     Person *person5=[Person personWithName:@"Jerry"];189     Person *person6=[Person personWithName:@"Tom"];190     Person *person7=[Person personWithName:@"Terry"];191     NSArray *array6=[NSArray arrayWithObjects:person4,person5,person6,person7, nil];192     //定义一个排序描述193     NSSortDescriptor *personName=[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];194     NSSortDescriptor *accountBalance=[NSSortDescriptor sortDescriptorWithKey:@"account.balance" ascending:YES];195     NSArray *des=[NSArray arrayWithObjects:personName,accountBalance, nil];//先按照person的name排序再按照account的balance排序196     NSArray *array7=[array6 sortedArrayUsingDescriptors:des];197     NSLog(@"%@",array7);198     /*结果:199      (200          "name=Jack",201          "name=Jerry",202          "name=Terry",203          "name=Tom"204      )205      */206 }207 208 int main(int argc, const char * argv[]) {209     test1();210     test2();211     test3();212     test4();213     return 0;214 }

需要注意几点:

  • NSArray中只能存放对象,不能存放基本数据类型,通常我们可以通过在基本数据类型前加@进行转换; 
  • 数组中的元素后面必须加nil以表示数据结束; 
  • makeObjectsPerformSelector执行数组中对象的方法,其参数最多只能有一个; 
  • 上面数组操作中无论是数组的追加、删除、截取都没有改变原来的数组,只是产生了新的数组而已; 
  • 对象的比较除了使用系统自带的方法,我们可以通过自定义比较器的方法来实现;

可变数组

下面看一下可变数组的内容:

  1 #import <Foundation/Foundation.h>  2 #import "Person.h"  3   4   5 void test1(){  6     Person *person1=[Person personWithName:@"Kenshin"];  7     Person *person2=[Person personWithName:@"Kaoru"];  8     Person *person3=[Person personWithName:@"Rosa"];  9     NSMutableArray *array1=[NSMutableArray arrayWithObjects:person1,person2,person3, nil]; 10     NSLog(@"%@",array1); 11     /*结果: 12      ( 13          "name=Kenshin", 14          "name=Kaoru", 15          "name=Rosa" 16      ) 17      */ 18      19     Person *person4=[Person personWithName:@"Jack"];//此时person4的retainCount为1 20     [array1 addObject:person4];//添加一个元素,此时person4的retainCount为2 21     NSLog(@"%@",array1); 22     /*结果: 23      ( 24          "name=Kenshin", 25          "name=Kaoru", 26          "name=Rosa", 27          "name=Jack" 28      ) 29      */ 30      31     [array1 removeObject:person3];//删除一个元素 32     NSLog(@"%@",array1); 33     /*结果: 34      ( 35          "name=Kenshin", 36          "name=Kaoru", 37          "name=Jack" 38      ) 39      */ 40      41     [array1 removeLastObject];//删除最后一个元素,//此时person4的retainCount为1 42     NSLog(@"%@",array1); 43     /*结果: 44      ( 45          "name=Kenshin", 46          "name=Kaoru" 47      ) 48      */ 49      50     [array1 removeAllObjects];//删除所以元素 51      52     //注意当往数组中添加一个元素时会retain因此计数器+1,当从数组中移除一个元素时会release因此计数器-1 53     //当NSMutalbeArray对象release的时候会依次调用每一个对象的release 54 } 55 void test2(){ 56     NSMutableArray *array1=[NSMutableArray arrayWithObjects:@"1",@"3",@"2", nil]; 57     NSLog(@"%@",array1); 58     /*结果: 59      ( 60          1, 61          3, 62          2 63      ) 64      */ 65      66     NSArray *array2= [array1 sortedArrayUsingSelector:@selector(compare:)];//注意这个方法没有修改array1 67     NSLog(@"%@",array1); 68     /*结果: 69      ( 70          1, 71          3, 72          2 73      ) 74      */ 75      76     NSLog(@"%@",array2); 77     /*结果: 78      ( 79          1, 80          2, 81          3 82      ) 83      */ 84     [array1 sortUsingSelector:@selector(compare:)];//这个方法会修改array1 85     NSLog(@"%@",array1); 86     /*结果: 87      ( 88          1, 89          2, 90          3 91      ) 92      */ 93      94 } 95  96 int main(int argc, const char * argv[]) { 97      98     test1(); 99     100     test2();101     102     return 0;103 }
  • 可变数组中的元素后面必须加nil以表示数据结束; 
  • 往一个可变数组中添加一个对象,此时这个对象的引用计数器会加1,当这个对象从可变数组中移除其引用计数器减1。同时当整个数组销毁之后会依次调用每个对象的releaes方法。 
  • 在不可变数组中无论对数组怎么排序,原来的数组顺序都不会改变,但是在可变数组中如果使用sortUsingSelector:排序原来的数组顺序就发生了变化。

 

字典

字典在我们日常开发中也是比较常用的,通过下面的代码我们看一下在ObjC中的字典的常用操作:初始化、遍历、排序

  1 #import <Foundation/Foundation.h>  2   3   4 void test1(){  5     NSDictionary *dic1=[NSDictionary dictionaryWithObject:@"1" forKey:@"a"];  6     NSLog(@"%@",dic1);  7     /*结果:  8      {  9         a = 1; 10      } 11      */ 12      13     //常用的方式 14     NSDictionary *dic2=[NSDictionary dictionaryWithObjectsAndKeys: 15                         @"1",@"a", 16                         @"2",@"b", 17                         @"3",@"c", 18                         nil]; 19     NSLog(@"%@",dic2); 20     /*结果: 21      { 22          a = 1; 23          b = 2; 24          c = 3; 25      } 26      */ 27      28      29     NSDictionary *dic3=[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1",@"2", nil] forKeys:[NSArray arrayWithObjects:@"a",@"b", nil]]; 30     NSLog(@"%@",dic3); 31     /*结果: 32      { 33          a = 1; 34          b = 2; 35      } 36      */ 37      38      39     //更简单的方式 40     NSDictionary *dic4=@{@"1":@"a",@"2":@"b",@"3":@"c"}; 41     NSLog(@"%@",dic4); 42     /*结果: 43      { 44          1 = a; 45          2 = b; 46          3 = c; 47      } 48      */ 49 } 50 void test2(){ 51     NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys: 52                         @"1",@"a", 53                         @"2",@"b", 54                         @"3",@"c", 55                         @"2",@"d", 56                         nil]; 57     NSLog(@"%zi",[dic1 count]); //结果:4 58     NSLog(@"%@",[dic1 valueForKey:@"b"]);//根据键取得值,结果:2 59     NSLog(@"%@",dic1[@"b"]);//还可以这样读取,结果:2 60     NSLog(@"%@,%@",[dic1 allKeys],[dic1 allValues]); 61     /*结果: 62      ( 63          d, 64          b, 65          c, 66          a 67      ),( 68          2, 69          2, 70          3, 71          1 72      ) 73  74      */ 75      76     NSLog(@"%@",[dic1 objectsForKeys:[NSArray arrayWithObjects:@"a",@"e" , nil]notFoundMarker:@"not fount"]);//后面一个参数notFoundMarker是如果找不到对应的key用什么值代替 77     /*结果: 78      ( 79          1, 80          "not fount" 81      ) 82      */ 83 } 84 void test3(){ 85     NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys: 86                         @"1",@"a", 87                         @"2",@"b", 88                         @"3",@"c", 89                         @"2",@"d", 90                         nil]; 91     //遍历1 92     for (id key in dic1) {//注意对于字典for遍历循环的是key 93         NSLog(@"%@=%@",key,[dic1 objectForKey:key]); 94     } 95     /*结果: 96      d=2 97      b=2 98      c=3 99      a=1100      */101     102     //遍历2103     NSEnumerator *enumerator=[dic1 keyEnumerator];//还有值的迭代器[dic1 objectEnumerator]104     id key=nil;105     while (key=[enumerator nextObject]) {106         NSLog(@"%@=%@",key,[dic1 objectForKey:key]);107         108     }109     /*结果:110      d=2111      b=2112      c=3113      a=1114      */115     116     //遍历3117     [dic1 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {118         NSLog(@"%@=%@",key,obj);119     }];120     /*结果:121      d=2122      b=2123      c=3124      a=1125      */126 }127 128 void test4(){129     NSMutableDictionary *dic=[NSMutableDictionary dictionaryWithObjectsAndKeys:@"1",@"a",130                               @"2",@"b",131                               @"3",@"c",132                               @"2",@"d",133                             nil];134     [dic removeObjectForKey:@"b"];135     NSLog(@"%@",dic);136     /*结果:137      {138          a = 1;139          c = 3;140          d = 2;141      }142      */143     144     [dic addEntriesFromDictionary:@{@"e":@"7",@"f":@"6"}];145     NSLog(@"%@",dic);146     /*结果:147      {148          a = 1;149          c = 3;150          d = 2;151          e = 7;152          f = 6;153      }154      */155     156     [dic setValue:@"5" forKey:@"a"];157     NSLog(@"%@",dic);158     /*结果:159      {160          a = 5;161          c = 3;162          d = 2;163          e = 7;164          f = 6;165      }166      */167      168     169     //注意,一个字典的key或value添加到字典中时计数器+1;字典释放时调用key或value的release一次,计数器-1170 }171 172 173 int main(int argc, const char * argv[]) {174     test1();175     test2();176     test3();177     test4();178     return 0;179 }

注意:同数组一样,不管是可变字典还是不可变字典初始化元素后面必须加上nil以表示结束。

 

装箱和拆箱

其实从上面的例子中我们也可以看到,数组和字典中只能存储对象类型,其他基本类型和结构体是没有办法放到数组和字典中的,当然你也是无法给它们发送消息的(也就是说有些NSObject的方法是无法调用的),这个时候通常会用到装箱(boxing)和拆箱(unboxing)。其实各种高级语言基本上都有装箱和拆箱的过程,例如C#中我们将基本数据类型转化为Object就是一个装箱的过程,将这个Object对象转换为基本数据类型的过程就是拆箱,而且在C#中装箱的过程可以自动完成,基本数据类型可以直接赋值给Object对象。但是在ObjC中装箱的过程必须手动实现,ObjC不支持自动装箱。

在ObjC中我们一般将基本数据类型装箱成NSNumber类型(当然它也是NSObject的子类,但是NSNumber不能对结构体装箱),调用其对应的方法进行转换:

+(NSNumber *)numberWithChar:(char)value;+(NSNumber *)numberWithInt:(int)value;+(NSNumber *)numberWithFloat:(float)value;+(NSNumber *)numberWithDouble:(double)value;+(NSNumber *)numberWithBool:(BOOL)value;+(NSNumber *)numberWithInteger:(NSInteger)value;

 

拆箱的过程就更加简单了,可以调用如下方法:

-(char)charValue;-(int)intValue;-(float)floatValue;-(double)doubleValue;-(BOOL)boolValue;

简单看一个例子

 1 #import <Foundation/Foundation.h> 2  3  4 /*可以存放基本类型到数组、字典*/ 5 void test1(){ 6     //包装类NSNumber,可以包装基本类型但是无法包装结构体类型 7     NSNumber *number1=[NSNumber numberWithChar:a];//‘a‘是一个C语言的char类型我们无法放倒NSArray中,但是我们可以通过NSNumber包装 8     NSArray *array1=[NSArray arrayWithObject:number1]; 9     NSLog(@"%@",array1);10     /*结果:11      (12         9713      )14      */15     16     NSNumber *number2= [array1 lastObject];17     NSLog(@"%@",number2);//返回的不是基本类型,结果:9718     19     20     char char1=[number2 charValue];//number转化为char21     NSLog(@"%c",char1); //结果:a22 }23 24 int main(int argc, const char * argv[]) {25     test1();26     return  0;27 }

上面我们看到了基本数据类型的装箱和拆箱过程,那么结构体呢?这个时候我们需要引入另外一个类型NSValue,其实上面的NSNumber就是NSValue的子类,它包装了一些基本数据类型的常用装箱、拆箱方法,当要对结构体进行装箱、拆箱操作我们需要使用NSValue,NSValue可以对任何数据类型进行装箱、拆箱操作。

事实上对于常用的结构体Foundation已经为我们提供好了具体的装箱方法:

+(NSValue *)valueWithPoint:(NSPoint)point;+(NSValue *)valueWithSize:(NSSize)size;+(NSValue *)valueWithRect:(NSRect)rect;

对应的拆箱方法:

-(NSPoint)pointValue;-(NSSize)sizeValue;-(NSRect)rectValue;

例:

 1 #import <Foundation/Foundation.h> 2  3 //NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体 4 void test1(){ 5     CGPoint point1=CGPointMake(10, 20); 6     NSValue *value1=[NSValue valueWithPoint:point1];//对于系统自带类型一般都有直接的方法进行包装 7     NSArray *array1=[NSArray arrayWithObject:value1];//放倒数组中 8     NSLog(@"%@",array1); 9     /*结果:10      (11         "NSPoint: {10, 20}"12      )13      */14     15     NSValue *value2=[array1 lastObject];16     CGPoint point2=[value2 pointValue];//同样对于系统自带的结构体有对应的取值方法(例如本例pointValue)17     NSLog(@"x=%f,y=%f",point2.x,point2.y);//结果:x=10.000000,y=20.00000018 }19 20 21 int main(int argc, const char * argv[]) {22     test1();23     return  0;24 }

那么如果是我们自定义的结构体类型呢,这个时候我们需要使用NSValue如下方法进行装箱:

+(NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

调用下面的方法进行拆箱:

-(void)getValue:(void *)value;

例如:

 1 #import <Foundation/Foundation.h> 2  3 typedef struct { 4     int year; 5     int month; 6     int day; 7 } Date; 8  9 10 //NSNumber是NSValue的子类,而NSValue可以包装任何类型,包括结构体11 void test1(){12     //如果我们自己定义的结构体包装13     Date date={2014,2,28};14     char *type=@encode(Date);15     NSValue *value3=[NSValue value:&date withObjCType:type];//第一参数传递结构体地址,第二个参数传递类型字符串16     NSArray *array2=[NSArray arrayWithObject:value3];17     NSLog(@"%@",array2);18     /*结果:19      (20         "<de070000 02000000 1c000000>"21      )22      */23     24     Date date2;25     [value3 getValue:&date2];//取出对应的结构体,注意没有返回值26     //[value3 objCType]//取出包装内容的类型27     NSLog(@"%i,%i,%i",date2.year,date2.month,date2.day); //结果:2014,2,2828     29 }30 31 32 int main(int argc, const char * argv[]) {33     test1();34     return  0;35 }

扩展1-NSNull

通过前面的介绍大家都知道无论在数组还是在字典中都必须以nil结尾,否则数组或字典无法判断是否这个数组或字典已经结束(与C语言中的字符串比较类似,C语言中定义字符串后面必须加一个”\0”)。但是我们有时候确实想在数据或字典中存储nil值而不是作为结束标记怎么办呢?这个时候需要使用NSNull,这个类是一个单例,只有一个null方法。简单看一下:

 1 #import <Foundation/Foundation.h> 2  3  4  5 int main(int argc, const char * argv[]) { 6      7     NSNull *nl=[NSNull null];//注意这是一个对象,是一个单例,只有一个方法null创建一个对象 8     NSNull *nl2=[NSNull null]; 9     NSLog(@"%i",nl==nl2);//由于是单例所以地址相等,结果:110     11     NSArray *array1=[NSArray arrayWithObjects:@"abc",nl,@123, nil];12     NSLog(@"%@",array1);13     /*结果:14      (15          abc,16          "<null>",17          12318      )19      */20 21     return  0;22 }

扩展2-@符号

我们知道在ObjC中很多关键字前都必须加上@符号,例如@protocol、@property等,当然ObjC中的字符串必须使用@符号,还有就是%@可以表示输出一个对象。其实@符号在新版的ObjC中还有一个作用:装箱。

 1 #import <Foundation/Foundation.h> 2  3 typedef enum { 4     spring, 5     summer, 6     autumn, 7     winter 8 } Season; 9 10 int main(int argc, const char * argv[]) {11     /*装箱*/12     NSNumber *number1=@100;13     NSArray *array1=[NSArray arrayWithObjects:number1,@"abc",@16,@A,@16.7,@YES, nil];14     NSLog(@"%@",array1);15     /*结果:16      (17          100,18          abc,19          16,20          65,21          "16.7"22          123      )24      */25     NSNumber *number2=@(1+2*3);26     NSLog(@"%@",number2); //结果:727     NSNumber *number3=@(autumn);28     NSLog(@"%@",number3); //结果:229     30 31     NSArray *array2=@[@"abc",@16,@A,@16.7,@YES];//使用这种方式最后不用添加nil值了32     NSLog(@"%@",array2[2]); //结果:6533     NSMutableArray *array3=[NSMutableArray arrayWithArray:array2];34     array3[0]=@"def";35     NSLog(@"%@",array3[0]); //结果:def36     37     NSDictionary *dic1=@{@"a":@123,@"b":@c,@"c":@YES};38     NSLog(@"%@",dic1);39     /*结果:40      {41          a = 123;42          b = 99;43          c = 1;44      }45      */46     NSMutableDictionary *dic2=[NSMutableDictionary dictionaryWithDictionary:dic1];47     dic2[@"a"]=@456;48     NSLog(@"%@",dic2[@"a"]);//结果:45649 50     return 0;51 }

 

反射

由于ObjC动态性,在ObjC中实现反射可以说是相当简单,下面代码中演示了常用的反射操作,具体作用也都在代码中进行了注释说明:

Account.h

1 #import <Foundation/Foundation.h>2 3 @interface Account : NSObject4 5 @property (nonatomic,assign) double balance;6 7 @end

Account.m

1 #import "Account.h"2 3 @implementation Account4 5 @end

Person.h

 1 #import <Foundation/Foundation.h> 2 @class Account; 3  4 @interface Person : NSObject 5  6 @property (nonatomic,copy) NSString *name; 7 @property (nonatomic,retain) Account *account; 8  9 -(Person *)initWithName:(NSString *)name;10 11 +(Person *)personWithName:(NSString *)name;12 13 -(void)showMessage:(NSString *)infomation;14 15 //自己实现对象比较方法16 -(NSComparisonResult)comparePerson:(Person *)person;17 @end

Person.m

 1 #import "Person.h" 2  3 @implementation Person 4  5 -(Person *)initWithName:(NSString *)name{ 6     if(self=[super init]){ 7         self.name=name; 8     } 9     return self;10 }11 12 +(Person *)personWithName:(NSString *)name{13     Person *person=[[Person alloc]initWithName:name];14     return person;15 }16 17 -(void)showMessage:(NSString *)infomation{18     NSLog(@"My name is %@,the infomation is \"%@\".",_name,infomation);19 }20 21 //自己实现对象比较方法22 -(NSComparisonResult)comparePerson:(Person *)person{23     return [_name compare:person.name];24 }25 26 -(NSString *)description{27     return [NSString stringWithFormat:@"name=%@",_name];28 }29 30 @end

main.m

 1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3  4  5 int main(int argc, const char * argv[]) { 6     /*常用方法*/ 7     Person *person1=[Person personWithName:@"Kenshin"]; 8     NSLog(@"%i",[person1 isKindOfClass:[NSObject class]]); //判断一个对象是否为某种类型(如果是父类也返回YES),结果:1 9     NSLog(@"%i",[person1 isMemberOfClass:[NSObject class]]); //判断一个对象是否是某个类的实例化对象,结果:010     NSLog(@"%i",[person1 isMemberOfClass:[Person class]]); //结果:111     NSLog(@"%i",[person1 conformsToProtocol:@protocol(NSCopying)]);//是否实现了某个协议,结果:012     NSLog(@"%i",[person1 respondsToSelector:@selector(showMessage:)]);//是否存在某个方法,结果:113     14     [person1 showMessage:@"Hello,world!"];//直接调用一个方法15     [person1 performSelector:@selector(showMessage:) withObject:@"Hello,world!"];16     //动态调用一个方法,注意如果有参数那么参数类型只能为ObjC对象,并且最多只能有两个参数17 18     19     /*反射*/20     //动态生成一个类21     NSString *className=@"Person";22     Class myClass=NSClassFromString(className);//根据类名生成类23     Person *person2=[[myClass alloc]init]; //实例化24     person2.name=@"Kaoru";25     NSLog(@"%@",person2);//结果:name=Kaoru26 27     //类转化为字符串28     NSLog(@"%@,%@",NSStringFromClass(myClass),NSStringFromClass([Person class])); //结果:Person,Person29 30     //调用方法31     NSString *methodName=@"showMessage:";32     SEL mySelector=NSSelectorFromString(methodName);33     Person *person3=[[myClass alloc]init];34     person3.name=@"Rosa";35     [person3 performSelector:mySelector withObject:@"Hello,world!"]; //结果:My name is Rosa,the infomation is "Hello,world!".36 37     //方法转化为字符串38     NSLog(@"%@",NSStringFromSelector(mySelector)); //结果:showMessage:39     40     return 0;41 }

 

拷贝

对象拷贝操作也比较常见,在ObjC中有两种方式的拷贝:copy和mutablecopy,这两中方式都将产生一个新的对象,只是后者产生的是一个可变对象。在ObjC中如果要想实现copy或者mutablecopy操作需要实现NSCopy或者NSMutableCopy协议,拷贝操作产生的新的对象默认引用计数器是1,在非ARC模式下我们应该对这个对象进行内存管理。在熟悉这两种操作之前我们首先需要弄清两个概念:深复制(或深拷贝)和浅复制(或浅拷贝)。

  • 浅复制:在执行复制操作时,对于对象中每一层(对象中包含的对象,例如说属性是某个对象类型)复制都是指针复制(如果从引用计数器角度出发,那么每层对象的引用计数器都会加1)。 
  • 深复制:在执行复制操作时,至少有一个对象的复制是对象内容复制(如果从引用计数器角度出发,那么除了对象内容复制的那个对象的引用计数器不变,其他指针复制的对象其引用计数器都会加1)。

注:

指针拷贝:拷贝的是指针本身(也就是具体对象的地址)而不是指向的对象内容本身。

对象复制:对象复制指的是复制内容是对象本身而不是对象的地址。

完全复制:上面说了深复制和浅复制,既然深复制是至少一个对象复制是对象内容复制,那么如果所有复制都是对象内容复制那么这个复制就叫完全复制。

对比copy和mutablecopy其实前面我们一直还用到一个操作是retain,它们之间的关系如下:

retain:始终采取浅复制,引用计数器会加1,返回的对象和被复制对象是同一个对象1(也就是说这个对象的引用多了一个,或者说是指向这个对象的指针多了一个);

copy:对于不可变对象copy采用的是浅复制,引用计数器加1(其实这是编译器进行了优化,既然原来的对象不可变,复制之后的对象也不可变那么就没有必要再重新创建一个对象了);对于可变对象copy采用的是深复制,引用计数器不变(原来的对象是可变,现在要产生一个不可变的当然得重新产生一个对象);

mutablecopy:无论是可变对象还是不可变对象采取的都是深复制,引用计数器不变(如果从一个不可变对象产生一个可变对象自然不用说两个对象绝对不一样肯定是深复制;如果从一个可变对象产生出另一个可变对象,那么当其中一个对象改变自然不希望另一个对象改变,当然也是深复制)。

注:

可变对象:当值发生了改变,那么地址也随之发生改变;

不可变对象:当值发生了改变,内容首地址不发生变化;

引用计数器:用于计算一个对象有几个指针在引用(有几个指针变量指向同一个内存地址);

 

 1 #import <Foundation/Foundation.h> 2  3  4 void test1(){ 5     NSString *name=@"Kenshin"; 6     NSString *str1=[NSString stringWithFormat:@"I‘m %@.",name];//注意此时str1的计数器是1 7     NSLog(@"%lu",[str1 retainCount]); //结果:1 8      9     10     NSMutableString *str2=[str1 mutableCopy];//注意此时str2的计数器为1,str1的计数器还是111     //NSMutableString *str5 =CFRetain((__bridge CFTypeRef)str2);12     NSLog(@"retainCount(str1)=%lu,retainCount(str2)=%lu",[str1 retainCount],[str2 retainCount]);13     //结果:retainCount(str1)=1,retainCount(str2)=114     15     16     [str2 appendString:@"def"];//改变str2,str1不变17     NSLog(@"%zi",str1==str2);//二者不是向同一个对象,结果:018     NSLog(@"str1=%@",str1); //结果:str1=I‘m Kenshin.19     NSLog(@"str2=%@",str2); //结果:str2=I‘m Kenshin.def20     21     22     NSLog(@"str1‘s %lu",[str1 retainCount]);23     NSString *str3=[str1 copy];//str3不是产生的新对象而是复制了对象指针,但是str1的计数器+1(当然既然str3同样指向同一个对象,那么如果计算str3指向的对象引用计数器肯定等于str1的对象引用计数器)24     NSLog(@"%zi",str1==str3);//二者相等指向同一个对象,结果:125     NSLog(@"retainCount(str1)=%lu,retainCount(str3)=%lu",str1.retainCount,str3.retainCount);26     //结果:retainCount(str1)=2,retainCount(str3)=227     28     //需要注意的是使用copy和mutableCopy是深复制还是浅复制不是绝对,关键看由什么对象产生什么样的对象29     NSString *str4=[str2 copy];//由NSMutableString产生了NSString,二者类型都不同肯定是深拷贝,此时str2的计数器还是1,str4的计数器也是130     [str2 appendString:@"g"];//改变原对象不影响str431     NSLog(@"%zi",str2==str4); //结果:032     NSLog(@"str2=%@",str2); //结果:str2=I‘m Kenshin.defg33     NSLog(@"str4=%@",str4); //结果:str4=I‘m Kenshin.def34 35     36     [str1 release];37     str1=nil;38     [str3 release];//其实这里也可以调用str1再次release,因为他们两个指向的是同一个对象(但是一般不建议那么做,不容易理解)39     str3=nil;40     41     [str2 release];42     str2=nil;43     [str4 release];44     str4=nil;45     46     //上面只有一种情况是浅拷贝:不可变对象调用copy方法47     48 }49 50 int main(int argc,char *argv[]){51     test1();52     return 0;53 }

为了方便大家理解上面的代码,这里以图形画出str1、str2、str3、str4在内存中的存储情况: 

技术分享

从上面可以清楚的看到str1和str3同时指向同一个对象,因此这个对象的引用计数器是2(可以看到两箭头指向那个对象),str2和str4都是两个新的对象;另外ObjC引入对象拷贝是为了改变一个对象不影响另一个对象,但是我们知道NSString本身就不能改变那么即使我重新复制一个对象也没有任何意义,因此为了性能着想如果通过copy方法产生一个NSString时ObjC不会再复制一个对象而是将新变量指向同一个对象。 

注意:网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得retainCount都是不准确的,特别是在对象拷贝操作之后你会发现二者取值也是不同的,因此如果大家要查看retainCount最好还是暂时关闭ARC。

 

要想支持copy或者mutablecopy操作那么对象必须实现NSCoping协议并实现-(id)copyWithZone:(NSZone*)zone方法,在Foundation中常用的可复制对象有:NSNumber、NSString、NSMutableString、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary。下面看一下如何让自定义的类支持copy操作:

Person.h

 1 #import <Foundation/Foundation.h> 2 @class Account; 3  4 @interface Person : NSObject 5  6 @property  NSMutableString *name; 7 @property (nonatomic,assign) int age; 8  9 10 @end

Person.m

 1 #import "Person.h" 2  3 @implementation Person 4  5  6 -(NSString *)description{ 7     return [NSString stringWithFormat:@"name=%@,age=%i",_name,_age]; 8 } 9 10 //实现copy方法11 -(id)copyWithZone:(NSZone *)zone{12     //注意zone是系统已经分配好的用于存储当前对象的内存13     //注意下面创建对象最好不要用[[Person allocWithZone:zone]init],因为子类如果没有实现该方法copy时会调用父类的copy方法,此时需要使用子类对象初始化如果此时用self就可以表示子类对象,还有就是如果子类调用了父类的这个方法进行重写copy也需要调用子类对象而不是父类Person14     Person *person1=[[[self class] allocWithZone:zone]init];15     person1.name=_name;16     person1.age=_age;17     return person1;18 }19 20 @end

main.m

 1 #import <Foundation/Foundation.h> 2 #import "Account.h" 3 #import "Person.h" 4  5 void test1(){ 6     Person *person1=[[Person alloc]init]; 7     NSMutableString *str1=[NSMutableString stringWithString:@"Kenshin"]; 8     person1.name=str1; 9     //由于name定义的时候使用属性参数采用的是copy策略,而根据前面的知识我们知道NSMutableString的copy策略采用的是对象内容复制,因此如果修改str1不会改变person1.name10     [str1 appendString:@" Cui"];11     NSLog(@"%@",str1);//结果:Kenshin Cui12     NSLog(@"%@",person1.name); //结果:Kenshin13     14 }15 16 void test2(){17     Person *person1=[[Person alloc]init];18     person1.name=[NSMutableString stringWithString:@"Kenshin"];19     person1.age=28;20     Person *person2=[person1 copy];21     NSLog(@"%@",person1); //结果:name=Kenshin,age=022     NSLog(@"%@",person2); //结果:name=Kenshin,age=023     24     [person2.name appendString:@" Cui"];25     26     NSLog(@"%@",person1);//结果:name=Kenshin Cui,age=2827     NSLog(@"%@",person2);//结果:name=Kenshin Cui,age=2828 }29 30 int main(int argc,char *argv[]){31     test1();32     test2();33     return 0;34 }

在上面的代码中重点说一下test2这个方法,在test2方法中我们发现当修改了person2.name属性之后person1.name也改变了,这是为什么呢?我们可以看到在Person.m中自定义实现了copy方法,同时实现了一个浅拷贝。之所以说是浅拷贝主要是因为我们的name属性参数是直接赋值完成的,同时由于name属性定义时采用的是assign参数(默认为assign),所以当通过copy创建了person2之后其实person2对象的name属性和person1指向同一个NSMutableString对象。通过图形表示如下:

技术分享

上面test2的写法纯属为了让大家了解复制的原理和本质,实际开发中我们很少会遇到这种情况,首先我们一般定义name的话可能用的是NSString类型,根本也不能修改;其次我们定义字符串类型的话一般使用(copy)参数,同样可以避免这个问题(因为NSMutableString的copy是深复制)。那么如果我们非要使用NSMutabeString同时不使用属性的copy参数如何解决这个问题呢?答案就是使用深复制,将-(id)copyWithZone:(NSZone *)zone方法中person1.name=_name改为,person1.name=[_name copy];或person1.name=[_name mutablecopy]即可,这样做也正好满足我们上面对于深复制的定义。

补充-NSString的引用计数器

在好多语言中字符串都是一个特殊的对象,在ObjC中也不例外。NSString作为一个对象类型存储在堆中,多数情况下它跟一般的对象类型没有区别,但是这里我们需求强调一点那就是字符串的引用计数器。

 1 #import <Foundation/Foundation.h> 2  3  4 int main(int argc,char *argv[]){ 5      6     NSString *str1=@"Kenshin"; 7     NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //结果:-1 8     [str1 retain]; 9     NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //结果:-110     11     NSString *str2=[NSString stringWithString:@"Kaoru"];12     NSLog(@"retainCount(str2)=%i",str2.retainCount); //结果:-113     [str1 retain];14     NSLog(@"retainCount(str2)=%i",str2.retainCount); //结果:-115     NSString *str2_1=[NSString stringWithString:[NSString stringWithFormat:@"Kaoru %@",@"sun"]];16     NSLog(@"retainCount(str2_1)=%i",str2_1.retainCount);17     [str2_1 release];18     [str2_1 release];19     20     21     22     NSString *str3=[NSString stringWithFormat:@"Rosa %@",@"Sun"];23     NSLog(@"retainCount(str3)=%i",str3.retainCount); //结果:124     [str3 retain];25     NSLog(@"retainCount(str3)=%i",str3.retainCount); //结果:226     [str3 release];27     [str3 release];28     29     NSString *str4=[NSString stringWithUTF8String:"Jack"];30     NSLog(@"retainCount(str4)=%i",str4.retainCount); //结果:131     [str4 retain];32     NSLog(@"retainCount(str4)=%i",str4.retainCount); //结果:233     [str4 release];34     [str4 release];35     36     NSString *str5=[NSString stringWithCString:"Tom" encoding:NSUTF8StringEncoding];37     NSLog(@"retainCount(str5)=%i",str5.retainCount); //结果:138     [str5 retain];39     NSLog(@"retainCount(str5)=%i",str5.retainCount); //结果:240     [str5 release];41     [str5 release];42     43     44     45     NSMutableString *str6=@"Jerry";46     NSLog(@"retainCount(str6)=%i",str6.retainCount); //结果:-147     [str6 retain];48     NSLog(@"retainCount(str6)=%i",str6.retainCount); //结果:-149     [str6 release];50     [str6 release];51     52     NSMutableArray *str7=[NSMutableString stringWithString:@"Lily"];53     NSLog(@"retainCount(str7)=%i",str7.retainCount); //结果:154     [str7 retain];55     NSLog(@"retainCount(str7)=%i",str7.retainCount); //结果:256     [str7 release];57     [str7 release];58 59     60     return 0;61 }

看完上面的例子如果不了解NSString的处理你也许会有点奇怪?请看下面的解释

  • str1是一个字符串常量,它存储在常量区,系统不会对它进行引用计数,因此无论是初始化还是做retain操作其引用计数器均为-1; 
  • str3、str4、str5创建的对象同一般对象类似,存储在堆中,系统会对其进行引用计数; 
  • 采用stringWithString定义的变量有些特殊,当后面的字符串是字符串常量,则它本身就作为字符串常用量存储(str2),类似于str1;如果后面的参数是通过类似于str3、str4、str5的定义,那么它本身就是一个普通对象,只是后面的对象引用计数器默认为1,当给它赋值时会做一次拷贝操作(浅拷贝),引用计数器加1,所有str2_1引用计数器为2; 
  • str6其实和str1类似,虽然定义的是可变数组,但是它的本质还是字符串常量,事实上对于可变字符串只有为字符串常量时引用计数器才为-1,其他情况它的引用计数器跟一般对象完全一致;

文件操作

在今天的最后一节内容中让我们看一下Foundation中文件操作,下面将以一个例子进行说明:

  1 #import <Foundation/Foundation.h>  2   3 /*目录操作*/  4 void test1(){  5     //文件管理器是专门用于文件管理的类  6     NSFileManager *manager=[NSFileManager defaultManager];  7       8     //获得当前程序所在目录(当然可以改变)  9     NSString *currentPath=[manager currentDirectoryPath]; 10     NSLog(@"current path is :%@",currentPath); 11     //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug 12      13     //创建目录 14     NSString *myPath=@"/Users/kenshincui/Desktop/myDocument"; 15     BOOL result= [manager createDirectoryAtPath:myPath withIntermediateDirectories:YES attributes:nil error:nil]; 16     if(result==NO){ 17         NSLog(@"Couldn‘t create directory!"); 18     } 19      20     //目录重命名,如果需要删除目录只要调用removeItemAtPath:<#(NSString *)#> error:<#(NSError **)#> 21     NSError *error; 22     NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument"; 23     if([manager moveItemAtPath:myPath toPath:newPath error:&error]==NO){ 24         NSLog(@"Rename directory failed!Error infomation is:%@",error); 25     } 26      27     //改变当前目录 28     if([manager changeCurrentDirectoryPath:newPath]==NO){ 29         NSLog(@"Change current directory failed!"); 30     } 31     NSLog(@"current path is :%@",[manager currentDirectoryPath]); 32     //结果:current path is :/Users/kenshincui/Desktop/myNewDocument 33      34     //遍历整个目录 35     NSString *path; 36     NSDirectoryEnumerator *directoryEnumerator= [manager enumeratorAtPath:newPath]; 37     while (path=[directoryEnumerator nextObject]) { 38         NSLog(@"%@",path); 39     } 40     /*结果: 41      documents 42      est.txt 43     */ 44      45     //或者这样遍历 46     NSArray *paths= [manager contentsOfDirectoryAtPath:newPath error:nil]; 47     NSObject *p; 48     for (p in paths) { 49         NSLog(@"%@",p); 50     } 51     /*结果: 52      documents 53      est.txt 54      */ 55 } 56  57 /*文件操作*/ 58 void test2(){ 59     NSFileManager *manager=[NSFileManager defaultManager]; 60     NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test.txt"; 61     NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt"; 62     NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt"; 63      64     //判断文件是否存在,这个方法也可以判断目录是否存在,这要后面的参数设置位YES 65     if ([manager fileExistsAtPath:filePath isDirectory:NO]) { 66         NSLog(@"File exists!"); 67     } 68      69     //文件是否可读 70     if([manager isReadableFileAtPath:filePath]){ 71         NSLog(@"File is readable!"); 72     } 73      74     //判断两个文件内容是否相等 75     if ([manager contentsEqualAtPath:filePath andPath:filePath2]) { 76         NSLog(@"file1 equals file2"); 77     } 78      79     //文件重命名,方法类似于目录重命名 80     if (![manager moveItemAtPath:filePath toPath:newPath error:nil]) { 81         NSLog(@"Rename file1 failed!"); 82     } 83      84     //文件拷贝 85     NSString *filePath3=@"/Users/kenshincui/Desktop/test3.txt"; 86     if(![manager copyItemAtPath:newPath toPath:filePath3 error:nil]){ 87         NSLog(@"Copy failed!"); 88     } 89      90     //读取文件属性 91     NSDictionary *attributes; 92     if ((attributes=[manager attributesOfItemAtPath:newPath error:nil])==nil) { 93         NSLog(@"Read attributes failed!"); 94     } 95     for (NSObject *key in attributes) { 96         NSLog(@"%@=%@",key,attributes[key]); 97     } 98     /*结果: 99          NSFileOwnerAccountID=501100          NSFileHFSTypeCode=0101          NSFileSystemFileNumber=1781953102          NSFileExtensionHidden=0103          NSFileSystemNumber=16777218104          NSFileSize=27105          NSFileGroupOwnerAccountID=20106          NSFileOwnerAccountName=kenshincui107          NSFileCreationDate=2014-07-28 11:47:58 +0000108          NSFilePosixPermissions=420109          NSFileHFSCreatorCode=0110          NSFileType=NSFileTypeRegular111          NSFileExtendedAttributes={112          "com.apple.TextEncoding" = <7574662d 383b3133 34323137 393834>;113          }114          NSFileGroupOwnerAccountName=staff115          NSFileReferenceCount=1116          NSFileModificationDate=2014-07-28 11:47:58 +0000117      */118     119     //删除文件120     [manager removeItemAtPath:newPath error:nil];121     122 }123 //文件操作--文件内容操作(NSData,非结构化字节流对象,有缓冲区管理机制,可用于网络传输)124 void test3(){125     NSFileManager *manager=[NSFileManager defaultManager];126     NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";127     NSData *data=http://www.mamicode.com/[manager contentsAtPath:filePath];128     NSLog(@"%@",data);//存储的时二进制字节流129     //结果:<68656c6c 6f20776f 726c642c e4b896e7 958ce4bd a0e5a5bd efbc81>130     131     //NSData转化成字符串132     NSString *str1=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];133     NSLog(@"%@",str1);134     //结果:hello world,世界你好!135     136     //字符串转化成NSData137     NSString *str2=@"Kenshin";138     NSData *data2=[str2 dataUsingEncoding:NSUTF8StringEncoding];139     NSLog(@"%@",data2);140     141     //当然一般如果仅仅是简单读取文件内容,直接用户NSString方法即可142     NSString *content=[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];143     NSLog(@"%@",content);144     //结果:hello world,世界你好!145     146 }147 //文件操作--细粒度控制文件,文件操作柄148 void test4(){149     NSFileManager *manager=[NSFileManager defaultManager];150     NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";151     152     //以只读方式打开文件153     NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:filePath];//注意这个方法返回类型为instancetype,也就是说对于上面的NSFileHandle它的返回类型也是NSFileHandle154     NSData *data= http://www.mamicode.com/[fileHandle readDataToEndOfFile];//完整读取文件155     NSString *newPath=@"/Users/kenshincui/Desktop/test4.txt";156     [manager createFileAtPath:newPath contents:nil attributes:nil];157     NSFileHandle *fileHandle2=[NSFileHandle fileHandleForWritingAtPath:newPath];//以可写方式打开文件158     [fileHandle2 writeData:data];//写入文件内容159     160     [fileHandle2 closeFile];//关闭文件161 162     163     //定位到指定位置,默认在文件开头164     [fileHandle seekToFileOffset:12];165     NSData *data2= [fileHandle readDataToEndOfFile];166     NSLog(@"data2=%@",[[NSString alloc]initWithData:data2 encoding:NSUTF8StringEncoding]);167     //结果:data2=世界你好!168     169     [fileHandle seekToFileOffset:6];170     NSData *data3=[fileHandle readDataOfLength:5];171     NSLog(@"data3=%@",[[NSString alloc]initWithData:data3 encoding:NSUTF8StringEncoding]);172     //结果:data3=world173     174     [fileHandle closeFile];175     176 }177 178 //文件路径179 void test5(){180     NSString *filePath=@"/Users/kenshincui/Desktop/myDocument";181     NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt";182 183     //临时文件所在目录184     NSString *path=NSTemporaryDirectory();185     NSLog(@"temporary directory is :%@",path);186     //结果:/var/folders/h6/lss6gncs509c2pgzgty3wd_40000gn/T/187 188     NSString *lastComponent= [filePath lastPathComponent];189     NSLog(@"%@",lastComponent); //结果:myDocument190     191     NSLog(@"%@",[filePath stringByDeletingLastPathComponent]);192     //结果:/Users/kenshincui/Desktop193     NSLog(@"%@",[filePath stringByAppendingPathComponent:@"Pictrues"]);194     //结果:/Users/kenshincui/Desktop/myDocument/Pictrues195     NSLog(@"%@",[filePath2 pathExtension]);196     //结果:txt197     198     [[filePath pathComponents] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {199         NSLog(@"%i=%@",idx,obj);200     }];201     /*结果:202      0=/203      1=Users204      2=kenshincui205      3=Desktop206      4=myDocument207      */208     209     210 }211 212 //文件操作--NSURL213 void test6(){214     NSURL *url=[NSURL URLWithString:@"http://developer.apple.com"];215     NSString *str1=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];216     NSLog(@"%@",str1);217 }218 219 //文件操作--NSBundle,程序包,一般用于获取Resource中的资源(当然由于当前补充IOS应用没有程序包,只是表示当前程序运行路径)220 //在ios中经常用于读取应用程序中的资源文件,如图片、声音、视频等221 void test7(){222     //在程序包所在目录创建一个文件223     NSFileManager *manager=[NSFileManager defaultManager];224     NSString *currentPath=[manager currentDirectoryPath];225     NSLog(@"current path is :%@",currentPath);226     //结果:current path is :/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug227     NSString *filePath=[currentPath stringByAppendingPathComponent:@"test.txt"];228     [manager createFileAtPath:filePath contents:[@"Hello,world!" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];229     230     231     //利用NSBundle在程序包所在目录查找对应的文件232     NSBundle *bundle=[NSBundle mainBundle];//主要操作程序包所在目录233     //如果有test.txt则返回路径,否则返回nil234     NSString *path=[bundle pathForResource:@"test" ofType:@"txt"];//也可以写成:[bundle pathForResource:@"instructions.txt" ofType:nil];235     NSLog(@"%@",path);236     //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/test.txt237     NSLog(@"%@",[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]);238     //结果:Hello,world!239     240     //假设我们在程序运行创建一个Resources目录,并且其中新建pic.jpg,那么用下面的方法获得这个文件完整路径241     NSString *path1= [bundle pathForResource:@"pic" ofType:@"jpg" inDirectory:@"Resources"];242     NSLog(@"%@",path1);243     //结果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/Resources/pic.jpg244 }245 246 int main(int argc,char *argv[]){247 248     test1();249     test2();250     test3();251     test4();252     test5();253     test6();254     test7();255     256     return 0;257 }

归档

归档,在其他语言中又叫“序列化”,就是将对象保存到硬盘;解档,在其他语言又叫“反序列化”就是将硬盘文件还原成对象。其实归档就是数据存储的过程,在IOS中数据的存储有五种方式:

  1. xml属性列表(plist归档)

  2. NSUserDefaults(偏好设置)

  3. NSKeyedArchiver归档(加密形式)

  4. SQLite3(嵌入式数据库)

  5. Core Data(面向对象方式的嵌入式数据库)

当然关于2、4、5点不是我们今天介绍的重点,这个在IOS开发过程中我们会重点说到。

xml属性列表

首先我们先来看一下xml属性列表,xml属性列表进行归档的方式是将对象存储在一个plist文件中,这个操作起来比较简单,其实相当于xml序列化。但是同时它也有缺点:一是这种方式是明文保存的;二是这种方式操作的对象有限,只有NSArray、NSMutableArray、NSDictionary、NSMutableDictionary支持(归档时只要调用对应的writeToFile方法即可,解档调用arrayWithContentsOfFile或dictionaryWithContentsOfFile,注意像NSString、NSNumber、NSData即使有这个方法它存储的也不是xml格式)。

 1 #import <Foundation/Foundation.h> 2  3 //xml属性 4 void test1(){ 5     //数组 6     NSString *path=@"/Users/kenshincui/Desktop/arrayXml.plist"; 7     NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"]; 8     [array1 writeToFile:path atomically:YES]; 9     10     NSArray *array2=[NSArray arrayWithContentsOfFile:path];11     [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {12         NSLog(@"array2[%lu]=%@",idx,obj);13     }];14     /*结果:15      array1[0]=Kenshin16      array1[1]=Kaoru17      array1[2]=Rosa18      */19     20     21     //字典22     NSString *path2=@"/Users/kenshincui/Desktop/dicXml.plist";23     NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5};24     [dic1 writeToFile:path2 atomically:YES];25     26     NSDictionary *dic2=[NSDictionary dictionaryWithContentsOfFile:path2];27     [dic2 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {28         NSLog(@"dic2[%@]=%@",key,obj);29     }];30     /*结果:31      dic2[height]=172.532      dic2[age]=2833      dic2[name]=Kenshin34      */35 }36 37 int main(int argc,char *argv[]){38     39     test1();40     41     return 0;42 }

生成的文件如下

技术分享

技术分享

NSKeyedArchiver归档

如果要针对更多对象归档或者需要归档时能够加密的话就需要使用NSKeyedArchiver进行归档和解档,使用这种方式归档的范围更广而且归档内容是密文存储。从归档范围来讲NSKeyedArchiver适合所有ObjC对象,但是对于自定义对象我们需要实现NSCoding协议;从归档方式来讲NSKeyedArchiver分为简单归档和复杂对象归档,简单归档就是针对单个对象可以直接将对象作为根对象(不用设置key),复杂对象就是针对多个对象,存储时不同对象需要设置不同的Key。

首先看一下系统对象两种归档方式(简单归档和复杂归档)

  1 #import <Foundation/Foundation.h>  2   3 //系统对象简单归档  4 void test1(){  5     //NSString归档  6     NSString *str1=@"Hello,world!";  7     NSString *path1=@"/Users/kenshincui/Desktop/archiver1.arc";  8     if(![NSArchiver archiveRootObject:str1 toFile:path1]){  9         NSLog(@"archiver failed!"); 10     } 11     //NSString解档 12     NSString *str2= [NSUnarchiver unarchiveObjectWithFile:path1]; 13     NSLog(@"str2=%@",str2);//结果:str2=Hello,world! 14      15      16     //NSArray归档 17     NSString *path2=@"/Users/kenshincui/Desktop/archiver2.arc"; 18     NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"]; 19     if(![NSArchiver archiveRootObject:array1 toFile:path2]){ 20         NSLog(@"archiver failed!"); 21     } 22     //NSArray解档 23     NSArray *array2=[NSUnarchiver unarchiveObjectWithFile:path2]; 24     [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 25         NSLog(@"array2[%lu]=%@",idx,obj); 26     }]; 27     /*结果: 28      array2[0]=Kenshin 29      array2[1]=Kaoru 30      array2[2]=Rosa 31      */ 32 } 33  34 //系统复杂对象归档(多对象归档) 35 void test2(){ 36     /*归档*/ 37     NSString *path1=@"/Users/kenshincui/Desktop/archiver3.arc"; 38      39     int int1=89; 40     CGSize size1={12.5,16.8}; 41     NSNumber *number1=@60.5; 42     NSString *str1=@"Hello,world!"; 43     NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"]; 44     NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5}; 45      46     //同时对多个对象进行归档 47     NSMutableData *data1=[[NSMutableData alloc]init];//定义一个NSMutableData用于临时存放数据 48     NSKeyedArchiver *archiver=[[NSKeyedArchiver alloc]initForWritingWithMutableData:data1];//定义归档对象 49     [archiver encodeInt:int1 forKey:@"int"];//对int1归档并指定一个key以便以后读取 50     [archiver encodeSize:size1 forKey:@"size"]; 51     [archiver encodeObject:number1 forKey:@"number"]; 52     [archiver encodeObject:str1 forKey:@"string"]; 53     [archiver encodeObject:array1 forKey:@"array"]; 54     [archiver encodeObject:dic1 forKey:@"dic"]; 55  56     [archiver finishEncoding];//结束归档 57      58     [data1 writeToFile:path1 atomically:YES];//写入文件 59      60      61      62     /*解档*/ 63     int int2; 64     CGSize size2; 65     NSNumber *number2; 66     NSString *str2; 67     NSArray *array2; 68     NSDictionary *dic2; 69      70     NSData *data2=[[NSData alloc]initWithContentsOfFile:path1];//读出数据到NSData 71     NSKeyedUnarchiver *unarchiver=[[NSKeyedUnarchiver alloc]initForReadingWithData:data2]; 72      73     int2= [unarchiver decodeInt64ForKey:@"int"]; 74     size2=[unarchiver decodeSizeForKey:@"size"]; 75     number2=[unarchiver decodeObjectForKey:@"number"]; 76     str2=[unarchiver decodeObjectForKey:@"string"]; 77     array2=[unarchiver decodeObjectForKey:@"array"]; 78     dic2=[unarchiver decodeObjectForKey:@"dic"]; 79      80     [unarchiver finishDecoding]; 81      82     NSLog(@"int2=%i,size=%@,number2=%@,str2=%@,array2=%@,dic2=%@",int2,NSStringFromSize(size2),number2,str2,array2,dic2); 83     /*结果: 84      int2=89, 85      size={12.5, 16.800000000000001}, 86      number2=60.5, 87      str2=Hello,world!, 88      array2=( 89          Kenshin, 90          Kaoru, 91          Rosa 92      ), 93      dic2={ 94          age = 28; 95          height = "172.5"; 96          name = Kenshin; 97      } 98      */ 99 }100 101 102 int main(int argc,char *argv[]){103 104     test1();105     test2();106     107     return 0;108 }

接下来看一下自定义的对象如何归档,上面说了如果要对自定义对象进行归档那么这个对象必须实现NSCoding协议,在这个协议中有两个方法都必须实现:

-(void)encodeWithCoder:(NSCoder *)aCoder; //通过给定的Archiver对消息接收者进行编码;-(id)initWithCoder:(NSCoder *)aDecoder; //从一个给定的Unarchiver的数据返回一个初始化对象;

这两个方法分别在归档和解档时调用。下面通过一个例子进行演示(注意对于自定义类的多对象归档与系统类多对象归档完全一样,代码中不再演示):

Person.h

 1 #import <Foundation/Foundation.h> 2  3 @interface Person : NSObject<NSCoding> 4  5 @property (nonatomic,copy) NSString *name; 6 @property (nonatomic,assign) int age; 7 @property (nonatomic,assign) float height; 8 @property (nonatomic,assign) NSDate *birthday; 9 10 @end

Person.m

 1 #import "Person.h" 2  3 @implementation Person 4  5 #pragma mark 解码 6 -(id)initWithCoder:(NSCoder *)aDecoder{ 7     NSLog(@"decode..."); 8     if (self=[super init]) { 9         self.name=[aDecoder decodeObjectForKey:@"name"];10         self.age=[aDecoder decodeInt64ForKey:@"age"];11         self.height=[aDecoder decodeFloatForKey:@"heiht"];12         self.birthday=[aDecoder decodeObjectForKey:@"birthday"];13     }14     return self;15 }16 17 #pragma mark 编码18 -(void)encodeWithCoder:(NSCoder *)aCoder{19     NSLog(@"encode...");20     [aCoder encodeObject:_name forKey:@"name"];21     [aCoder encodeInt64:_age forKey:@"age" ];22     [aCoder encodeFloat:_height forKey:@"height"];23     [aCoder encodeObject:_birthday forKey:@"birthday"];24 25 }26 27 #pragma mark 重写描述28 -(NSString *)description{29     NSDateFormatter *formater1=[[NSDateFormatter alloc]init];30     formater1.dateFormat=@"yyyy-MM-dd";31     return [NSString stringWithFormat:@"name=%@,age=%i,height=%.2f,birthday=%@",_name,_age,_height,[formater1 stringFromDate:_birthday]];32 }33 34 @end

main.m

 1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3  4  5 int main(int argc,char *argv[]){ 6  7     //归档 8     Person *person1=[[Person alloc]init]; 9     person1.name=@"Kenshin";10     person1.age=28;11     person1.height=1.72;12     NSDateFormatter *formater1=[[NSDateFormatter alloc]init];13     formater1.dateFormat=@"yyyy-MM-dd";14     person1.birthday=[formater1 dateFromString:@"1986-08-08"];15     16     NSString *path1=@"/Users/kenshincui/Desktop/person1.arc";17     18     [NSKeyedArchiver archiveRootObject:person1 toFile:path1];19 20     //解档21     Person *person2= [NSKeyedUnarchiver unarchiveObjectWithFile:path1];22     NSLog(@"%@",person2);23     /*结果:24      name=Kenshin,age=28,height=0.00,birthday=1986-08-0825      */26     27     return 0;28 }

 

oc_转_Foundation 框架